SQL Spec 是否需要在 EXISTS() 中使用 GROUP BY

Eva*_*oll 11 postgresql sql-server group-by sql-standard exists

Microsoft 当前允许使用此语法。

SELECT *
FROM ( VALUES (1) ) AS g(x)
WHERE EXISTS (
  SELECT *
  FROM ( VALUES (1),(1) )
    AS t(x)
  WHERE g.x = t.x
  HAVING count(*) > 1
);
Run Code Online (Sandbox Code Playgroud)

请注意,没有GROUP BYEXISTS条款,是有效的ANSI SQL。或者它只是暴露了一个实现细节。

作为参考,PostgreSQL 中不允许使用相同的语法。

错误:列“tx”必须出现在 GROUP BY 子句中或用于聚合函数中

但是这种语法是允许的..

SELECT *
FROM ( VALUES (1) ) AS g(x)
WHERE EXISTS (
  SELECT 1  -- This changed from the first query
  FROM ( VALUES (1),(1) )
    AS t(x)
  WHERE g.x = t.x
  HAVING count(*) > 1
);
Run Code Online (Sandbox Code Playgroud)

并且这种语法是允许的。

SELECT *
FROM ( VALUES (1) ) AS g(x)
WHERE EXISTS (
  SELECT *
  FROM ( VALUES (1),(1) )
    AS t(x)
  WHERE g.x = t.x
  GROUP BY t.x  -- This changed from the first query
  HAVING count(*) > 1
);
Run Code Online (Sandbox Code Playgroud)

问题来自在聊天中与@ErikE 的对话

Eva*_*oll 11

我在 SQL 2011 规范中找到了它...

如果<select list>“*”只包含在 a 中<table subquery>,而 a 直接包含在 an 中<exists predicate>,则 the<select list>等价于 a <value expression>,即任意的<literal>

这证实了*在这种情况下不等同于任意文字,实际上 PostgreSQL 违反了规范。

请记住,这是一个不同的问题

SELECT *
FROM ( VALUES (1),(2),(3) ) AS t(x)
HAVING count(*) > 1
Run Code Online (Sandbox Code Playgroud)

这两个数据库都拒绝。

PostgreSQL,

错误:列“tx”必须出现在 GROUP BY 子句中或用于聚合函数中

SQL 服务器,

列 'tx' 在选择列表中无效,因为它不包含在聚合函数或 GROUP BY 子句中。

为什么这个错误在 PostgreSQL 中仍然存在

感谢 irc.freenode.net/#PostgreSQL 上的 RhodiumToad 帮助解决这个问题。他还指出解决这种情况的难度

20:33 < RhodiumToad> 一个问题是,在 pg 中你可以做 exists(select func() from ... 其中 func() 是一个可能返回 0 行的 SRF

SRF 是一个集合返回函数。

例如,在 PostgreSQL 中,我们可以使用 SRF 生成 1-10 的系列(generate_series在核心中)

SELECT * FROM generate_series(1,10); 
Run Code Online (Sandbox Code Playgroud)

而且,我们同样可以把它放在这里。

SELECT generate_series(1,10);
Run Code Online (Sandbox Code Playgroud)

他们两个一起给我们一个交叉连接(笛卡尔积)

SELECT generate_series(1,10), generate_series(1,2);
Run Code Online (Sandbox Code Playgroud)

但是,如果其中任何一个返回 0 行,您将一无所获。实际上与此相同

SELECT * FROM ( VALUES (1) ) AS t(x)
CROSS JOIN ( SELECT 1 LIMIT 0 ) AS g;
Run Code Online (Sandbox Code Playgroud)

而且,这就是完全优化它的问题。您可以在 EXIST 语句内的选择列表中有一个 SRF,该语句返回 0 行,并强制 EXISTS 评估为 false。