无论是 EXISTS 还是 NOT EXISTS,SELECT 查询都会返回相同的结果——为啥?
Posted
技术标签:
【中文标题】无论是 EXISTS 还是 NOT EXISTS,SELECT 查询都会返回相同的结果——为啥?【英文标题】:SELECT query returns same thing whether EXISTS or NOT EXISTS -- why?无论是 EXISTS 还是 NOT EXISTS,SELECT 查询都会返回相同的结果——为什么? 【发布时间】:2014-08-12 23:26:02 【问题描述】:我正在调试以下 SQL 语句,试图了解它的行为方式。
我惊讶地发现,如果我将 NOT EXISTS
更改为 EXISTS
(并查询相同的、未更改的数据),我会得到完全相同的输出(这是行数,例如, 237
)。怎么会这样?
我预计将NOT EXISTS
更改为仅EXISTS
会将其从返回正数行数(例如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
)
);
以下是我对上述伪代码的解释。这些解释是真的吗?
-
如果
(condition_1a AND condition_1b AND condition_1c)
是True
,count(*)
只能返回非零行数
condition_1c
仅在 (condition_2a=True AND condition_2b=False)
时为 True
如果整个表达式返回非零行数,则 condition_2b
必须是 False
才能使 NOT EXISTS
成为 True
。
如果整个表达式返回非零行数,则将NOT EXISTS
更改为EXISTS
应该会导致整个表达式返回0
。
我正在使用PostgreSQL v9.2.8
【问题讨论】:
会不会是巧合?无论哪种方式,可能有相同数量的合格行。 重现问题的少量样本数据会很有趣且很有帮助。您确定连接条件或 WHERE 子句的其他部分不是问题的根源吗? 我猜org_id
永远不会是 22。
您是否尝试仅运行外部子查询,同时使用存在和不存在?您每次获得的行数是否相同?
@GordonLinoff:如果org_id
永远不是 22,则计数为 0,而不是 237。
【参考方案1】:
至于您在问题更新中添加的“解释”:
1.
如果(condition_1a AND condition_1b AND condition_1c)
是True
,count(*)
只能返回非零行数
count(*)
从不返回 NULL,而是在现在找到行时返回零 (0
)。这使得它在标准聚合函数中很特别。 Per documentation:
需要注意的是,除了
count
,这些函数都返回一个 未选择任何行时为空值。
你的意思可能是:
count(*) 只能返回非零行数
但你对事件的顺序也很模糊。为每个单独的输入行评估 WHERE
和 JOIN
条件。聚合函数 count(*)
在之后评估。考虑SELECT
查询中的事件顺序:
正确的句子是:
如果(condition_1a AND condition_1b AND condition_1c)
评估为TRUE
对于一个或多个输入行,count(*)
只能返回非零数字。
2.
condition_1c
仅在(condition_2a=True AND condition_2b=False)
时为真
正确。
3.
如果整个表达式返回非零行数,则condition_2b
必须为False
,以便NOT EXISTS
为True
。 p>
参见 1。此外,如果您的 EXISTS
表达式不是常量(引用外部查询的列或调用任何 volatile 函数),EXISTS
表达式的结果可能不同每个输入行。
4.
如果整个表达式返回非零行数,那么将NOT EXISTS
更改为EXISTS
应该会导致整个表达式返回0
。
不正确 - 如果EXISTS
表达式不是常量。请参阅 3。将 NOT EXISTS
更改为 EXISTS
可能会导致任何行数。
鉴于您建立在不正确的假设之上,我建议您重新评估您的发现并返回SSCCE如果可以的话。
【讨论】:
【参考方案2】:...
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
)
);
condition_3d 和 condition_3e 链接 select_2 和 select_3,但它是一个非常松散的耦合,因为 priority_id 和 name 可能分别链接到不同的博客和标签。在没有看到实际数据的情况下,我建议通过指定 select_2.blog_id = select_3.blog_id (或类似的),可能需要在 select_2 和 select_3 之间建立更紧密的联系。
关于你的伪代码,我对代码所说的内容进行以下解释:
-
如果存在来自 org=22 的标签,并且存在来自 org=4 的 IS NOT A blog_tag,其中活动的“foo”博客具有相同的优先级并且标签具有相同的名称。李>
在情况 1. Select_2 将返回一堆行,其中标签、博客和 blog_tag 的组合排除了匹配条件。例如,可能是标签 a、b、d 和 f。
在情况 2. Select_2 将返回一堆行,其中标签、博客和 blog_tag 的组合包含匹配条件。例如,可能是标签 c、e、g、k。
Select_2 无论如何都找到了一些东西,这就是它返回所有结果所需的全部内容。
注意:查询之间的别名是非常特殊的,很难看出每个查询中特定表的使用位置。 blog_tags 在 select_1 或 select_3 中都没有别名,blogs 的别名是 t。我建议在查询中的所有实例中始终对表使用相同的首字母缩写词(即 blog_tags 始终为 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
)
);
【讨论】:
以上是关于无论是 EXISTS 还是 NOT EXISTS,SELECT 查询都会返回相同的结果——为啥?的主要内容,如果未能解决你的问题,请参考以下文章
无论对象是从 nib 加载还是以编程方式创建,执行初始化代码的正确方法是啥?
Dart/flutter 小部件测试,无论是按键还是文本都找不到文本小部件
Cordova:无论如何,是不是可以在 Javascript 中检测 iOS 应用程序是作为调试版还是作为发布版构建的?
Kolmogorov-Smirnov 检验中的假设检验 - 无论是临界值还是 p 值
无论管道是失败还是成功,都向 GitLab 报告 Tekton 管道状态(使用 gitlab-set-status 任务)