关于使用 SQL 的几个要点:
- 您不能在 WHERE 子句中使用列别名,但可以在 HAVING 子句中使用。这就是你得到错误的原因。
- 与使用相关子查询相比,使用 JOIN 和 GROUP BY 可以更好地进行计数。它会快得多。
- 使用 HAVING 子句过滤组。
这是我编写此查询的方式:
SELECT t1.id, COUNT(t2.id) AS num_things
FROM t1 JOIN t2 USING (id)
GROUP BY t1.id
HAVING num_things = 5;
我意识到这个查询可以跳过JOIN
with t1,就像在 Charles Bretana 的解决方案中一样。但我假设您可能希望查询包含 t1 中的其他一些列。
回复:评论中的问题:
不同之处在于该WHERE
子句在行上进行评估,然后GROUP BY
将组减少到每组一行。该HAVING
子句在组形成后进行评估。因此,例如,您不能使用;更改COUNT()
组的 HAVING
您只能排除组本身。
SELECT t1.id, COUNT(t2.id) as num
FROM t1 JOIN t2 USING (id)
WHERE t2.attribute = <value>
GROUP BY t1.id
HAVING num > 5;
在上面的查询中,WHERE
过滤匹配条件的行,HAVING
过滤至少有五个计数的组。
引起大多数人困惑的一点是当他们没有GROUP BY
从句时,所以它看起来像HAVING
并且WHERE
可以互换。
WHERE
在选择列表中的表达式之前进行评估。这可能并不明显,因为 SQL 语法将选择列表放在首位。WHERE
因此,您可以通过使用限制行来节省大量昂贵的计算。
SELECT <expensive expressions>
FROM t1
HAVING primaryKey = 1234;
如果您使用上述查询,则会为每一行计算选择列表中的表达式,只是因为条件而丢弃大部分结果HAVING
。但是,下面的查询仅计算与条件匹配的单行WHERE
的表达式。
SELECT <expensive expressions>
FROM t1
WHERE primaryKey = 1234;
回顾一下,查询是由数据库引擎根据一系列步骤运行的:
- 从表中生成一组行,包括由 生成的任何行
JOIN
。
- 根据行集评估
WHERE
条件,过滤掉不匹配的行。
- 在选择列表中为行集中的每个计算表达式。
- 应用列别名(注意这是一个单独的步骤,这意味着您不能在选择列表的表达式中使用别名)。
GROUP BY
根据子句,将组压缩为每组一行。
- 根据组评估
HAVING
条件,过滤掉不匹配的组。
- 根据
ORDER BY
子句对结果进行排序。