INSERT
写出列名,再给每个列一个值。没写的列用默认值——这里 added_at 默认取当前时间。
1
INSERT INTO wishlists (customer_id, product_id)
VALUES (1, 288);
1 行被修改
浏览器会另开一个标签页打开 Planche,示例数据库已经载入,这条查询也已经放好。本页其他示例都会复用同一个标签页;在你按下 F9 之前,什么都不会执行。
一行被修改。明确写出列名、而不是依赖表里的列顺序,正是这条语句在别人加了一个列之后还能继续正常工作的原因。
数据库会拒绝什么
把完全相同的语句再跑一遍。
2
-- The same statement a second time. (customer_id, product_id) is the
-- primary key of wishlists, so the database refuses the duplicate.
INSERT INTO wishlists (customer_id, product_id)
VALUES (1, 288);
错误
报错,什么也没变。wishlists 上有 PRIMARY KEY (customer_id, product_id),所以同一组合不能存在两次——是约束拒绝了它。约束不是需要绕开的障碍,它是在阻止你应用里的一个 bug 变成永久错误的数据。
UPDATE,以及那个最要紧的子句
两个习惯能让这种事难得多。第一,先写 WHERE 再写 SET。第二,先当成 SELECT 跑一遍——一样的 FROM、一样的 WHERE——看清返回什么,再去改。
第二件事 Planche 能替你做。打开设置 › 安全 › 预览受影响的行,UPDATE 或 DELETE 会先被改写成对应的 SELECT,数好条数并展示给你,然后才执行。只读连接和给生产环境连接标颜色,也在同一处。
4
SELECT id, first_name, last_name, loyalty_points
FROM customers
WHERE id = 1;
结果 — 1 行
| id | first_name | last_name | loyalty_points |
|---|---|---|---|
| 1 | Liam | Fischer | 3619 |
DELETE
用子查询挑出要动的行
UPDATE 和 DELETE 的 WHERE 就是普通的 WHERE,所以子查询那一课的内容在这里全都能用。要根据另一张表的内容去改这张表的行,靠的就是子查询。
6
-- Which rows to change is its own question, and a subquery can answer it.
UPDATE customers
SET loyalty_points = loyalty_points + 50
WHERE id IN (
SELECT customer_id
FROM orders
WHERE status = 'refunded'
AND total > 2000
);
242 行被修改
有 242 位客户存在金额超过 2,000 的退款订单,于是这 242 行被改了。前面那个习惯照样适用:先当成 SELECT id FROM customers WHERE id IN (...) 跑一遍数一数,因为出错的地方就在子查询里。
7
DELETE FROM wishlists
WHERE product_id IN (
SELECT id FROM products WHERE stock_quantity = 0
);
260 行被修改
NOT EXISTS 和 EXISTS 在这里也能用,而取反的场景更应该选它们,理由子查询那一课已经讲过。真正不可移植的是在 UPDATE 或 DELETE 里直接连接——每个引擎的写法都不一样,其中一个干脆做不到。方言页会把五种都讲一遍。
事务
事务把若干语句捆在一起,要么全都发生,要么全都不发生——而且在你提交之前,还允许你看看结果再改主意。
8
BEGIN;
UPDATE coupons
SET used_count = used_count + 1
WHERE code = 'WELCOME10';
SELECT code, used_count FROM coupons WHERE code = 'WELCOME10';
ROLLBACK;
结果 — 1 行
| code | used_count |
|---|---|
| WELCOME10 | 63 |
BEGIN 开启事务,SELECT 里优惠券数量已经加上了,ROLLBACK 又把它放了回去。把 ROLLBACK 换成 COMMIT,改动就变成永久的。自己确认一下计数没有变:
9
SELECT code, used_count FROM coupons WHERE code = 'WELCOME10';
结果 — 1 行
| code | used_count |
|---|---|
| WELCOME10 | 62 |
在真实数据库上做有风险的改动时,这就是那件对的工具:开事务、执行语句、确认行数和预期一致,然后才提交。Planche 把它做成了一种模式——连接 › 手动事务会一直开着一个事务,并把提交和回滚放到工具栏上,于是你跑的每条语句在你表态之前都只是暂时的。
现在你能做什么
除了查清一条查询为什么慢,其他都能做了——那是最后一课。