读取数据:SELECT、WHERE、ORDER BY、LIMIT

大多数查询由 SELECT、WHERE、ORDER BY 和 LIMIT 四个子句组成。本课逐一说明它们的作用和组合方式。

第一条查询

一条查询只回答一个问题:我要哪些行的哪些列? 最短的问法是 SELECT *——所有列——再用 FROM 指一张表。

1

-- Every column of every product. Run it with F9 or Ctrl/Cmd + Enter.
SELECT *
FROM products
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

idcategory_idnamedescriptionskupricecoststock_quantityweight_kgattributesis_activecreated_atupdated_at
116Glass Orchard (Paperback ed.)Glass Orchard (Paperback ed.) — built to last.SKU-00001913.72443.951796.18{"color":"green","warranty_months":12,"material":"fabric"}12024-01-13 07:13:522024-01-13 07:13:52
217Luma The GeneLuma The Gene — built to last.SKU-00002332.64161.72835.893{"color":"silver","warranty_months":24,"material":"steel"}12022-06-16 08:45:202022-06-16 08:45:20
321Quill HelmetQuill Helmet — designed for daily use.SKU-00003196.26118.253998.261{"color":"silver","warranty_months":24}12022-08-26 20:06:032022-08-26 20:06:03
416Kite Glass Orchard 3Kite Glass Orchard 3 — built to last.SKU-000041719.68845.912435.263{"color":"silver","warranty_months":24}02022-07-19 11:34:272022-07-19 11:34:27
517The Gene (3rd ed.)The Gene (3rd ed.) — a dependable everyday choice.SKU-00005122.869.53571.898{"color":"red","warranty_months":36,"material":"aluminium"}12022-05-21 10:53:402022-05-21 10:53:40

浏览器会另开一个标签页打开 Planche,示例数据库已经载入,这条查询也已经放好。本页其他示例都会复用同一个标签页;在你按下 F9 之前,什么都不会执行。

* 表示“所有列”,LIMIT 20 表示“最多返回 20 行”。如果只是查看表的结构,就不必读取 products 的数千行数据;这些数据还需要传输和渲染。

Planche 也会限制结果行数(设置 › 安全 › 行数上限)。不过在查询中明确写 LIMIT,可以按预期控制数据库读取的数据量。

选择列

SELECT * 适合先查看表。确定需要哪些数据后,应明确列出列名,这样结果更易读,传输的数据也更少。

2

SELECT name, sku, price, stock_quantity
FROM products
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

nameskupricestock_quantity
Glass Orchard (Paperback ed.)SKU-00001913.72179
Luma The GeneSKU-00002332.6483
Quill HelmetSKU-00003196.26399
Kite Glass Orchard 3SKU-000041719.68243
The Gene (3rd ed.)SKU-00005122.8357

SELECT 中的列顺序就是结果列的顺序,与表本身的列顺序无关。要更改结果列名,可使用 AS。

用 WHERE 筛选行

SELECT 挑列,WHERE 挑行。它接受一个条件,只留下条件为真的那些行。

3

SELECT name, price, stock_quantity
FROM products
WHERE price > 100
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

namepricestock_quantity
Lean Startup (Deluxe ed.)107.3784
Pixel Sofa108.187
Terra Pillow Pro113.670
Pulse LED Strip114.86480
Brief Answers (3rd ed.)117.96194

比较运算符都是你能猜到的:=、<>(或 !=)、<、<=、>、>=。其中两个和别的语言不一样,每个人都会被绊一次:

  • 相等只写一个 =,不是 ==。
  • 没有任何东西等于 NULL,NULL 自己也不行。不管有多少商品没记重量,WHERE weight_kg = NULL 一行都匹配不到。你要的判断是 IS NULL / IS NOT NULL。下一课会正经讲它。

用 ORDER BY 排序

没有 ORDER BY,行回来的顺序是未定义的。如果你在意顺序,就写出来;如果没写,就别指望眼前看到的这个顺序,因为索引一变、数据一变,它立刻就会变。

4

SELECT name, price
FROM products
ORDER BY price DESC
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

nameprice
Glide GPS Watch Pro2484.4
Terra 3D Puzzle2481.77
Aria Castle Kit2479.41
Aria LED Strip Pro2463.41
Nova Brief Answers2445.49

DESC 从大到小,ASC(默认)从小到大。注意这条查询里的组合:ORDER BY price DESC 配上 LIMIT 20,就是"前 20 名"的写法。在脑子里把两个子句调个个儿,就能看出顺序为什么重要——数据库是先把全部排好序,再取前二十行。

在 Planche 里你很少需要手写这一条。点结果网格的列标题,它会在服务端对整张表排序,按 升序 → 降序 → 取消 循环。看看它生成的 SQL,也是检查自己写法的好办法。

按多个列排序

写上几个排序键,后一个负责打破前一个留下的平局。

5

SELECT category_id, name, price
FROM products
ORDER BY category_id, price DESC
LIMIT 30;
在 Planche 中运行

结果 — 共 30 行,这里是前 5 行

category_idnameprice
2Aria Notebook2116.4
2Ember Laptop Plus2087.98
2Sol Notebook1982.34
2Sol Ultrabook1942.47
2Nimbus Ultrabook1933.06

这会把商品按分类聚在一起,并在每个分类内部把最贵的放前面。读起来就是"先按分类,再按价格降序"——正是你会脱口而出的那句话。

表达式和别名

SELECT 后面不一定是列。可以是算术、函数调用,或任何能为每一行产生一个值的东西——而 AS 给这个结果起名字。

6

SELECT name,
       price,
       cost,
       ROUND(price - cost, 2) AS margin
FROM products
WHERE is_active = 1
ORDER BY margin DESC
LIMIT 15;
在 Planche 中运行

结果 — 共 15 行,这里是前 5 行

namepricecostmargin
Ember Tab Plus2412.93984.941427.99
Aria LED Strip Pro2463.411098.691364.72
Luma Denim Jacket Pro2357.59993.241364.35
Drift A Winter Letter2422.51082.431340.07
Nova Brief Answers2445.491108.091337.4

ROUND(price - cost, 2) AS margin 算出了一个表里根本不存在的列,而 margin 这个名字随后可以在 ORDER BY 里用。但它在 WHERE 里用不了,这一点经常把人绊住。原因在于数据库执行子句的顺序:

FROM  ->  WHERE  ->  SELECT  ->  ORDER BY  ->  LIMIT

轮到 ORDER BY 时,SELECT 已经把 margin 算出来了。而 WHERE 运行时它还不存在,所以 WHERE margin > 50 是个错误——你只能把表达式重写一遍,或者把整条查询再包一层。把这个顺序记住;WHERE 在分组前筛行、HAVING 在分组后筛,也是同一个顺序解释的,那是再下一课的内容。

现在你能做什么

只用这几个子句就能回答不少真实问题:最贵的二十个商品、所有缺货的商品、在售目录里利润率最高的那些。还做不到的是跨两张表提问——哪个客户下了哪张订单——那就是连接要解决的事。

继续之前,把上面例子里的数字和列名改一改再跑一遍。网格是原地刷新的,试错的循环很快。

自己动手写

  1. 仍在售的商品里最便宜的十个,只显示名称和价格。(is_active 为 1 表示在售。)

    查看答案

    查看答案

    SELECT name, price
    FROM products
    WHERE is_active = 1
    ORDER BY price
    LIMIT 10;
    在 Planche 中运行

    结果 — 共 10 行,这里是前 5 行

    nameprice
    Clean Code (2nd ed.)6.89
    Nimbus Desk Max7.21
    Drift Castle Kit Max17.35
    Pixel Laptop18.46
    Terra Dumbbell Set19.8

    三个子句各干一件事:WHERE 去掉不在售的,ORDER BY price 把便宜的放前面(升序是默认的,不用写关键字),LIMIT 10 到此为止。

  2. 每个商品的库存金额是多少?显示名称、数量,以及价格 × 数量并保留两位小数,最大的排在前面。

    查看答案

    查看答案

    SELECT name,
           stock_quantity,
           ROUND(price * stock_quantity, 2) AS stock_value
    FROM products
    ORDER BY stock_value DESC
    LIMIT 10;
    在 Planche 中运行

    结果 — 共 10 行,这里是前 5 行

    namestock_quantitystock_value
    Luma Boots4921042454.52
    Glide GPS Watch Pro4141028541.6
    Brief Answers (Deluxe ed.)434988448.02
    Brief Answers (Hardcover ed.)413980879.13
    Vega Field Watch464975221.28

    第三列在表里根本不存在;AS stock_value 给它起了名字,而因为 ORDER BY 在 SELECT 之后执行,这个名字可以直接用来排序。