【问题标题】:Convert this SQL query to Linq (Not Exists + sub query)将此 SQL 查询转换为 Linq(不存在 + 子查询)
【发布时间】:2010-01-22 01:29:21
【问题描述】:

我希望将此 SQL 转换为 LINQ。 (它应该从输入中选择基于3列的表生成中不存在的行。如果两个表中的列都包含NULL,则应该认为它具有相同的值)

SELECT i.* FROM INPUT AS i
WHERE NOT EXISTS
(SELECT p.Agent FROM Production AS p
WHERE ISNULL(i.CustID,'') <> ISNULL(p.CustID,'')
AND ISNULL(i.CustName,'') <> ISNULL(p.CustName,'')
AND ISNULL(i.household,'') <> ISNULL(p.Household,''))

【问题讨论】:

    标签: linq linq-to-sql


    【解决方案1】:

    首先 - 这不是一个好的 SQL 查询。每列都包含在一个不可分割的函数中,这意味着引擎将无法利用任何这些列上的任何索引(假设您有任何索引)。

    让我们首先将其重写为一个半体面的 SQL 查询:

    SELECT i.*
    FROM Input i
    LEFT JOIN Production p
        ON (p.CustID = i.CustID OR (p.CustID IS NULL AND i.CustID IS NULL))
        AND (p.CustName = i.CustName OR (p.CustName IS NULL AND i.CustName IS NULL))
        AND (p.Household = i.Household OR
            (p.Household IS NULL AND i.Household IS NULL))
    WHERE p.CustID IS NULL
    

    话虽如此,LEFT JOIN / IS NULL 对于效率也不是很好,但我们在这里没有太多选择,因为我们要在多个列上进行比较。根据您的列名,我开始怀疑架构是否已正确规范化。 CustID 最有可能与一个且只有一个 CustName 相关联 - 您必须比较这两者的事实似乎有点奇怪。还有Household - 我不确定那是什么,但如果它是varchar(x)/nvarchar(x) 列,那么我想知道它是否也可能与客户有 1:1 的关系。

    如果我在这里猜测太多,请随意忽略这一段;但为了以防万一,我想说如果这些数据没有正确规范化,规范化它会使查询变得更容易和更快:

    SELECT *
    FROM Input
    WHERE CustID NOT IN (SELECT CustID FROM Production)
    

    无论如何,回到第一个查询,因为这是我们现在必须处理的。不幸的是,在 Linq 中无法根据这些特定条件创建连接,因此我们需要将 SQL 查询重写为稍差一些(因为我们现在必须从 Input 读取两次):

    SELECT *
    FROM Input
    WHERE <Primary Key> NOT IN
    (
        SELECT i.<Primary Key>
        FROM Input i
        INNER JOIN Production p
        ON (p.CustID = i.CustID OR (p.CustID IS NULL AND i.CustID IS NULL))
        AND (p.CustName = i.CustName OR (p.CustName IS NULL AND i.CustName IS NULL))
        AND (p.Household = i.Household OR
            (p.Household IS NULL AND i.Household IS NULL))
    )
    

    现在我们终于可以将一些东西翻译成 Linq 语法了。我们仍然不能显式地进行连接,这将是最好的,但我们还是老派,从笛卡尔连接开始,将连接条件扔到 WHERE 段中,服务器仍然可以对其进行排序:

    var excluded =
        from i in input
        from p in production
        where
            ((p.CustID == i.CustID) || ((p.CustID == null) && (i.CustID == null))) &&
            ((p.CustName == i.CustName) || 
                ((p.CustName == null) && (i.CustName == null))) &&
            ((p.Household == i.Household) ||
                ((p.Household == null) && (i.Household == null)));
        select i.PrimaryKey;
    
    var results =
        from i in input
        where !excluded.Contains(i.PrimaryKey)
        select i;
    

    我在这里假设您在桌子上有某种主键。如果不这样做,您还有其他问题,但您可以使用 EXCEPT 解决此特定问题:

    var excluded =
        from i in input
        from p in production
        where
            ((p.CustID == i.CustID) || ((p.CustID == null) && (i.CustID == null))) &&
            ((p.CustName == i.CustName) || 
                ((p.CustName == null) && (i.CustName == null))) &&
            ((p.Household == i.Household) ||
                ((p.Household == null) && (i.Household == null)));
        select i;
    
    var results = input.Except(excluded);
    

    【讨论】:

    • SQL 连接在这里不起作用,因为它将返回第二个表中 3 列匹配的每一行。我只需要一排。列名仅用于说明。没有主键且 ID 列不相关。每个表的 ID 都是唯一的,因此不能使用。您的 LINQ 给了我一些指示。谢谢。
    • @Tony_Henrich:如果右侧表中有多个匹配行,那么您是对的,第一个查询将返回重复行。但是,我写的后续查询不会有这个问题,Linq 查询会做你想要的(如果没有 PK,包括最后一个......但我真的建议不要没有 PK,这个例子很好说明为什么它们很重要)。
    • 长话短说,这些表格实际上是 Excel 电子表格。我已将数据复制到 SQL Server 以验证操作。主键无济于事,因为没有候选人。每个表中的唯一 ID 列对于该表来说是唯一的。表之间的值不匹配。
    • 呸。试图将 Excel 用作数据库是自找麻烦。我只需添加几个 VLOOKUP 函数并完成它。
    • 我没有使用 Excel 作为数据库引擎。数据在 Excel 电子表格中。数据被导入到通用列表中,其中 LINQ 用于查询和更新数据,然后写回 Excel。该过程避免使用数据库引擎。我将数据传输到 SQL Server 是因为我比 LINQ 更了解 SQL,特别是在涉及复杂连接时。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-09
    • 2014-08-14
    相关资源
    最近更新 更多