2010-02-13 51 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)。这显然有些武断,但为了更精确,我们需要更多地了解数据结构和需求。

相关问题