2010-02-13 25 views
0

我必須在DataTable中只留下數據庫中當前不存在日期的記錄。使用LINQ從另一個表中使用另一個表中的字段刪除重複項

SELECT DISTINCT CAST(S.[date] AS DATE) -- original date is DATETIME2(0) 
FROM ... 
WHERE ... 

,並加載到一個DataTable

var tableDate = new DataTable(); 
new SqlDataAdapter(command).Fill(tableDate); 

如何從另一張表,現在刪除所有

所以我使用存儲過程(?是正確的)讀取所有現有的日期不必要的行?我認爲LINQ可以幫助,但我不知道如何..

+0

我不確定我是否理解這些要求。您的意思是您需要查找數據庫中給定表中不存在的所有日期值,或者您是否需要從表B中不存在日期值的數據庫中的表A中刪除行? – Thomas 2010-02-28 16:36:03

+0

@Thomas:讓我來描述一下情況。我需要使用SqlBulCopy將新數據添加到數據庫。但首先我需要清理它 - 我沒有添加數據庫中已經存在的數據。標準 - 日期。例如,首先我添加了Jan,1,2,3的數據。之後 - 2,3,4。我需要從第二組中刪除Jan,2。 – abatishchev 2010-03-01 06:47:21

回答

2

我在看你的回答,你說的作品,你只是想知道如何做一個「單LINQ查詢。」請記住,這些查詢都紛紛推遲執行,所以下面兩個查詢是功能上等同:

var q = 
    from d in dates 
    select d.Field<DateTime>("date"); 
return 
    (from r in records 
    where !q.Contains(r.Field<DateTime>("date")) 
    select r).CopyToDataTable(); 

和:

return 
    (from r in records 
    where !dates 
     .Select(d => d.Field<DateTime>("date")) 
     .Contains(r.Field<DateTime>("date")) 
    select r).CopyToDataTable(); 

第二個版本是很多難讀,但儘管如此,它是「一個查詢」。


話雖如此,沒有這些例子似乎真的符合你的問題的標題,這表明您試圖刪除重複的行。如果這確實是你正在嘗試做的,這裏是將做到這一點的方法:

static DataTable RemoveDuplicates(DataTable dt) 
{ 
    return 
     (from row in dt.Rows.OfType<DataRow>() 
     group row by row.Field<string>("date") into g 
     select g 
      .OrderBy(r => r.Field<int>("ID")) 
      .First()).CopyToDataTable(); 
} 

如果你不關心重複刪除,那麼你可以只取出OrderBy線。您可以測試這個如下:

static void Main(string[] args) 
{ 
    using (DataTable original = CreateSampleTable()) 
    using (DataTable filtered = RemoveDuplicates(original)) 
    { 
     DumpTable(filtered); 
    } 
    Console.ReadKey(); 
} 

static DataTable CreateSampleTable() 
{ 
    DataTable dt = new DataTable(); 
    dt.Columns.Add("ID", typeof(int)); 
    dt.Columns.Add("Code", typeof(string)); 
    dt.Columns.Add("Name", typeof(string)); 
    dt.Rows.Add(1, "123", "Alice"); 
    dt.Rows.Add(2, "456", "Bob"); 
    dt.Rows.Add(3, "456", "Chris"); 
    dt.Rows.Add(4, "789", "Dave"); 
    dt.Rows.Add(5, "123", "Elen"); 
    dt.Rows.Add(6, "123", "Frank"); 
    return dt; 
} 

static void DumpTable(DataTable dt) 
{ 
    foreach (DataRow row in dt.Rows) 
    { 
     Console.WriteLine("{0},{1},{2}", 
      row.Field<int>("ID"), 
      row.Field<string>("Code"), 
      row.Field<string>("Name")); 
    } 
} 

(只是用「代碼」代替「日期」,在RemoveDuplicates方法對於本例)

希望這些回答你的問題之一。否則,我認爲你需要更清楚你的要求。

1

你可以使用Except()

return records.Except(dates);

更新: 如果您DataTable已鍵入的字段,那麼它應該像下面:

var excluded = arbDates.Rows.OfType<System.Data.DataRow>().Select(a => a[0]) .Except(excDates.Rows.OfType<System.Data.DataRow>().Select(e => e[0]));

否則你會投它:

var excluded = arbDates.Rows.OfType<System.Data.DataRow>() .Select(a => Convert.ToDateTime(a[0].ToString())) .Except( excDates.Rows.OfType<System.Data.DataRow>() .Select(e => Convert.ToDateTime(e[0].ToString())));

+0

嗨。你能幫我用q1.Except(q2)寫一個LINQ查詢嗎?如何將選擇結合在一起? – abatishchev 2010-02-18 21:58:03

+0

不幸的是,它沒有奏效。 table.AsEnumerable()。除了(Database.CreateDataTable(command).AsEnumerable())。ToArray()每次都返回與表最初相同的記錄數。我會嘗試使用自定義比較器並將報告。 – abatishchev 2010-02-19 11:19:47

+0

我現有的自定義比較器也沒有幫助。我在下面的答案中發佈了它。有任何想法嗎? – abatishchev 2010-02-19 11:26:39

1

你的SQL語句看起來很好。據我所知,你正在施放從午夜開始獲得默認時間值。因此,所比較的其他表格中的日期也必須與該格式相匹配,以便將日期與中性時間進行比較。如果不是,您仍然可以使用我在下面的代碼,但是您必須在引用tableResult行的字段的任何位置添加.Date屬性。此外,我還使用了Field<DateTime>(0),但根據您的查詢並基於您之前的示例,您可能需要使用Field<DateTime>("date")

不需要自定義比較器。要將您的LINQ查詢合併到一個查詢中,您可以簡單地使用let關鍵字並通過查詢攜帶中間結果並引用它。

試試這個:

var tableDate = new DataTable(); 
new SqlDataAdapter(command).Fill(tableDate); 

// this is the other table that has other dates, so populate as needed 
var tableResult = new DataTable(); 

var newTable = 
    (from row in tableResult.AsEnumerable() 
    let uniqueRows = tableResult.AsEnumerable().Select(r => r.Field<DateTime>(0)) 
           .Except(tableDate.AsEnumerable().Select(r => r.Field<DateTime>(0))) 
    where uniqueRows.Contains(row.Field<DateTime>(0)) 
    select row).CopyToDataTable(); 

在點號的查詢是:

var newTable = tableResult.AsEnumerable() 
    .Select(row => new 
    { 
     Row = row, 
     UniqueRows = tableResult.AsEnumerable() 
           .Select(r => r.Field<DateTime>(0)) 
           .Except(tableDate.AsEnumerable().Select(r => r.Field<DateTime>(0))) 
    }) 
    .Where(item => item.UniqueRows.Contains(item.Row.Field<DateTime>(0))) 
    .Select(item => item.Row) 
    .CopyToDataTable(); 

相反的tableResult.AsEnumerable()你可以使用tableResult.Rows.Cast<DataRow>()tableResult.Rows.OfType<DataRow>()。所有這些方法的結果都是一樣的。

如果你想從現有表刪除重複項(而不是將其複製到新表),你可以刪除該表由Intersect method返回的項目:

var commonDates = tableDate.AsEnumerable().Select(row => row.Field<DateTime>(0)) 
          .Intersect(tableResult.AsEnumerable().Select(row => row.Field<DateTime>(0))); 

for (int index = tableResult.Rows.Count - 1; index >= 0; index--) 
{ 
    if (commonDates.Contains(tableResult.Rows[index].Field<DateTime>(0))) 
    { 
     tableResult.Rows.RemoveAt(index); 
    } 
} 
1

據我瞭解的問題,您正試圖取消某些導入數據。您可能不需要使用LINQ來做到這一點。雖然帖子標題暗示了LINQ,但後來您會質疑LINQ是否是最好的解決方案,並且考慮到我們所知,我認爲您可以使用單個Insert語句來完成此操作。

首先,我建議大量數據複製到一個數據庫中的臨時位置(如果你是不是已經這樣做了),像這樣:

Create Table TempBulkCopyData 
(
    Id int not null identity(1,1) 
    , Date DateTime2 not null 
    , ... 
) 

一個批量複製的優勢轉化爲一個臨時的位置在於您可以添加索引等來加速清潔過程。要消除重複數據,然後你可以運行一個查詢,像這樣:

Insert DestinationData(...) 
Select ... 
From BulkCopyData As BCD 
Where Id = (
      Select Min(BCD2.[Id]) 
      From BulkCopyData As BCD2 
      Where Cast(BCD2.[Date] As Date) = Cast(BCD.[Date] As Date) 
      ) 

或者

Insert DestinationData(...) 
Select ... 
From BulkCopyData As BCD 
Where Id = (
      Select Min(BCD2.[Id]) 
      From BulkCopyData As BCD2 
      Where DateDiff(d, BCD.[Date], BCD2.[Date]) = 0 
      ) 

這將拉動它找到的第一個日期(一個具有最低ID)。這顯然有些武斷,但爲了更精確,我們需要更多地瞭解數據結構和需求。

相關問題