将此SQL查询转换为Linq(不存在+子查询)

时间:2010-01-22 01:29:21

标签: linq linq-to-sql

我希望将此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,''))

1 个答案:

答案 0 :(得分:4)

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

让我们首先将其重写为一个半体面的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的关系。< / p>

如果我在这里推测太多,那么随意解雇这一段;但为了以防万一,我想说如果这些数据没有正确规范化,将其标准化将使查询更容易,更快:

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);