代码之家  ›  专栏  ›  技术社区  ›  Rob Bednark Bohdan

SELECT查询返回相同的结果,无论是EXISTS还是NOT EXISTS——为什么?

  •  0
  • Rob Bednark Bohdan  · 技术社区  · 11 年前

    我正在调试下面的SQL语句,试图了解它的行为。

    我很惊讶地发现,如果我改变 NOT EXISTS 为了正义 EXISTS (并查询相同的、不变的数据),我得到了完全相同的输出(这是行的计数,例如。, 237 ). 这怎么可能?

    我希望改变 不存在 为了正义 存在 将从返回正行数(例如。, 237 )返回 0 .

    SELECT count(*) FROM blog_tags
    WHERE blog_tags.subscribed = true
    AND blog_tags.special = true
    AND EXISTS (
        SELECT 1
        FROM tags
        INNER JOIN blog_tags AS bt ON bt.tag_id = tags.id
        INNER JOIN blogs ON bt.blog_id = blogs.id
        WHERE blogs.org_id = 22
        AND NOT EXISTS ( /* Removing the "NOT" on this line has no effect */
            SELECT 1
            FROM blog_tags
            INNER JOIN tags AS tg ON blog_tags.tag_id = tg.id
            INNER JOIN blogs AS t ON blog_tags.blog_id = t.id
            WHERE t.org_id = 4
            AND t.active = true
            AND t.type = 'foo'
            AND t.priority_id = blogs.priority_id
            AND tg.name = tags.name
        )
    );
    

    我想知道我是否在概念上理解错误。将其改写为伪代码:

    /* select_1 */
    SELECT count(*) FROM sometable_1
    WHERE condition_1a
    AND condition_1b
    AND EXISTS (
        /* condition_1c (the entire EXISTS inside these parentheses) */
        /* select_2 */
        SELECT 1
        FROM sometable2
        INNER JOIN join_expression_1a
        INNER JOIN join_expression_1b
        WHERE condition_2a
        AND NOT EXISTS ( /* Removing the "NOT" on this line has no effect */
            /* condition_2b (the entire NOT EXISTS inside these parentheses */
            /* select_3 */
            SELECT 1
            FROM sometable1
            INNER JOIN join_expression_2a
            INNER JOIN join_expression_2b
            WHERE condition_3a
            AND condition_3b
            AND condition_3c
            AND condition_3d
            AND condition_3e
        )
    );
    

    以下是我对上述伪代码的解释。这些解释是真的吗?

    1. count(*) 只能在以下情况下返回非零行数 (condition_1a AND condition_1b AND condition_1c) True
    2. condition_1c 仅在以下情况下为True (condition_2a=True AND condition_2b=False)
    3. 如果整个表达式返回非零行数,则 condition_2b 必须是 False 为了 不存在 成为 真的 .
    4. 如果整个表达式返回非零行数,则更改 不存在 存在 应导致整个表达式返回 0 .

    我正在使用 PostgreSQL v9.2.8

    2 回复  |  直到 11 年前
        1
  •  2
  •   Community CDub    8 年前

    关于问题更新中添加的“解释”:

    1. count(*) 只能在以下情况下返回非零行数 (condition_1a AND condition_1b AND condition_1c) True

    计数(*) 从不返回NULL,但返回零( 0 ),当现在找到行时。这使得它在标准聚合函数中具有特殊性。 Per documentation:

    需要注意的是,除了 count ,这些函数返回 未选择行时为空值。

    你的意思可能是:

    count(*) can only return a non-zero number of rows

    但你对事件的顺序也很模糊。 WHERE JOIN 为每个单独的输入行评估条件。聚合函数 计数(*) 已评估 之后 那个考虑 SELECT 查询:

    正确的句子是:

    计数(*) 只能在以下情况下返回非零数字 (条件_1a与条件_1b与条件_1c) 评估为 TRUE 对于一个或多个输入行 .

    2. condition_1c 仅在以下情况下为True (condition_2a=True AND condition_2b=False)

    对的

    3. 如果整个表达式返回非零行数,则 condition_2b 必须是 False 为了 NOT EXISTS 成为 真的 .

    参见1。此外,如果您 EXISTS 表达式不是常量(引用外部查询的列或调用任何易失性函数) 存在 表达式可以是 不同的 对于每个输入行。

    4. 如果整个表达式返回非零行数,则更改 不存在 存在 应导致整个表达式返回 0 .

    不准确的 -如果 存在 表达式不是常量。见3。改变 不存在 存在 可能导致 任何 行数。

    鉴于你建立在不正确的假设基础上,我建议你重新评估你的发现,并提出 SSCCE 如果可以的话 .

        2
  •  2
  •   simo.3792    11 年前
    ...
        AND NOT EXISTS ( /* Removing the "NOT" on this line has no effect */
            /* condition_2b (the entire NOT EXISTS inside these parentheses */
            /* select_3 */
            SELECT 1
            FROM sometable1
            INNER JOIN join_expression_2a
            INNER JOIN join_expression_2b
            WHERE condition_3a
            AND condition_3b
            AND condition_3c
            AND condition_3d    --- this condition links select_2 to select_3
            AND condition_3e    --- this condition links select_2 to select_3
        )
    );
    

    condition3d和condition3e将select2与select3链接起来,但它是一个相当松散的耦合,因为 优先级_id 名称 可以分别链接到不同的博客和标签。在没有看到实际数据的情况下,我建议通过指定select_2.blog_id=select_3.blog_id(或类似),可能需要在select_2和select_3之间建立更紧密的链接。

    关于伪代码,我对代码所说的内容进行以下解释:

    1. 如果有一个来自org=22的标签 不是 来自org=4的blog_tag,其中活动的“foo”博客具有相同的优先级,标记具有相同的名称。
    2. 如果有一个来自org=22的标签 是一个 来自org=4的blog_tag,其中活动的“foo”博客具有相同的优先级,标记具有相同的名称。

    在情况1中。Select_2将返回一堆行,其中标记、博客和blog_tag的组合排除匹配条件。可能是标签a、b、d和d;例如f。

    在案例2中。Select_2将返回一堆行,其中标记、博客和blog_tag的组合包含匹配条件。例如,可能是标签c、e、g、k。

    无论哪种方式,Select_2都找到了一些东西,这就是返回所有结果所需的全部内容。

    注意:查询之间的别名非常特殊,很难看到每个查询中使用的特定表。blog_tags在select_1或select_3中都没有别名,blogs被别名为t。我建议在查询中的所有实例中始终使用相同的表首字母缩略词(即blog_tag总是bt),然后为每个实例添加一个数字(即bt1、bt2等)。如下:

    SELECT count(*) FROM blog_tags AS bt  -- add alias
    WHERE bt.subscribed = true
    AND bt.special = true
    AND EXISTS (
        SELECT 1
        FROM tags AS t1   -- add alias
        INNER JOIN blog_tags AS bt1 ON bt1.tag_id = t1.id -- change alias
        INNER JOIN blogs AS b1 ON bt1.blog_id = b1.id  -- change alias
        WHERE b1.org_id = 22
        AND NOT EXISTS ( /* Removing the "NOT" on this line has no effect */
            SELECT 1
            FROM blog_tags AS bt2 -- change alias
            INNER JOIN tags AS t2 ON bt2.tag_id = t2.id -- change alias
            INNER JOIN blogs AS b2 ON bt2.blog_id = b2.id -- change alias
            WHERE b2.org_id = 4
            AND b2.active = true
            AND b2.type = 'foo'
            AND b2.priority_id = b1.priority_id
            AND t2.name = t1.name
        )
    );
    
    推荐文章