测验:深入 PostgreSQL:第 2 部分
你是SQL硬核还是SQL软妹?
第2部分,共2部分。 返回第1部分
欢迎来到我的深度 PostgreSQL 测验第 2 部分!
本测验涵盖了常见与鲜为人知的 PostgreSQL 特性和坑点:JOIN、NULL、ANSI SQL、事务、内部机制、索引、数据抽样……以及更多!
祝你好运! 🍀
PostgreSQL (v10+) 中用于自增 IDENTITY 列的(SQL 标准)语法是什么?
虽然 SERIAL 仍被广泛使用,但 SQL 标准 的方式是:
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEYid INT IDENTITY(1,1)是 SQL Server 语法。id INT AUTO_INCREMENT是 MySQL 语法。id INTEGER AUTO_INCREMENT PRIMARY KEY是 MySQL 语法。
相较于 SERIAL,它有以下几个优势:
- 符合 SQL 标准
- 更好地处理序列所有权
- 对手动插入的语义更清晰
- 在复制环境下表现更佳
SERIAL 实际上只是创建序列并设置默认值的简写,这可能在转储和恢复时导致问题。
除“Depends on side effects”之外, 错误发生后会怎样?
BEGIN;UPDATE students SET status = 'alumni' WHERE grade = 12;UPDATE students SET graduation_date = CURRENT_DATE WHERE grade = 12;UPDATE students SET grade = 'GRADUATED'; -- 🚨 Error: type mismatchCOMMIT;当此处出现错误时:
- 整个事务会被回滚
- 没有任何更改被提交
- 事务被标记为失败
若想部分提交:
BEGIN;UPDATE ...;SAVEPOINT my_save;UPDATE ...; -- ErrorROLLBACK TO my_save;COMMIT;哪个索引更适合此查询?
-- Query:SELECT * FROM studentsWHERE grade_level = 42 AND last_name IN ('Levy', 'Smith');对我来说,令人惊讶的是即使查询文本中的列顺序与索引定义不匹配,多列索引仍然可以被使用。
注意: 对于 B‑tree 索引,首列非常重要。因为此查询按 grade_level 和 last_name 过滤,首列为 grade_level、随后为 last_name 的索引最合适。
SQL 能选取的行越少,性能就越好。
例如,如果你有一个 task 表,并且知道 status 的选择性高于 created_at,可以为类似 status = 'done' AND created_at > '2024-01-01' 的查询创建以 status 为首列的索引。真正决定性能的不是 WHERE 子句中文本的条件顺序,而是索引列的顺序以及谓词的形态。
B+Tree 索引可用于:
- 首列或最左前缀列。
=,>,<,BETWEEN,IN等操作符,具体取决于查询形态。- 前缀匹配,如
LIKE 'prefix%'。
感谢 u/mwdb2 指出此解释中早期的错误!
如何指定包含句点的列名?
CREATE TABLE teachers ( id INT GENERATED BY DEFAULT AS IDENTITY, first.name TEXT, -- How do you include the column: first.name? salary NUMERIC);PostgreSQL 使用双引号来引用标识符:
- 双引号
"..."用于标识符(列名、表名) - 单引号
'...'用于字符串字面量 - 形式
first.name会被解释为table_name.column_name
如果不加引号,列名中的句点会被当作模式/表分隔符!此外,所有未加引号的标识符默认会被转为小写。
为了减少意外,最好使用 snake_case 并避免使用特殊字符。
哪种语法可以**随机抽样 10%**的学生?
这道题很刁钻!大约有 6 个陷阱,例如:
- 高层描述 + 语法碎片,考察你如何拼凑不完整的信息。
- 需要熟悉
TABLESAMPLE语法,因为没有一个TABLESAMPLE选项使用了正确的写法!(阴险!我知道!继续,加油,我相信你!!! ❤️) ROW_NUMBER() OVER (ORDER BY RAND())和ORDER BY RANDOM() > 10只是红鲱鱼。它们看起来像是可行的,但其实不对。RAND()并不存在,且RANDOM() > 10永远为假,因为RANDOM()的取值范围是0.0-1.0。 另外,ORDER BY RANDOM() > 10甚至会在布尔值上排序,而不是在随机数上。> 10看起来像是百分比,但其实不是。BERNOULLI是合法的,但SAMPLETABLE不是。WHERE RANDOM() >= 0.1是个陷阱!RANDOM()返回0.0 <= x < 1.0,所以>= 0.1会返回约 90% 的行,而不是 10%。
PostgreSQL 支持两种抽样方法:
BERNOULLI:每行等概率抽取SYSTEM:块级抽样(更快但随机性稍差)
想了解更多抽样技巧,请阅读来自 Render 的文章。
示例:
-- Sample 10% of rowsSELECT * FROM students TABLESAMPLE BERNOULLI (10);
-- For repeatable results, use REPEATABLE:SELECT * FROM studentsTABLESAMPLE BERNOULLI (10) REPEATABLE (42);还有一点值得一提,TABLESAMPLE 已被 报告 说随机性不佳。如果需要更随机的固定大小抽样,可以考虑使用 ORDER BY RANDOM() LIMIT 10。但要注意,这会返回 10 行,而不是 10%,且在大表上可能很慢。
市面上也有一些第三方扩展提供更好的随机抽样方法,但超出本题范围 😅
这将返回多少行?
SELECT COUNT(*)FROM studentsWHERE grade = NULL;好,这有点儿棘手。关键是 x = NULL 永远不会匹配任何行,它总是返回一行,计数为 0。
因为 NULL 是未知值,不能与任何东西比较,甚至不能与它自身比较。
检查 NULL 值的正确方式是使用 IS NULL。
示例:
postgres=# SELECT count(*) FROM students WHERE age = null;count------- 0(1 row)
postgres=#postgres=# SELECT count(*) FROM students WHERE age is null;count------- 3(1 row)EXPLAIN ANALYZE 对 UPDATE 语句有什么作用?
EXPLAIN ANALYZEUPDATE studentsSET grade = grade + 1WHERE status = 'active';EXPLAIN ANALYZE 实际上会执行查询!在分析写操作时要小心,尤其是 UPDATE、DELETE 和 INSERT 语句。每次运行 EXPLAIN ANALYZE 后可能需要执行清理操作,以获得可重复的结果。
最佳实践:
- 对 UPDATE/DELETE/INSERT 使用不带 ANALYZE 的
EXPLAIN - 将修改包装在事务中:
BEGIN;EXPLAIN ANALYZE ...;ROLLBACK;做得好!你深入探讨了 PostgreSQL 的多个方面!🐘
希望你学到了新东西,或者至少拿到了可以炫耀的分数!🏆
想要生活更刺激?看看我的 测验合集吧,乐趣无穷*!