我有下表
CREATE TABLE `books` (
`book_id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) DEFAULT NULL,
`author_fname` varchar(100) DEFAULT NULL,
`author_lname` varchar(100) DEFAULT NULL,
`released_year` int(11) DEFAULT NULL,
`stock_quantity` int(11) DEFAULT NULL,
`pages` int(11) DEFAULT NULL,
PRIMARY KEY (`book_id`)
)
仅针对拥有多本书的作者尝试找出作者的第一本书和最后一本书的发行年份。以下是查询
SELECT author_lname,
MIN(released_year) first_release,
MAX(released_year) last_release,
COUNT(*) book_count,
MAX(pages) max_page_count
FROM books
GROUP BY author_lname
ORDER BY book_count DESC;
但我不能使用book_count
inwhere
子句,所以我可以做book_count > 1
我正在寻找为什么这是不可能的解释以及如何获得预期结果。
为什么不可能
该
WHERE
子句应用条件来一次一行地过滤行。子句中的表达式WHERE
只能引用连接行的列。也就是说,连接的多个表中的任何列,但不是由选择列表中的表达式形成的列的任何别名,也不是由多行聚合形成的列的任何别名。尽管该子句首先出现在查询语法中,但在按子句中的条件过滤掉行之后,
SELECT
才会对选择列表的表达式进行求值。WHERE
同样,选择列表中的聚合表达式仅应用于通过子句中的条件的行子集
WHERE
。因此该WHERE
子句必须首先过滤掉行。如何实现你想要的
有几种方法可以解决这个问题。
一种是使用派生表子查询。在子查询执行聚合和别名等操作后,外部查询可以在其
WHERE
子句中应用条件来过滤掉子查询生成的行。子查询中的 ORDER BY 应该没有什么区别,除非子查询也使用 LIMIT。
另一种方法是使用
HAVING
它,它类似于WHERE
过滤行,但它在聚合完成后起作用,并且选择列表表达式已被评估。可以将其视为HAVING
过滤组,而WHERE
过滤单个行。哪种方法更好?
我更喜欢在可能的情况下使用
HAVING
,因为派生表可能必须创建一个临时表来保存结果,而临时表可能会拖累性能。但在某些情况下,无论如何都需要使用派生表子查询。例如,也许您想将子查询的结果连接到另一个表。