查询的执行顺序
前几课的子句凑在一起,一条查询可以很长。先看一条把它们全用上的:
1SELECT category,2 COUNT(*) AS product_count,3 ROUND(AVG(price), 2) AS avg_price4FROM products5WHERE stock > 06GROUP BY category7HAVING SUM(stock) >= 508ORDER BY avg_price DESC9LIMIT 2;
| category | product_count | avg_price |
|---|---|---|
| 数码 | 3 | 27.50 |
| 厨房 | 2 | 10.95 |
意思是:有货的商品按分类分组,只看库存加起来至少 50 的分类,按平均价从高到低,取前 2 个。
我们写的时候 SELECT 在最前面,可数据库做的时候,SELECT 几乎排在最后。
真正的顺序
数据库照这个顺序一步一步做,每一步都拿上一步的结果接着做:
FROM:拿到products表,10 行。WHERE:一行一行看,台灯库存是 0,删掉,剩 9 行。GROUP BY:按分类分成 4 组。HAVING:箱包的库存加起来只有 40,整组删掉,剩 3 组。SELECT:每组算出category、product_count、avg_price,别名在这时才起好。ORDER BY:按avg_price从高到低排。LIMIT:只留前 2 行。
点上面那段的单步,把这 7 步一步一步走一遍,看每一步之后表变成了什么样。
没写的子句就跳过。最短的查询只有 FROM 和 SELECT 两步。
WHERE 里用不了别名
记住这个顺序,很多报错就说得通了。算每件商品的库存值(价格 × 库存),只要超过 500 的:
1SELECT name, price * stock AS stock_value2FROM products3WHERE stock_value > 500;
ERROR 1054 (42S22): Unknown column 'stock_value' in 'where clause'
没有叫「stock_value」的列(WHERE 里不能用 SELECT 里起的别名)。检查一下列名有没有写错;如果它是一段文字,要加引号:'stock_value'。
ERROR 1054:不认识 stock_value。WHERE 是第 2 步,起别名的 SELECT 是第 5 步,做 WHERE 的时候这个名字还不存在。
在 WHERE 里把算式再写一遍就好:
1SELECT name, price * stock AS stock_value2FROM products3WHERE price * stock > 5004ORDER BY stock_value DESC;
| name | stock_value |
|---|---|
| 数据线 | 1700.00 |
| 双肩包 | 598.50 |
| 咖啡杯 | 594.00 |
| 耳机 | 590.00 |
| 笔记本 | 540.00 |
| 手机壳 | 525.00 |
ORDER BY 里可以
上面那段的 ORDER BY stock_value DESC 没报错:ORDER BY 是第 6 步,在 SELECT 之后,别名已经有了。分组的查询也一样:
1SELECT customer_id, COUNT(*) AS order_count2FROM orders3GROUP BY customer_id4ORDER BY order_count DESC, customer_id5LIMIT 3;
| customer_id | order_count |
|---|---|
| 1 | 3 |
| 2 | 2 |
| 4 | 2 |
运行会怎样?
1SELECT name, price AS p2FROM products3WHERE p < 104ORDER BY p;
WHERE 在 SELECT 之前做,那时还没有 p 这个名字。ORDER BY p 本来没问题,可查询在 WHERE 就停了。
聚合在哪一步算
顺序也说明了第 9 课的 ERROR 1111:聚合函数要等第 3 步分完组才算得出来,第 2 步的 WHERE 用不上;第 4 步的 HAVING 和后面的 SELECT、ORDER BY 都可以用。所以按一组的结果排序,直接写聚合函数也行:
1SELECT category, SUM(stock) AS total_stock2FROM products3GROUP BY category4ORDER BY SUM(stock) DESC;
| category | total_stock |
|---|---|
| 数码 | 245 |
| 文具 | 200 |
| 厨房 | 100 |
| 箱包 | 40 |
小技巧: MySQL 比较宽松,GROUP BY 和 HAVING 里也认 SELECT 的别名。别的数据库很多不认。要到哪儿都能用,记住标准的规矩:别名只在 ORDER BY 里用。
LIMIT 最后做
LIMIT 是最后一步,在排好序之后才截。所以「最贵的 3 件」这样写是对的:先按价格从高到低排好,再取前 3 行。
1SELECT name, price2FROM products3ORDER BY price DESC4LIMIT 3;
| name | price |
|---|---|
| 耳机 | 59.00 |
| 双肩包 | 39.90 |
| 台灯 | 25.00 |
要是先截再排,取到的只是表里碰巧在前面的 3 行,那就不是「最贵的 3 件」了。
子句的书写顺序
做的顺序是一回事,写的顺序是固定的另一回事,不能随便调换:
SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT …
写错位置,比如把 WHERE 写在 GROUP BY 后面,就会得到 ERROR 1064:语法错误。下面第 3 题就是这样。
练一练
写好查询,点检查。提示随便看,实在想不出来再看答案。
修好它:别名
✓想查出金额(quantity * unit_price)至少 30 的明细行,却报了 ERROR 1054。改好它,列是 order_id 和 amount,按 amount 从大到小,一样的按 order_id 排。
| order_id | amount |
|---|---|
| 3 | 59.00 |
| 8 | 59.00 |
| 2 | 39.90 |
| 7 | 39.90 |
| 12 | 39.60 |
1SELECT order_id, quantity * unit_price AS amount2FROM order_items3WHERE amount >= 304ORDER BY amount DESC, order_id;
便宜货卖得最好的
✓从 order_items 里,只看单价低于 20 的明细,按商品分组,算出每件商品卖了几件;只留卖了至少 3 件的,按件数从多到少(一样的按 product_id),取前 3 个。列是 product_id 和 sold。
| product_id | sold |
|---|---|
| 1 | 10 |
| 8 | 6 |
| 2 | 3 |
1SELECT product_id, quantity, unit_price FROM order_items;修好它:顺序
✓想数一数每个分类里价格超过 10 的商品有几件,却报了 ERROR 1064。改好它,列是 category 和 COUNT(*),按 category 排序。
| category | COUNT(*) |
|---|---|
| 厨房 | 1 |
| 数码 | 3 |
| 箱包 | 2 |
1SELECT category, COUNT(*)2FROM products3GROUP BY category4WHERE price > 105ORDER BY category;
这一课学会了
- 执行顺序:
FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT。 - 书写顺序是固定的:
SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY、LIMIT,写错位置是ERROR 1064。 SELECT的别名在WHERE里还不存在(ERROR 1054),在ORDER BY里可以用。- 聚合函数在分组之后才有,所以
WHERE里不能用,HAVING、SELECT、ORDER BY里可以。 LIMIT最后做:先排好序再截。