我时不时会看到这样的情况:
SELECT u.* FROM users u
WHERE 0 = (SELECT COUNT(*) FROM addresses a WHERE a.user_id = u.id);
这让我有点痛苦。因此,我想写一写它的问题所在,以及如何避免这种结构。
让我们来看看某些人在写这篇文章时是怎么想的。该查询将返回所有地址为零的用户。换句话说,就是从未提供过任何地址的用户。
这有什么不好呢?毕竟,如果我们需要查找没有地址的用户,它就能完成需要做的事情?没错。不对。
问题在于计算。如果一个用户有数百万个地址,会发生什么?Pg 会尽职尽责地数完所有地址,然后拒绝这一行,因为其中有问题。
问题是,你并不需要有一百万个地址。即使有两个地址,也需要进行计数,虽然时间成本会比计数一百万行低,但它仍然存在,而且是 100% 的无用功。
因此,问题可能是:如何正确地做这件事?很简单,有一个EXISTS表达式:
SELECT u.* FROM users u
WHERE NOT EXISTS (SELECT FROM addresses a WHERE a.user_id = u.id);
只要找到一条记录,EXISTS(在本例中为 NOT EXISTS)就会检查并放弃测试。无需计数。
当然,在某些(大多数?)情况下,当人们使用这种结构(0 == 计数)时,有人会说:"但在我们的系统中,这并不重要:"但在我们的情况下,这并不重要,因为......"。这就是标题中 "最佳实践"的由来。一个人不应该做错事,即使在这种特殊情况下它们并不重要,至少不要养成做错事的习惯,然后在真正重要的时候不小心犯了同样的错误。