Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 不存在vs不在_Sql_Sql Server_Optimization_Plsql - Fatal编程技术网

Sql 不存在vs不在

Sql 不存在vs不在,sql,sql-server,optimization,plsql,Sql,Sql Server,Optimization,Plsql,我正在优化一些SQL查询(这可能是我最近发布的一个问题的第2部分),并将一些NOT IN替换为NOT EXISTS谓词 我认为这样做的主要好处是,使用notexists,您可以得到这样的好处:当找到单个匹配项时,语句将终止,但如果不使用counting子查询,则必须执行完整的表扫描,这是对的吗 如果所选数据包含空值,则NOT IN似乎还需要额外的工作,这是否正确 在我在proc中实现它们之前,我需要确保在这两种情况下,第二个语句比第一个语句(以及功能上等效的语句)更好: 案例1:

我正在优化一些SQL查询(这可能是我最近发布的一个问题的第2部分),并将一些NOT IN替换为NOT EXISTS谓词

我认为这样做的主要好处是,使用notexists,您可以得到这样的好处:当找到单个匹配项时,语句将终止,但如果不使用counting子查询,则必须执行完整的表扫描,这是对的吗

如果所选数据包含空值,则NOT IN似乎还需要额外的工作,这是否正确

在我在proc中实现它们之前,我需要确保在这两种情况下,第二个语句比第一个语句(以及功能上等效的语句)更好:

案例1:

        --exclude sessions that were tracked as part of a conversion during the last response_time minutes
        -- AND session_id NOT IN (SELECT DISTINCT tracked_session_id    
        --                              FROM data.conversions WITH (NOLOCK)
        --                              WHERE client_id = @client_id
        --                              AND utc_date_completed >= DATEADD(minute, (-2) * cy.response_time, @date)
        --                              AND utc_date_completed <= @date     
        --                              AND utc_date_clicked <= @date)

        AND NOT EXISTS (SELECT 1
                            FROM data.conversions WITH (NOLOCK)
                            WHERE client_id = @client_id
                            AND utc_date_completed >= DATEADD(minute, (-2) * cy.response_time, @date)
                            AND utc_date_completed <= @date
                            AND utc_date_clicked <= @date
                            AND data.conversions.tracked_session_id = d.session_id
        )

Cheers

不存在
运行查询,对行进行计数,如果计数=0,则返回
true

notin
运行查询,对结果进行迭代,将给定值与结果进行比较,如果不匹配,则返回
true

通常第一种方法要快得多

当然,您必须注意从周围的查询中包含哪些列/值到子查询中,因为这可能会导致子查询运行N次(外部结果集中的每行一次)


还有另一种方法:使用
外部连接将表A连接到表B,并检查表B中的列是否为
NULL
。这将只运行一次子查询。它只能在更简单的情况下使用(不适用于多个表联接链)

你是对的,空值有很大的不同。
notin
查询检查每个元素是否完全不匹配。与null进行比较不会产生最终结果。因此,如果子查询包含null,则不会将其视为“
不在”
”中

这种行为的非直观的副作用是,
不在
实际上不是
中的
的对立面

不存在
查询不存在此问题


对于哪一种性能更好,我会犹豫是否做任何笼统的陈述,因为这通常取决于发生了什么样的优化。这就是为什么如果你关心绩效,那么找出执行计划是很重要的。

正如你正确地说的,这两者是不同的。如果
中不为
的项的子查询包含
NULL
则不会返回任何结果,因为没有任何项等于
NULL
,也没有任何项不等于
NULL
(甚至不等于NULL)

假设您使用这两种方法获得相同的结果,那么只要您在
in
语句中处理
NULL
值,这两种方法之间就没有区别。优化器非常聪明,知道如果消除了
NULL
值,或者使用不可为NULL的列,这两个列是相同的,所以使用相同的
反半联接

考虑以下两个表:

CREATE TABLE T (ID INT NOT NULL PRIMARY KEY);
CREATE TABLE T2 (ID INT NOT NULL PRIMARY KEY);
这两个查询得到完全相同的执行计划:

SELECT  *
FROM    T
WHERE   ID NOT IN (SELECT ID FROM T2);

SELECT  *
FROM    T
WHERE   NOT EXISTS (SELECT ID FROM T2 WHERE T.ID = T2.ID);
SELECT  *
FROM    T
WHERE   ID NOT IN (SELECT ID FROM T3 WHERE T3.ID IS NOT NULL);

SELECT  *
FROM    T
WHERE   NOT EXISTS (SELECT ID FROM T3 WHERE T.ID = T3.ID);
因为优化程序知道T2.ID是不可为空的列。第三张表:

CREATE TABLE T3 (ID INT);
如果ID列既没有索引也不可为空,则这两个查询呈现非常不同的执行计划:

SELECT  *
FROM    T
WHERE   ID NOT IN (SELECT ID FROM T3);

SELECT  *
FROM    T
WHERE   NOT EXISTS (SELECT ID FROM T3 WHERE T.ID = T3.ID);
而不存在将更有效率。然而,这两种方法(本质上)再次产生相同的执行计划:

SELECT  *
FROM    T
WHERE   ID NOT IN (SELECT ID FROM T2);

SELECT  *
FROM    T
WHERE   NOT EXISTS (SELECT ID FROM T2 WHERE T.ID = T2.ID);
SELECT  *
FROM    T
WHERE   ID NOT IN (SELECT ID FROM T3 WHERE T3.ID IS NOT NULL);

SELECT  *
FROM    T
WHERE   NOT EXISTS (SELECT ID FROM T3 WHERE T.ID = T3.ID);
所有这些查询和示例数据都已打开

编辑

要真正回答您的问题:

如果
跟踪的会话id
数据中是不可为空的列,则案例1的性能将与
不在
不存在的情况相同。转换
,或者在IN语句中添加
跟踪的会话id不为空的
。如果列不可为null,并且不排除null值,则性能将不相同,并且假设不存在null
不存在
将执行得更好,如果不存在null,则结果将不相同,因此性能不具有可比性

案例2的示例数据实际上让我很惊讶,我假设这不会优化为
反半联接
,并且已经写了一个答案,说了这么多,但就在保存编辑之前,我认为我最好检查一下,并惊讶地看到:

SELECT  *
FROM    T
WHERE   (   SELECT  COUNT(*) 
            FROM    T3
            WHERE   T.ID = T3.ID
        ) = 0;
不存在时的优化完全相同
。因此,乐观主义者似乎比我想象的更聪明,如果你希望计数不是0,它只会生成一个不同的计划


执行计划是否会告诉您是否不在执行表扫描?我个人会看一下计划中关于性能/IO统计信息的内容,而不是其他内容。不幸的是,我无法(轻松地)对数据源运行这些存储过程,以获得一个以Oracle 10g IN/开始的查询计划,而不是显式转换为EXISTS/NOT。“不存在”与“不存在”一样有效。但是,如果子查询可能返回空值,则不要使用not IN。not IN的可能重复项不太明显,因为
not EXISTS
可以在找到单个匹配项时立即终止,同样,
not IN
可以优化为执行
左反半联接,该联接也会在找到第一个匹配项时终止,并且可以使用索引
不存在
不在
都优化为使用
左反半联接
。这是非常错误的。正如@ChrisChilvers所说,它们都作为左反半联接运行。区别在于空值的大小handled@ChrisChilvers很想知道。你能提供一些参考吗?这就是我的想法(行计数vs迭代),不是吗?如果你要求@ChrisChilvers,请看一下执行计划。在mssql 2005中,由于许多人在IF语句中使用该语法,他们增加了对
count(*)=0
count(*)>0
的优化。虽然存在是更好的选择,因为技术上是正确的。