我正在调试以下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
)
);
以下是我对上述伪代码的解释。这些解释是真的吗?
count(*)
为(condition_1a AND condition_1b AND condition_1c)
,则True
只能返回非零行数
condition_1c
仅在(condition_2a=True AND condition_2b=False)
condition_2b
必须为False
,NOT EXISTS
为True
。NOT EXISTS
更改为EXISTS
会导致整个表达式返回0
。我正在使用PostgreSQL v9.2.8
答案 0 :(得分: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之间建立更紧密的链接。
关于你的伪代码,我对代码所说的内容采取以下解释:
如果情况1. Select_2将返回一堆行,其中标签,博客和blog_tag的组合排除了匹配条件。可能是标签a,b,d&例如。
如果是2.Select_2将返回一堆行,其中标签,博客和blog_tag的组合包含匹配条件。例如,可能是标签c,e,g,k。
无论哪种方式,Select_2都找到了一些东西,只需要返回所有结果。
注意:查询之间的别名非常临时,很难看出每个查询中特定表的使用位置。 blog_tags在select_1或select_3中没有别名,博客的别名为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
)
);
答案 1 :(得分:1)
关于你的解释"在问题更新中添加:
如果1.
为count(*)
,
(condition_1a AND condition_1b AND condition_1c)
True
只能返回非零行数
count(*)
永远不会返回NULL,但会在找到行时返回零(0
)。这使得它在标准聚合函数中很特殊。 Per documentation:
应该注意除
count
外,这些函数返回a 没有选择行时为空值。
你可能意味着:
count(*) can only return a non-zero numberof rows
但你对事件的顺序也很模糊。对每个输入行评估WHERE
和JOIN
条件。在之后评估聚合函数count(*)
。考虑SELECT
查询中的事件序列:
正确的句子是:
count(*)
只有(condition_1a AND condition_1b AND condition_1c)
评估为TRUE
的一个或多个输入行才能返回非零数字
时才为真
2.
condition_1c
仅在(condition_2a=True AND condition_2b=False)
正确。
3.
如果整个表达式返回非零行数,则condition_2b
必须为False
才能NOT EXISTS
成为True
。
参见1.另外,如果EXISTS
表达式不是常量(引用外部查询的列或调用任何volatile函数),EXISTS
表达式的结果可能不同< / em>为每个输入行。
4.
如果整个表达式返回非零行数,则将NOT EXISTS
更改为EXISTS
会导致整个表达式返回{{ 1}}。
不正确 - 如果0
表达式不是常量。请参阅3.将EXISTS
更改为NOT EXISTS
可能会导致任何行数。
鉴于您的建议是基于错误的假设,我建议您重新评估您的发现,如果可以,请使用SSCCE 返回。