Linq GroupBy 를 통해 평균 값구하기 (C# Winform / VB .net)

2022. 3. 3. 19:56C#

728x90
반응형

 

 



위와 같은 Table이 존재 하고 수량의 평균 값어치만 구하고 뿌려주고 싶을 때

여러가지 방법이 있다.

한가지는 DB에서 가져올 때 아예 쿼리로 Group By로 묶어줄 수 있다.

SELECT `구분`, `대분류`, `중분류`, COUNT(중분류) AS 수량 FROM t.test

하지만 프로그램이 최초 load될 때 DataSet에 담아두고 각종 Filter작업을 해야할 때는

차라리 그냥 DataTable에서 Filter를 해주는 것이 더 편리하기때문에 Linq라는 작업을 통해서 할 수 있다.


맨 처음 저같은 경우에는 간단한 예제로 하기 위해 csv파일을 불러와 작업을 했는데요.

수량을 계산하기 위해서는 수량 Column이 String Type으로 된 DataColumn이면 안되니까 변경을 해주었습니다.

별도로 Columns을 생성해준 것이 아니라면 아래의 사진과 같은 오류가 날거에요 ㅋ

이럴 땐 특정 Column의 Type을 변경해줄 겁니다.


C# - DataTable Column Type 바꾸기

if (dt != null && dt.Rows.Count != 0)
{
    dt = ChangeColumnType(typeof(Int32));
    DataGridView1.DataSource = dt;
}
private DataTable ChangeColumnType(Type type)
{
    // 임시 Table을 만든다. 
    DataTable dtCloned = dt.Clone();

    // 원하는 Type으로 변경한다.
    dtCloned.Columns[3].DataType = type;

    
    foreach (DataRow dr in dt.Rows)
    {
        // 임시 Table에 원본 DataRow의 row를 가져온다.
        dtCloned.ImportRow(dr);
    }

    return dtCloned;
}


VB.NET - DataTable Column Type 바꾸기

If Not (dt Is Nothing) And Not (dt.Rows.Count = 0) Then
    '// Column을 바꿉니다.
    dt = ChangeColumnType(GetType(Int32))

    DataGridView1.DataSource = dt
End If
Private Function ChangeColumnType(ByRef type As Type)

    '// 임시 Table을 만든다.
    Dim dtCloned As DataTable = dt.Clone

    dtCloned.Columns(3).DataType = type

    For Each dr As DataRow In dt.Rows
        dtCloned.ImportRow(dr)
    Next

    Return dtCloned

End Function




C# - DataTable Linq로 GroupBy 해서 Average 구하기

private void TestLinq()
{
    var query = dt
        .AsEnumerable()
        .GroupBy(r => new
        {
            구분 = r.Field<string>("구분"),
            대분류 = r.Field<string>("대분류"),
            중분류 = r.Field<string>("중분류")
        }).Select(g => 
        {
            var row = dt.NewRow();
            row["구분"] = g.Key.구분;
            row["대분류"] = g.Key.대분류;
            row["중분류"] = g.Key.중분류;
            row["수량"] = g.Average(x => x.Field<Int32>("수량"));
            return row;
        }).CopyToDataTable();

    DataGridView1.DataSource = query;

}

방법1은 직접 datatable 형식으로 return 을 하여 바로 할당하는 방법

방법2

private void TestLinq2()
{
    var query = from r in dt.AsEnumerable()
                group r by new
                {
                    구분 = r.Field<string>("구분"),
                    대분류 = r.Field<string>("대분류"),
                    중분류 = r.Field<string>("중분류"),
                } into g
                select new
                {
                    구분 = g.Key.구분,
                    대분류 = g.Key.대분류,
                    중분류 = g.Key.중분류,
                    수량 = g.Average(x => x.Field<Int32>("수량"))
                };
    DataTable tmpDt = new DataTable();
    tmpDt = dt.Clone();
    foreach (var item in query)
    {
        tmpDt.Rows.Add(item.구분, item.대분류, item.중분류, item.수량);
    }

    DataGridView1.DataSource = tmpDt;
}


VB.NET - DataTable Linq로 GroupBy 해서 Average 구하기

        '// dt.AsEnumerable()
        '// Group할 Field를 지정 후 Group한다.
        '// 여러개를 그룹할 때는 New With {}으로 묶어서 할 수 있다.
        '// 한 개를 기준으로 그룹할 때는 Group r By Item = r.Field(Of String)("구분")
        '// 위와 같이 표현할 수 있다.
        '// let r = grp.First 를 하여 Data를 맨 위로 올린다.
        '// Key값으로 지정된 컬럼들을 선택 후, value필드를 만들어 계산한다.
        Dim query = From r In dt.AsEnumerable()
                    Group r By Item = New With {
                       Key .구분 = r.Field(Of String)("구분"),
                       Key .대분류 = r.Field(Of String)("대분류"),
                       Key .중분류 = r.Field(Of String)("중분류")
                        } Into grp = Group
                    Let r = grp.First
                    Select New With {
                        .구분 = r("구분"),
                        .대분류 = r("대분류"),
                        .중분류 = r("중분류"),
                        .Value = grp.Average(Function(x) x.Field(Of Int32)("수량"))
        }
        Dim tmpDt As DataTable = New DataTable
        tmpDt = dt.Clone
        For Each item In query
            tmpDt.Rows.Add(item.구분, item.대분류, item.중분류, item.Value)
        Next

        DataGridView1.DataSource = tmpDt

방법2

        '// Group By를 할 Field를 지정 후 Key값으로 하고 select한다.
        '// dt의 newrow메서드로 행을 추가하고 dt.row에 선택한 key값을 넣는다.
        '// 수량은 선택한 필드에서 계산을 한다. 
        Dim return_dt = dt.AsEnumerable.GroupBy(
            Function(l) New With {
               Key .구분 = l.Field(Of String)("구분"),
               Key .대분류 = l.Field(Of String)("대분류"),
               Key .중분류 = l.Field(Of String)("중분류")}).Select(
            Function(g)
                Dim row = dt.NewRow
                row("구분") = g.Key.구분
                row("대분류") = g.Key.대분류
                row("중분류") = g.Key.중분류
                row("수량") = g.Average(Function(r) r.Field(Of Int32)("수량"))
                Return row
            End Function
        ).CopyToDataTable

        DataGridView1.DataSource = return_dt

 

위의 코드를 실행하면 아래와 같이 된다.

Linq를 잘 활용하면 Average 및 Sum 등의 간단한 작업은 코드 한줄로 끝낼 수 있다는 것이다.

이런 점은 Linq라는 기능은 참 강력한 기능인 것 같다.

 

 

 

 

728x90
반응형