0

我必须只留下DataTable数据库中当前不存在日期的记录。

所以我使用存储过程读取了所有现有日期(正确吗?):

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可以提供帮助,但我不确定如何..

4

4 回答 4

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(只需将本例方法中的“日期”替换为“代码”即可)

希望其中之一能回答您的问题。否则我认为你将不得不更清楚你的要求。

于 2010-02-28T17:49:50.363 回答
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())));

于 2010-02-18T18:36:24.073 回答
1

您的 SQL 语句看起来不错。据我了解,您正在投射以获取从午夜开始的默认时间值。因此,要比较的另一个表中的日期也必须与该格式匹配,以便将日期与中性时间进行比较。如果不是,您仍然可以使用我下面的代码,但您必须在引用.DatetableResult行字段的任何位置添加该属性。我也使用过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>()or tableResult.Rows.OfType<DataRow>()。所有这些方法的结果都是相同的。

如果要从现有表中删除重复项(而不是将其复制到新表中),可以从表中删除Intersect 方法返回的项目:

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);
    }
}
于 2010-02-28T18:32:07.527 回答
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 最低的那个)。这显然有些武断,但为了更精细,我们需要更多地了解数据结构和要求。

于 2010-03-01T19:33:20.983 回答