2010-03-04 3 views
22

두 개의 간단한 SQL 쿼리를 사용하여 dataTable1dataTable2을 채우는 코드는 다음과 같습니다. dataTableSqlJoined은 같은 테이블에서 채워지지만 함께 연결됩니다.LINQ와 연결된 두 개의 DataTable에서 결합 된 DataTable을 만듭니다. C#

마치 SQL을 사용하여 작성한 것처럼 dataTableLinqJoined을 만들 수있는 LINQ 쿼리를 작성하려고합니다. 아래 예제에서는 dataTable1의 값만 반환합니다.

내가 가지고있는 문제는 linq 쿼리의 SELECT에 무엇을 넣는 것입니다. 두 DataRow의 모든 열을 포함하는 새로운 DataRow를 어떻게 만들 수 있습니까? 런타임까지 정확한 열 이름/쿼리 스키마를 알 수 없습니다.

sqlCommand = new SqlCommand("SELECT ID, A, B FROM Table1", sqlConnection, sqlTransaction); 
sqlAdapter = new SqlDataAdapter(sqlCommand); 
DataTable dataTable1 = new DataTable(); 
sqlAdapter.Fill(dataTable1); 

sqlCommand = new SqlCommand("SELECT ID, C, D FROM Table2", sqlConnection, sqlTransaction); 
sqlAdapter = new SqlDataAdapter(sqlCommand); 
DataTable dataTable2 = new DataTable(); 
sqlAdapter.Fill(dataTable2); 

sqlCommand = new SqlCommand("SELECT Table1.ID, A, B, Table2.ID, C, D FROM Table1 INNER JOIN Table2 ON Table1.ID = Table2.ID", sqlConnection, sqlTransaction); 
sqlAdapter = new SqlDataAdapter(sqlCommand); 
DataTable dataTableSqlJoined = new DataTable(); 
sqlAdapter.Fill(dataTableSqlJoined); 

var dataRows = 
    from 
     dataRows1 in dataTable1.AsEnumerable() 
    join 
     dataRows2 in dataTable2.AsEnumerable() 
    on 
     dataRows1.Field<int>("ID") equals dataRows2.Field<int>("ID") 
    select 
     dataRows1; // + dataRows2; 

DataTable dataTableLinqJoined = dataRows.CopyToDataTable(); 

조금 더 배경을 생각하면 통합 된 쿼리는 매우 DB 집약적이며 성능 문제를 일으키는 것입니다. 첫 번째 쿼리에서 반환 된 데이터는 상당히 정적이며 캐시를 많이 차지할 수 있습니다. 두 번째 쿼리에서 반환하는 데이터는 계속 변경되지만 실행 속도가 빠르며 따라서 캐시 할 필요가 없습니다. 또한 결합 된 DataTable의 전달에 의존하는 많은 코드가 있으므로 다른 형식으로 데이터를 전달할 때 가능한 많은 옵션이 없습니다.

+0

한편으로는 이러한 쿼리의 성능을 절대적으로 알고있는 방법에 대해 약간 궁금해하지만, 다른 한편으로는 런타임까지 테이블 구조를 알지 못합니다. –

+0

쿼리가 동적으로 작성됩니다. –

답변

20
.

아직이 페이지를 보셨습니까? 그 방법은 당신이 객체 배열로 행 데이터를 깰 수있다, 당신을 위해 LINQy 충분하지 않으면

HOW TO: Implement a DataSet JOIN helper class in Visual C# .NET

:

DataTable targetTable = dataTable1.Clone(); 
var dt2Columns = dataTable2.Columns.OfType<DataColumn>().Select(dc => 
    new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping)); 
targetTable.Columns.AddRange(dt2Columns.ToArray()); 
var rowData = 
    from row1 in dataTable1.AsEnumerable() 
    join row2 in dataTable2.AsEnumerable() 
     on row1.Field<int>("ID") equals row2.Field<int>("ID") 
    select row1.ItemArray.Concat(row2.ItemArray).ToArray(); 
foreach (object[] values in rowData) 
    targetTable.Rows.Add(values); 

나는 당신이가는 것만큼이나 간결 생각 그것을 만들 수 있어야하고 이유를 설명 할 것입니다 : 그것은 스키마입니다.

DataRow은 독립적 인 개체가 아닙니다. 그것의 소유에 따라 다릅니다 DataTable 그리고 그것 없이는 살 수 없다. 지원되지 않는 방법이 있습니다 "연결이 끊긴"DataRow; CopyToDataTable() 확장 방법은 하나의 DataTable에 이미 존재하는 행에서 작동하며 행을 복사하기 전에 소스 (DataRow은 모두 Table에 대한 참조가 있음)에서 스키마를 복사합니다 (대부분 ImportRow을 사용하지만, 실제로 리플렉터를 열어 확인).

이 경우 작성해야하는 새 스키마가 있습니다.새 행을 만들려면 먼저 첫 번째 문자를으로 유지할 테이블을 만들어야합니다. 이는 위의 메서드 위쪽에 최소한 3 행의 코드를 작성한다는 의미입니다.

DataTable 및 관련 DataRowCollection은 한 번에 여러 행을 추가하는 메소드를 노출하지 않으므로 마지막으로 행을 만들 수 있습니다. 단 한 번에 하나씩 만 만들 수 있습니다. DataRowCollection이를 만들기 위해 당신은 물론, 자신의 확장 메서드를 추가 할 수 있습니다 "모양"좋네요 :

public static void AddRange(this DataRowCollection rc, 
    IEnumerable<object[]> tuples) 
{ 
    foreach (object[] data in tuples) 
     rc.Add(tuples); 
} 
다음

첫 번째 방법에 foreach 제거하고로 교체받을 수있다 :

targetTable.Rows.AddRange(rowData); 

실제로는 단지 자세한 정보를 제거했지만 제거하지는 않았습니다.

요약하면 기존의 DataSet 클래스 계층 구조로 작업하는 동안에는 항상 작은 균열이 있습니다. Linq to DataSet 확장 기능은 훌륭하지만 확장 기능 일 뿐이며 위의 제한 사항을 변경할 수는 없습니다.

+0

나는 그 페이지를 보았지만 LINQy가 아니라는 사실을 좋아하지 않았다. targetTable을 작성하는 예제는 훌륭합니다. 감사. –

+0

코드를 약간 수정하면 itemarray가 Union이 아닌 Concat이되어야합니다. 그렇지 않으면 동일한 값 (널)을 가진 모든 열을 제거합니다. 그것 이외의. 완벽하게 작동합니다! 고맙습니다! –

+0

targetTable.Columns.AddRange (dt2Columns.ToArray()); 같은 이름의 컬럼을 추가 할 때 오류가 발생합니다. – Cannon

0
select new { 
    ID = dataRows1.ID, // no need to select dataRows2.ID, because of JOIN. 
    A = dataRows1.A, 
    B = dataRows1.B, 
    C = dataRows2.C, 
    D = dataRows2.D 
}; 
+0

런타임까지 열 이름/스키마를 알 수 없습니다. 이 유형을 DataTable로 변환하는 "간단한"방법도 없습니다. –

+0

질문에서 그 점을 발견하지 못했습니다. 죄송합니다. 알 수없는 형식을 DataTable로 변환하려면 리플렉션을 사용하거나 (표현식 : http://www.infoq.com/articles/expression-compiler) 미친 짓을하십시오. –

1

나는 바보처럼 들린다.

제 생각에 최종 테이블을 준비해야합니다 (테이블 A & 테이블 B의 모든 필드 포함). 그 결과 &에 ForEach
그리고, 대신에 LINQ를 사용하는이 &에 참여하는 일은 최종 데이터 테이블에 값을 삽입합니다.

의사 코드 :

dt1.Join (DT2) 어디에요 (...)를 ForEach (행 => 코드는 finalTable.Rows에 추가 익명 객체 &의 내용을 읽을 수 있습니다)

+0

이런 식으로 생각했습니다. 예 : 조합 된 쿼리를 실행하는 대신 조인을 수행하는 대신 두 번째 쿼리의 열에 null을 넣습니다. 그런 다음 두 번째 쿼리를 실행하여 쿼리를 채 웁니다. +1 대답,하지만 난 여전히 더 "우아한"뭔가를 바라고 있어요. –

5

잘됐다. 그러나 LINQy 코드에 몇 가지 개선 사항을 추가하고 싶습니다. dataTable2에서 대상 테이블에 열을 추가하는 동안 대상 테이블에 이미 존재하는 열이 거의 없을 가능성이 있습니다. 그래서 여기에 우리가 간다.

DataTable targetTable = dataTable1.Clone(); 
var dt2Columns = dataTable2.Columns.OfType<DataColumn>().Select(dc => 
new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping)); 
var dt2FinalColumns=from dc in dt2Columns.AsEnumerable() 
        where targetTable.Columns.Contains(dc.ColumnName) == false 
        select dc; 
targetTable.Columns.AddRange(dt2FinalColumns.ToArray()); 
var rowData =from row1 in dataTable1.AsEnumerable() 
      join row2 in dataTable2.AsEnumerable() 
      on row1.Field<int>("ID") equals row2.Field<int>("ID") 
      select row1.ItemArray.Concat(row2.ItemArray.Where(r2=> row1.ItemArray.Contains(r2)==false)).ToArray(); 
foreach (object[] values in rowData) 
targetTable.Rows.Add(values); 

희망이 나와 같은 사람에게 도움이되기를 바랍니다.

+0

dtcomments 란 무엇입니까? – Cannon

+0

dt2Columns로 dtcomments를 변경하십시오. 한 가지 질문 : 견인 테이블 이상의 것이 있다면 테이블에 어떻게 가입합니까? VAR rowData를 – Cannon

+0

'= dataTable1.AsEnumerable에 ROW1()는 dataTable2.AsEnumerable (ROW2의 가입)에서 row1.Field의 는 ("ID")는이 row2.Field ("ID")가 .AsEnumerable row3에 가입 같음() on suryakiran

관련 문제