2014-07-23 2 views
0

감사합니다.C# 전체 외부 LINQ가없는 두 개의 DataTable에 가입하십시오.

여기 내 목표 : Excel 워크 시트에서 읽은 두 개의 Datatables이 있습니다. Datatables는 동일한 스키마 (Datatable1의 열 A, B, C, ...는 Datatable2의 열 A, B, C ...와 같은 종류의 데이터를가집니다).

테이블의 데이터를 임의의 열로 비교해야합니다 (예 : 비교를 위해서만 열 A와 C가 중요하지만 열 A, B, C, ..., N의 데이터를 유지해야 함).

Excel 워크 시트에서이 내용을 읽었으므로 스키마를 기대할 수 없습니다. 예를 들어, 다른 워크 시트 세트를로드하면 비교 열이 다를 수 있습니다. 이러한 이유로 하드 코딩 된 SQL 문과 같은 LINQ를 사용할 수 없습니다.

전체 외부 참여와 동일한 작업을 수행해야합니다. 다른 데이터 테이블에 나타나지 않는 데이터 테이블 중 누락 된 데이터를 포함하여 모든 데이터를 표시하려고합니다.

DataRelations에 대해 조금 읽었지만 사용 방법을 잘 모르겠습니다.

예제 코드를 입력하십시오.

미리 감사드립니다.

+0

정말 완전 결합입니까, 병합입니까? – Corey

+0

*는 하드 코딩 된 SQL 문과 같습니다. * <--- 그리고 SQL 문을 포함하는 문자열 리터럴은 무엇입니까? –

+0

@Corey : 전체 외부 조인이 필요합니다 (두 워크 시트의 누락 된 데이터에 대해 null이 표시됨). – JessStuart

답변

0

임의의 수의 열을 가진 DataTable의 쌍이 있고 DataTable 두 개 각각에서 적절한 유형의 그룹화 값을 생성 할 수있는 함수가 있으면 Linq를 사용하여 대부분의 작업을 수행 할 수 있습니다. 당신.

DataTable에서 조인 키를 추출하는 기능부터 시작해 보겠습니다. object[]을 반환하는 것이 좋겠지 만 비교가 잘되지 않습니다. 그러나 우리는 Tuple<object, object>으로 처리 할 수 ​​있습니다. 더 많은 열이 필요하면 열을 더 추가하면됩니다. P

// Produces a JoinKey as Tuple containing columns 'A' and 'C' (0 and 2) 
public Tuple<object, object> GetJoinKey(DataRow row) 
{ 
    return Tuple.Create(row[0], row[2]); 
} 

이제 조인을하십시오. 우리는 완전 외부에 직접 가입 할 수는 없지만, 우리는 외부 두 가지와 Union 결과에 가입 할 수 있습니다까지 당신이 적당한 출력 형식을 만들 필요가

// given DataTables table1 & table2: 
var outerjoin = 
    (
     from row1 in table1.AsEnumerable() 
     join row2 in table2.AsEnumerable() 
      on GetJoinKey(row1) equals GetJoinKey(row2) 
      into matches 
     from row2 in matches.DefaultIfEmpty() 
     select new { key = GetJoinKey(row1), row1, row2 } 
    ).Union(
     from row2 in table2.AsEnumerable() 
     join row1 in table1.AsEnumerable() 
      on GetJoinKey(row2) equals GetJoinKey(row1) 
      into matches 
     from row1 in matches.DefaultIfEmpty() 
     select new { key = GetJoinKey(row2), row1, row2 } 
    ); 

다음 - 모든 행이있는 DataTable을 두 소스, 플러스 필드에서 키에 대한 몇 가지 정보 보유 : C의

var row1def = new object[table1.Columns.Count]; 
var row2def = new object[table2.Columns.Count]; 
foreach (var src in outerjoin) 
{ 
    // extract values from each row where present 
    var data1 = (src.row1 == null ? row1def : src.row1.ItemArray); 
    var data2 = (src.row2 == null ? row2def : src.row2.ItemArray); 

    // create row with key string and row values 
    result.Rows.Add(new object[] { src.key.ToString() }.Concat(data1).Concat(data2).ToArray()); 
} 

:

DataTable result = new DataTable(); 
// add column for string value of key: 
result.Columns.Add("__key", typeof(string)); 
// add columns from table1: 
foreach (var col in table1.Columns.OfType<DataColumn>()) 
    result.Columns.Add("T1_" + col.ColumnName, col.DataType); 
// add columns from table2: 
foreach (var col in table2.Columns.OfType<DataColumn>()) 
    result.Columns.Add("T2_" + col.ColumnName, col.DataType); 

마지막을 쿼리에서 테이블을 채우기 우리는 우리에게 99 %의 작업을 수행하는 단일 Linq 쿼리를 얻기 위해 몇 가지 작업을 단락시킬 수 있습니다. 나는 재미있게 들릴지 모르겠지만, 너와 함께 놀아 두겠다.

여기에 합리적으로 일반적인 제작, 조인 키 생성기에 대한 일반적인 기능 확장으로 수행 전체 방법입니다 : 이제

public static DataTable FullOuterJoin<T>(this DataTable table1, DataTable table2, Func<DataRow, T> keygen) 
{ 
    // perform inital outer join operation 
    var outerjoin = 
     (
      from row1 in table1.AsEnumerable() 
      join row2 in table2.AsEnumerable() 
       on keygen(row1) equals keygen(row2) 
       into matches 
      from row2 in matches.DefaultIfEmpty() 
      select new { key = keygen(row1), row1, row2 } 
     ).Union(
      from row2 in table2.AsEnumerable() 
      join row1 in table1.AsEnumerable() 
       on keygen(row2) equals keygen(row1) 
       into matches 
      from row1 in matches.DefaultIfEmpty() 
      select new { key = keygen(row2), row1, row2 } 
     ); 

    // Create result table 
    DataTable result = new DataTable(); 
    result.Columns.Add("__key", typeof(string)); 
    foreach (var col in table1.Columns.OfType<DataColumn>()) 
     result.Columns.Add("T1_" + col.ColumnName, col.DataType); 
    foreach (var col in table2.Columns.OfType<DataColumn>()) 
     result.Columns.Add("T2_" + col.ColumnName, col.DataType); 

    // fill table from join query 
    var row1def = new object[table1.Columns.Count]; 
    var row2def = new object[table2.Columns.Count]; 
    foreach (var src in outerjoin) 
    { 
     // extract values from each row where present 
     var data1 = (src.row1 == null ? row1def : src.row1.ItemArray); 
     var data2 = (src.row2 == null ? row2def : src.row2.ItemArray); 

     // create row with key string and row values 
     result.Rows.Add(new object[] { src.key.ToString() }.Concat(data1).Concat(data2).ToArray()); 
    } 

    return result; 
} 

, 테이블이 같은 스키마를 (이 경우 어떤 위 아무튼 ' T는 신경 쓰지 않는다면 거의 똑같은 일을 할 수있다. 결과 테이블 생성을 수정하여 테이블 중 하나를 복제 한 다음로드 루프에 병합 로직을 추가한다.

Here's a Gist 위의 내용은 테스트와 검증을 통해 확인되었습니다. 컴파일러에서 그 것을 버리고 나간다.

+0

이것은 굉장합니다! 나는 투표하기에 충분한 명성이 있었으면 좋겠다. – JessStuart

+0

정적 클래스 여야합니까? 이 경우 정적 클래스를 사용하면 어떤 이점이 있습니까? – JessStuart

+0

인스턴스 데이터를 사용하지 않고 정적 클래스에 넣을 때'DataTable'에 대한 확장 메서드로 작동 할 수 있기 때문에이 메서드를 정적 메서드로 만들었으므로'table1.FullOuterJoin (FullOuterJoin (table1, table2, keygen); 대신 – Corey