PostgreSQL中的HAVING子句

10

我正在将MySQL查询重写为PostgreSQL。我有一张包含文章的表和另一张包含类别的表。我需要选择所有至少有一篇文章的类别:

SELECT c.*,(
    SELECT COUNT(*) 
    FROM articles a 
    WHERE a."active"=TRUE AND a."category_id"=c."id") "count_articles" 
FROM articles_categories c 
HAVING (
    SELECT COUNT(*) 
    FROM articles a 
    WHERE a."active"=TRUE AND a."category_id"=c."id" ) > 0

我不知道为什么,但这个查询会导致错误:

ERROR:  column "c.id" must appear in the GROUP BY clause or be used in an aggregate function at character 8
2个回答

15
HAVING子句有一些棘手的地方需要理解。我不确定MySQL如何解释它,但可以在这里找到PostgreSQL文档:http://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-HAVING。本质上说:

 

存在HAVING将查询转换为分组查询,即使没有GROUP BY子句。这与查询包含聚合函数但没有GROUP BY子句时发生的情况相同。所有选定的行都被认为形成一个单独的组,并且SELECT列表和HAVING子句只能引用聚合函数中的表列。如果HAVING条件为真,则此类查询将发出单个行;如果条件不成立,则不发出任何行。

这篇博客文章也解释了相同的内容,它展示了没有GROUP BYHAVING隐含地意味着SQL:1999标准的“grand total”,即GROUP BY ( )子句(这在PostgreSQL中不被支持)。

考虑到您实际的查询和要求,只需重写整个查询并JOINarticles_categoriesarticles中:

SELECT DISTINCT c.*
FROM articles_categories c
JOIN articles a 
ON a.active = TRUE 
AND a.category_id = c.id

替代方案:

SELECT *
FROM articles_categories c
WHERE EXISTS (SELECT 1 
                FROM articles a
               WHERE a.active = TRUE 
                 AND a.category_id = c.id)

但是这个结果无论如何都需要进行分组,或者我必须使用DISTINCT(),是吗? - Radek Simko
当然可以。非常抱歉...我已经更正了查询并提供了一个替代方案。 - Lukas Eder
在 SQL 中,HAVING 在没有 GROUP BY 也是合法的,并且 PostgreSQL 也支持。这与问题无关。 - Peter Eisentraut
@Peter,你说得对。我不知道这一点。感谢你的提示。Postgres文档指出,如果没有任何GROUP BY子句,则添加HAVING子句会强制执行隐式的GROUP BY:http://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-HAVING。它还指出:“如果HAVING条件为真,则此类查询将发出单个行,如果不为真,则发出零行。”。所以这与问题有更多关系。你可以再次给我点赞 :-) - Lukas Eder
@Lukas Eder:完成。 :) 您的更新答案非常准确。 - Peter Eisentraut
1
太棒了! :) 再次感谢你的提示。即使是回答问题,人们也总是在stackoverflow上学习... - Lukas Eder

0
SELECT * FROM categories c
WHERE
EXISTS (SELECT 1 FROM article a WHERE c.id = a.category_id);

应该没问题...可能更简单一些 ;)


请问您能解释一下您的查询吗?它如何修复作者问题? - Augustin R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接