2022. 3. 3. 19:56ㆍC#
위와 같은 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라는 기능은 참 강력한 기능인 것 같다.
'C#' 카테고리의 다른 글
ListBox 행 선택해서 Delete 키로 행 삭제하기 (C# Winform) (0) | 2022.03.03 |
---|---|
.Net Core(닷넷 코어) 패키지 버전 가져오기 (0) | 2022.03.03 |
MD5를 통한 Checksum 자동화 [C#] (0) | 2022.02.05 |
C# XML다루기2) XML 특정 조건일 때 특정 값 수정 하기 (Modify) (0) | 2021.02.24 |
C# XML다루기1) XML Element 추가 작성하기 (Write) (0) | 2021.02.24 |