C# 确定通用列表之间的差异

C# 确定通用列表之间的差异,c#,mysql,performance,linq,collections,C#,Mysql,Performance,Linq,Collections,这个问题可能有10个重复,但我想知道是否有比我现在做的更好的方法。这是一个小例子,我用它来说明我是如何确定差异的: //let t1 be a representation of the ID's in the database. List<int> t1 = new List<int>() { 5, 6, 7, 8 }; //let t2 be the list of ID's that are in memory.

这个问题可能有10个重复,但我想知道是否有比我现在做的更好的方法。这是一个小例子,我用它来说明我是如何确定差异的:

        //let t1 be a representation of the ID's in the database.
        List<int> t1 = new List<int>() { 5, 6, 7, 8 };
        //let t2 be the list of ID's that are in memory.
        //these changes need to be reflected to the database.
        List<int> t2 = new List<int>() { 6, 8, 9, 10 };

        var hash = new HashSet<int>(t1);
        var hash2 = new HashSet<int>(t2);
        //determines which ID's need to be removed from the database
        hash.ExceptWith(t2); 
        //determines which ID's need to be added to the database.
        hash2.ExceptWith(t1);

        //remove contents of hash from database
        //add contents of hash2 to database
编辑3


不过,我忘记了这个语句实际上存在执行延迟的问题,尽管有人想知道,我通过使用一个自定义比较器和一个添加的变量来确定它来自哪个列表,从而解决了以前的问题。

即使用Linq版本替换它,您仍然需要两个操作

让我们假设您使用的是纯SQL

您可能需要两个查询:

一个用于删除记录 再加一个
使用LINQ代码将比您的解决方案复杂得多,可读性也低得多

最终,您将使用一个完整的外部联接,在LINQ世界中,它是两个GroupJoin。但是,我们只关心在两个表中都没有匹配记录的值。Null right value left outer join表示删除,Null left value right outer join表示添加。因此,为了让它以这种方式工作,我们只需执行两个左外部联接,切换第二种情况的输入以模拟右外部联接,将它们连接在一起可以使用union,但不必要,因为我们将消除任何重复项

List<int> t1 = new List<int>() { 5, 6, 7, 8 };
List<int> t2 = new List<int>() { 6, 8, 9, 10 };

var operations = 
    t1.GroupJoin(
        t2, 
        t1i => t1i, 
        t2i => t2i, 
        (t1i, t2join) => new { Id = t1i, Action = !t2join.Any() ? "Remove" : null })
    .Concat(
        t2.GroupJoin(
            t1, 
            t2i => t2i, 
            t1i => t1i, 
            (t2i, t1join) => new { Id = t2i, Action = !t1join.Any() ? "Insert" : null })
    .Where(tr => tr.Action != null)
我确信它仍然会比使用哈希集慢,但无论如何还是要尝试一下

Edit:是的,它的速度更快,因为在foreach中枚举集合或将其放入具体的数据类型(例如:列表、数组等)之前,它实际上不会对集合执行任何操作。仍然需要额外的时间来确定要添加/删除哪些,这就是最终的问题所在。通过分解这两个查询,我可以获得相当的速度,但通过ToList将其放入内存世界使其比hashset版本慢:

t1.Except(t2); // .ToList() slows these down
t2.Except(t1); 
老实说,我会在SQL方面处理它。在存储过程中,将所有值存储在一个表变量中,另一列根据值是否已存在于表中指示添加或删除。然后,您可以通过连接回这个表变量来执行批量删除/插入

编辑:我想扩展一下我的意思,将完整列表发送到数据库并在存储过程中进行处理:

var toModify = t1.Union(t2).Except(t1.Intersect(t2));
mods = string.Join(",", toModify.ToArray());
// Pass mods (comma separated list) to your sproc.
然后,在存储过程中,您将执行以下操作:

-- @delimitedIDs some unbounded text type, in case you have a LOT of records
-- I use XQuery to build the table (found it's faster than some other methods)
DECLARE @idTable TABLE (ID int, AddRecord bit)
DECLARE @xmlString XML
SET @xmlString = CAST('<NODES><NODE>' + REPLACE(@delimitedIDs, ',', '</NODE><NODE>') + '</NODE></NODES>' as XML)

INSERT INTO @idTable (ID)
SELECT node.value('.','int') 
FROM @xmlString.nodes('//NODE') as xs(node)

UPDATE id
SET AddRecord = CASE WHEN someTable.ID IS NULL THEN 1 ELSE 0 END
FROM @idTable id LEFT OUTER JOIN [SomeTable] someTable on someTable.ID = id.ID

DELETE a
FROM [SomeTable] a JOIN @idTable b ON b.ID = a.ID AND b.AddRecord = 0

INSERT INTO [SomeTable] (ID)
SELECT id FROM @idTable WHERE AddRecord = 1

诚然,这只是插入了一些ID,实际上并没有添加任何其他信息。但是,您仍然可以将XML数据传递到存储过程,并以类似的方式使用XQuery来获取需要添加的信息。

在我的系统上,我的版本实际上比这快4倍。我的大约是1911毫秒,你的大约是7080毫秒。我提到了性能问题。你想要一个LINQ解决方案,我给了你一个。我的最后一句话解释了我的想法。依她的说法,这更像是一个LINQ2SQL解决方案,用于生成查询,而不是处理已经持久化的对象。老实说,我认为你的方法可能会是最快的,尤其是使用哈希集。请看我在问题中的更新。您的上一条语句非常快。我忘记了这是延迟执行的。我知道我需要两个SQL查询或一个存储过程。问题是我是否可以在一个操作中确定列表中的差异。如果对于一个操作,你指的是一行代码,是的,你可以这样做,但你可能会将两个操作串联起来。拿出你的解决方案并将其标记为已接受,你可能会帮助有同样问题的人。
var toModify = t1.Union(t2).Except(t1.Intersect(t2));
mods = string.Join(",", toModify.ToArray());
// Pass mods (comma separated list) to your sproc.
-- @delimitedIDs some unbounded text type, in case you have a LOT of records
-- I use XQuery to build the table (found it's faster than some other methods)
DECLARE @idTable TABLE (ID int, AddRecord bit)
DECLARE @xmlString XML
SET @xmlString = CAST('<NODES><NODE>' + REPLACE(@delimitedIDs, ',', '</NODE><NODE>') + '</NODE></NODES>' as XML)

INSERT INTO @idTable (ID)
SELECT node.value('.','int') 
FROM @xmlString.nodes('//NODE') as xs(node)

UPDATE id
SET AddRecord = CASE WHEN someTable.ID IS NULL THEN 1 ELSE 0 END
FROM @idTable id LEFT OUTER JOIN [SomeTable] someTable on someTable.ID = id.ID

DELETE a
FROM [SomeTable] a JOIN @idTable b ON b.ID = a.ID AND b.AddRecord = 0

INSERT INTO [SomeTable] (ID)
SELECT id FROM @idTable WHERE AddRecord = 1