Sql 不存在vs不存在:效率

Sql 不存在vs不存在:效率,sql,sql-server,tsql,exists,sql-execution-plan,Sql,Sql Server,Tsql,Exists,Sql Execution Plan,我一直认为“不存在”是解决问题的方法,而不是使用“不存在”状态。然而,在对我一直使用的查询进行比较时,我注意到Not In条件的执行实际上似乎更快。任何关于为什么会出现这种情况的见解,或者如果到目前为止我只是做了一个可怕的假设,都将不胜感激 问题1: SELECT DISTINCT a.SFAccountID, a.SLXID, a.Name FROM [dbo].[Salesforce_Accounts] a WITH(NOLOCK) JOIN _SLX_AccountChannel b

我一直认为“不存在”是解决问题的方法,而不是使用“不存在”状态。然而,在对我一直使用的查询进行比较时,我注意到Not In条件的执行实际上似乎更快。任何关于为什么会出现这种情况的见解,或者如果到目前为止我只是做了一个可怕的假设,都将不胜感激

问题1:

SELECT DISTINCT 
a.SFAccountID, a.SLXID, a.Name FROM [dbo].[Salesforce_Accounts] a WITH(NOLOCK)
JOIN  _SLX_AccountChannel b WITH(NOLOCK)
ON a.SLXID = b.ACCOUNTID
JOIN [dbo].[Salesforce_Contacts] c WITH(NOLOCK)
ON a.SFAccountID = c.SFAccountID
WHERE b.STATUS IN ('Active','Customer', 'Current')
AND c.Primary__C = 0
AND NOT EXISTS
(
SELECT 1 FROM [dbo].[Salesforce_Contacts] c2 WITH(NOLOCK)
WHERE a.SFAccountID = c2.SFAccountID
AND c2.Primary__c = 1
);
问题2:

SELECT   
DISTINCT
a.SFAccountID FROM [dbo].[Salesforce_Accounts] a WITH(NOLOCK)
JOIN  _SLX_AccountChannel b WITH(NOLOCK)
ON a.SLXID = b.ACCOUNTID
JOIN [dbo].[Salesforce_Contacts] c WITH(NOLOCK) 
ON a.SFAccountID = c.SFAccountID
WHERE b.STATUS IN ('Active','Customer', 'Current')
AND c.Primary__C = 0
AND a.SFAccountID NOT IN (SELECT SFAccountID FROM [dbo].[Salesforce_Contacts] WHERE Primary__c = 1 AND SFAccountID IS NOT NULL);
查询1的实际执行计划:

查询2的实际执行计划:

时间/IO统计信息:

查询#1(使用不存在):

查询#2(使用Not In):

试一试


据我所知,a的工作方式与两个嵌套的for指令的工作方式不同

因此,假设您有两个表:表(1000条记录)和表(2000条记录)

就像做

for (int i = 0;  i < 1000; i++) {
   for (int j = 0;  j < 2000; j++) {
   }
}
for(int i=0;i<1000;i++){
对于(int j=0;j<2000;j++){
}
}
即1000*2000=200万次操作

与tabla.field的左连接是null技巧,就我所知,它只进行2000次操作


使用左连接

这是假设您正在尝试查找没有主要联系人的帐户,并且只能有一个主要联系人

SELECT  a.SFAccountID, a.SLXID, a.Name 
FROM    [dbo].[Salesforce_Accounts] a
        LEFT JOIN [dbo].[Salesforce_Contacts] c ON a.SFAccountID = c.SFAccountID AND c.Primary__C = 1
WHERE
        EXISTS (SELECT  * 
                FROM SLX_AccountChannel b 
                WHERE b.ACCOUNTID = a.SLXID 
                    AND b.STATUS IN ( 'Active', 'Customer', 'Current' ))
        AND c.SFContactID IS NULL
如果您想要有联系人但没有主要联系人的帐户,您可以使用

SELECT 
    a.SFAccountID ,
    a.SLXID ,
    a.Name
FROM 
    [dbo].[Salesforce_Accounts] a
WHERE
    a.SFAccountID IN (SELECT SFAccountID 
                    FROM [Salesforce_Contacts] 
                    GROUP BY SFAccountID 
                    HAVING SUM(CAST(Primary__c AS INT) = 0))

    AND a.SLXID IN (SELECT ACCOUNTID 
                    FROM _SLX_AccountChannel 
                    WHERE [STATUS] IN ( 'Active', 'Customer', 'Current' ))

我认为缺少索引会导致
EXISTS()
操作中的差异

虽然这个问题并不要求更好的查询,但对我来说,我会尽量避免像这样的区别

SELECT
    a.SFAccountID, a.SLXID, a.Name 
FROM 
    [dbo].[Salesforce_Accounts] a WITH(NOLOCK)
    CROSS APPLY 
    (
        SELECT SFAccountID 
        FROM [dbo].[Salesforce_Contacts] WITH(NOLOCK) 
        WHERE SFAccountID  = a.SFAccountID 
        GROUP BY SFAccountID
        HAVING MAX(Primary__C + 0) = 0 -- Assume Primary__C is a bit value
    ) b
WHERE
    -- Actually it is the filtering condition for account channel
    EXISTS
    (
        SELECT * FROM _SLX_AccountChannel WITH(NOLOCK) 
        WHERE ACCOUNTID = a.SLXID AND STATUS IN ('Active','Customer', 'Current')
    )
问题是:“为什么
不在
中似乎比
不存在
更快。”

我的答案是:它只是看起来更快,但它是一样的。(在本例中)

您是否实际测量了两个查询的时间并确认存在差异

或者你只是看了执行计划

据我所知,您在截图上看到的查询成本(53%对47%)是:

  • 估算的查询成本,即使计划是实际的
  • 这是查询成本,而不是时间,它是由CPU和IO“成本”组合而成的
在这种情况下,查询优化器似乎为两个查询生成了几乎相同的计划。对于计划中的某些运算符,计划的估计行数很可能(略有)不同,但实际性能是相同的,因为计划形状是相同的。如果估计的行数不同,则会导致不同的估计查询成本

要查看计划中的差异(如果有的话),我会使用类似的工具。它显示了更多的细节,您可以更轻松地比较查询的各个方面



重写查询以加快速度是另一个问题,我不打算在这里回答它。

您无需多次点击/加入
Salesforce\u联系人。这更紧凑、更快:

SELECT a.SFAccountID, a.SLXID, a.Name
FROM [dbo].[Salesforce_Accounts] a WITH(NOLOCK)
JOIN  _SLX_AccountChannel b WITH(NOLOCK)
    ON a.SLXID = b.ACCOUNTID
JOIN [dbo].[Salesforce_Contacts] c WITH(NOLOCK)
    ON a.SFAccountID = c.SFAccountID
WHERE b.STATUS IN ('Active','Customer', 'Current')
GROUP BY a.SFAccountID, a.SLXID, a.Name
HAVING MAX(c.Primary__C) = 0

中的
之间的差异可以忽略。

看看这是否有帮助(1)实际计划在我看来几乎相同。(2) 您需要测量查询的实际性能,而不是计划的估计性能。我对大型数据库的经验使我更喜欢
IN
而不是
EXISTS
。我也不再专门使用
CTE
,更多地使用临时表。哦,对不起,这是实际的执行计划。如果放大,计划看起来是一样的,但百分比略有不同。不要看估计值(是的,即使在实际计划中,这些百分比也是估计值),而是衡量实际绩效。读取次数、持续时间等。这种类型的
join
vs
where
在大多数情况下没有太大区别cases@JamieD77当它确实起作用时,它会更好。我这样做是为了生活。这一次更快。我可以得到一个简短的解释,为什么这会更快?谢谢以这种方式为生,你可能会认为你应该提到将联接分离成一个
子查询
语句中的
,而不是执行一个
独立的
,因为所有字段都来自
[Salesforce\u Accounts]
table@JamieD77也许当你开始以加入为生时,你会了解到加入会导致什么样的重复,甚至在Salesforce_客户中也可能不是唯一的。如果你有更好的东西,就把它贴出来。您确实收到了OP的评论,它更快了?在一个没有查询优化器的世界中,所有内容都在内存中,当然。在现实世界中,不太可能……在本地数据库中进行实验。请慷慨地填写,我想每张表有10万条记录。测量两个选项的时间(不在和左连接),你得到什么时间?嘿,垃圾话-1您完全错过了[dbo].[Salesforce\u Contacts].Primary\u C=0您怎么知道他只想要一个联系人不是Primary的帐户?这里有“和C.Primary\u C=0”。我不知道他想要什么,但我知道这个问题是怎么回事。@当你长大一点,我们可以讨论为什么将3个表连接在一起,然后使用distinct,因为您实际上只需要1个表中的信息,这是一种糟糕的做法建模者请不要反对@BUM I以重写像他的查询来解决性能问题为生,所以只要他在身边帮助别人,我对我的工作安全感就很好。
SELECT  a.SFAccountID, a.SLXID, a.Name 
FROM    [dbo].[Salesforce_Accounts] a
        LEFT JOIN [dbo].[Salesforce_Contacts] c ON a.SFAccountID = c.SFAccountID AND c.Primary__C = 1
WHERE
        EXISTS (SELECT  * 
                FROM SLX_AccountChannel b 
                WHERE b.ACCOUNTID = a.SLXID 
                    AND b.STATUS IN ( 'Active', 'Customer', 'Current' ))
        AND c.SFContactID IS NULL
SELECT 
    a.SFAccountID ,
    a.SLXID ,
    a.Name
FROM 
    [dbo].[Salesforce_Accounts] a
WHERE
    a.SFAccountID IN (SELECT SFAccountID 
                    FROM [Salesforce_Contacts] 
                    GROUP BY SFAccountID 
                    HAVING SUM(CAST(Primary__c AS INT) = 0))

    AND a.SLXID IN (SELECT ACCOUNTID 
                    FROM _SLX_AccountChannel 
                    WHERE [STATUS] IN ( 'Active', 'Customer', 'Current' ))
SELECT
    a.SFAccountID, a.SLXID, a.Name 
FROM 
    [dbo].[Salesforce_Accounts] a WITH(NOLOCK)
    CROSS APPLY 
    (
        SELECT SFAccountID 
        FROM [dbo].[Salesforce_Contacts] WITH(NOLOCK) 
        WHERE SFAccountID  = a.SFAccountID 
        GROUP BY SFAccountID
        HAVING MAX(Primary__C + 0) = 0 -- Assume Primary__C is a bit value
    ) b
WHERE
    -- Actually it is the filtering condition for account channel
    EXISTS
    (
        SELECT * FROM _SLX_AccountChannel WITH(NOLOCK) 
        WHERE ACCOUNTID = a.SLXID AND STATUS IN ('Active','Customer', 'Current')
    )
SELECT a.SFAccountID, a.SLXID, a.Name
FROM [dbo].[Salesforce_Accounts] a WITH(NOLOCK)
JOIN  _SLX_AccountChannel b WITH(NOLOCK)
    ON a.SLXID = b.ACCOUNTID
JOIN [dbo].[Salesforce_Contacts] c WITH(NOLOCK)
    ON a.SFAccountID = c.SFAccountID
WHERE b.STATUS IN ('Active','Customer', 'Current')
GROUP BY a.SFAccountID, a.SLXID, a.Name
HAVING MAX(c.Primary__C) = 0