跳到主要内容

SQL 速查表

填一次,语句跟着变

语句列表

点行首图标复制
70 条

查询与基础(8)

  • SELECT id, name, email FROM users LIMIT 10 ;

    查指定字段,最多返回若干行

    显式列出字段而不是 `*`:少读用不上的列,也避免表结构变更后按位置读错列

  • SELECT * FROM users LIMIT 10 ;

    看整行内容,排查时最常用

    只适合临时排查:`*` 会把大字段(JSON / 长文本)一起读出来,也依赖列顺序

  • SELECT COUNT(*) FROM users WHERE status = 'active' ;

    数一下符合条件的有多少行

    COUNT(*) 数行;COUNT(列名) 只数那一列非 NULL 的行,两者结果可能不同

  • SELECT id, name, email FROM users ORDER BY created_at DESC LIMIT 10 OFFSET 20;

    翻页:跳过前 20 行再取若干行

    OFFSET 越大越慢(前面那些行仍要读出来再丢掉);深翻页改用「记住上一页最后一条的排序键」

  • SELECT id AS user_id, name AS user_name FROM users LIMIT 10 ;

    给字段起别名,让结果列名好认

    别名只在结果集里生效,不影响表结构;`AS` 在多数库可省,但省掉后一旦拼错就变成两个列名,建议留着

  • SELECT CAST( id, name, email AS TEXT) FROM users LIMIT 10 ;

    临时转换字段类型(这里转成文本)

    Postgres 还可写 `列::text`;转换失败会直接报错,不会给个默认值

  • SELECT name || ' #' || id AS label FROM users LIMIT 10 ;

    把多个字段拼成一个显示用的字符串

    `||` 是标准写法(MySQL 默认要 `CONCAT()`,除非开了 PIPES_AS_CONCAT);有 NULL 参与时整段变 NULL,用 COALESCE 兜一下

  • SELECT id, name, email FROM users UNION ALL SELECT id, name, email FROM orders ;

    把两张表的查询结果摞在一起

    `UNION ALL` 不去重、不排序,比 `UNION` 快得多;两边列数、类型、顺序必须对得上(列名以前一条为准)

筛选与排序(8)

  • SELECT id, name, email FROM users WHERE status = 'active' ;

    按条件筛出需要的行

    条件是 `=`、`>` 这类比较式的组合;字符串要加单引号,NULL 不能用 `=` 判(见「判空」那条)

  • SELECT id, name, email FROM users ORDER BY created_at DESC ;

    按某个字段排序

    不写 ORDER BY 时行的顺序是**不保证**的,别依赖「看起来是按插入顺序」;NULL 排在最前还是最后各库不同

  • SELECT id, name, email FROM users WHERE id IN (1, 2, 3);

    在一组值里挑,等价于一串 OR

    列表元素多到几百个时改写成 JOIN 临时表或 `= ANY(数组)` 更好;`NOT IN` 遇到列表里有 NULL 会一行都不返回

  • SELECT id, name, email FROM users WHERE name LIKE 'prefix%';

    模糊匹配,这里找以某串开头的

    `%` 放开头就用不上普通索引,全靠全表扫;`ILIKE` 是 Postgres 写法,MySQL 默认排序规则本来就不区分大小写

  • SELECT id, name, email FROM users WHERE created_at BETWEEN '2026-01-01' AND '2026-01-31';

    取一个区间(含两端)

    BETWEEN 两端都含;写日期区间要小心时间部分 —— `<= 2026-01-31` 会漏掉当天 00:00 之后的记录,常用写法是 `< 下个月一号`

  • SELECT id, name, email FROM users WHERE deleted_at IS NULL;

    判断字段是否为空值

    NULL 表示「未知」,`= NULL` 永远不成立(结果是 NULL 而不是 true),只能用 IS NULL

  • SELECT id, name, email FROM users WHERE status = 'active' OR status = 'pending';

    几个条件满足其一即可

    AND 比 OR 先算:混着写必须加括号,否则条件会粘错

  • SELECT id, CASE WHEN status = 'active' THEN 1 ELSE 0 END AS is_active FROM users LIMIT 10 ;

    按条件在结果里生成一个新字段

    没写 ELSE 且条件都不满足时结果是 NULL(不是 0 也不是空串);要做「分档统计」用它比写多个子查询省事

聚合与分组(8)

  • SELECT status, COUNT(*) AS cnt FROM users GROUP BY status ORDER BY cnt DESC;

    按字段分组数数,看看每类各有多少

    SELECT 里没被聚合的列都必须出现在 GROUP BY 里,否则报错(MySQL 宽松模式下会随便取一行,更危险)

  • SELECT status, COUNT(*) AS cnt FROM users GROUP BY status HAVING COUNT(*) > 10;

    分组后按聚合结果再筛一遍

    WHERE 在分组**之前**过滤行、不能用聚合函数;HAVING 在分组**之后**。能挪到 WHERE 的条件就挪过去,少算一堆行

  • SELECT SUM(amount) AS total, AVG(amount) AS avg_amount FROM users WHERE status = 'active' ;

    求和与求平均

    AVG / SUM 会跳过 NULL,分母是「非 NULL 的行数」;想按总行数算平均要用 `SUM(x) / COUNT(*)` 或 `AVG(COALESCE(x, 0))`

  • SELECT MIN(created_at) AS first_at, MAX(created_at) AS last_at FROM users ;

    找最早与最晚的一条

    MIN/MAX 只给值不给那一行的其他字段;要连着其他列一起看,用 `ORDER BY … LIMIT 1` 或窗口函数取序号

  • SELECT COUNT(DISTINCT user_id) AS users FROM users ;

    数不重复的取值有几个(去重统计)

    COUNT(DISTINCT) 在大表上很贵(要排序或哈希去重);只要近似值可用预估行数(Postgres 的 `EXPLAIN`)替代

  • SELECT id, ROW_NUMBER() OVER (PARTITION BY status ORDER BY created_at DESC ) AS rn FROM users ;

    组内排名,给每一行编个序号

    窗口函数不改行数(不像 GROUP BY 会合并行);MySQL 8 / Postgres / SQL Server 支持,5.7 与更老的库没有

  • SELECT * FROM (SELECT id, name, email , ROW_NUMBER() OVER (PARTITION BY status ORDER BY created_at DESC ) AS rn FROM users ) t WHERE t.rn = 1;

    取每组里最新(或排序最靠前)的那一条

    必须套一层子查询再筛 rn —— 窗口函数的结果不能写在同一层的 WHERE 里(那时它还没算出来)

  • SELECT status, STRING_AGG(name, ', ') AS names FROM users GROUP BY status;

    把一组的多个值拼成一个字符串

    两者默认都不保证拼接顺序,要固定顺序得在聚合里写 `ORDER BY`(Postgres);结果串有长度上限

连接(7)

  • SELECT id, name, email FROM users JOIN orders ON orders.user_id = users.id ;

    只保留两张表都能对上的行

    `JOIN` 就是 `INNER JOIN`;两张表都有的列名要写全(`用户表.id`),否则报「列名有歧义」

  • SELECT id, name, email FROM users LEFT JOIN orders ON orders.user_id = users.id ;

    左连接:主表全保留,关联表没对上就补空

    关联表的条件写在 `ON` 上和写在 `WHERE` 上结果不同:写进 `WHERE` 会把补 NULL 的行滤掉,左连接退化成内连接

  • SELECT id, name, email FROM users RIGHT JOIN orders ON orders.user_id = users.id ;

    右连接:关联表全保留

    `A RIGHT JOIN B` 等于 `B LEFT JOIN A`:把两张表调个个儿写 LEFT JOIN,读起来清楚得多,实际很少用 RIGHT

  • SELECT id, name, email FROM users FULL OUTER JOIN orders ON orders.user_id = users.id ;

    两边都保留,对不上的地方补空

    Postgres / SQL Server 支持;**MySQL 没有 FULL OUTER JOIN**,要用 LEFT JOIN 与 RIGHT JOIN 的结果 UNION

  • SELECT id, name, email FROM users JOIN orders USING (id);

    同名字段做连接,少写一遍条件

    USING 的列在结果里只出现一次(不像 `ON a.id = b.id` 会出现两列);跨库移植性不如 ON

  • SELECT id, name, email FROM users CROSS JOIN orders LIMIT 10 ;

    笛卡尔积:每行都与另一张表的每行配一次

    结果行数是两边行数之积,几张万行表一交叉就是几亿行 —— 写漏连接条件时也会变成这个,看到结果行数暴涨先查 ON

  • SELECT child.id, parent.id AS parent_id FROM users child JOIN users parent ON child.parent_id = parent.id;

    同一张表自己连自己(层级 / 推荐关系)

    必须给同一张表起两个不同别名,否则第二处引用报错;层级很深时用递归 CTE

子查询与 CTE(7)

  • WITH recent AS (SELECT id, name, email FROM users WHERE created_at > NOW() - INTERVAL '7 days') SELECT * FROM recent;

    把一段查询提成带名字的临时结果,后面反复用

    CTE 比嵌套子查询好读,也便于分段调试;`INTERVAL` 的写法各库不同(MySQL 用 `DATE_SUB(NOW(), INTERVAL 7 DAY)`)

  • SELECT id, name, email FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id );

    只要另一张表里存在对应行就保留

    `EXISTS` 只判断有没有,子查询里写 `SELECT 1` 就够;它找到一行就停,通常比 `IN (子查询)` 更友好

  • SELECT id, name, email FROM users WHERE id IN (SELECT user_id FROM orders );

    把另一张表的查询结果当成条件值列表

    子查询结果里若有 NULL,`NOT IN` 会一行都不返回(这是最常踩的一条);`IN` 与 `EXISTS` 优化器常会改写成同一种计划

  • SELECT t.id FROM (SELECT id, status FROM users WHERE status = 'active' ) t LIMIT 10 ;

    把子查询当成一张临时表再用它查

    派生表必须有别名(这里 `t`),否则报语法错;多层嵌套 + UNION 时优先改成 CTE

  • SELECT id, name, email , (SELECT COUNT(*) FROM orders ) AS total FROM users LIMIT 10 ;

    子查询返回单个值时,可以当成一个字段用

    子查询必须只返回一行一列,多了直接报错;它对外层每一行都要算一次,放在 SELECT 里对性能不友好

  • SELECT id, name, email FROM users t, LATERAL (SELECT * FROM orders WHERE orders.user_id = users.id LIMIT 1) x;

    让子查询引用外层当前行(每行单独取一条关联记录)

    Postgres / MySQL 8.0.14+ 才支持;要用它解决「每组取最新一条」时,窗口函数通常更通用

  • WITH RECURSIVE tree AS (SELECT id, parent_id FROM users WHERE parent_id IS NULL UNION ALL SELECT c.id, c.parent_id FROM users c JOIN tree ON c.parent_id = tree.id) SELECT * FROM tree;

    递归展开层级数据(组织树、分类树)

    ⚠️ 数据里有环或条件写错会**一直递归下去**,把连接和磁盘吃满 —— 先在只读副本上试,或加深度上限(`WHERE depth < 10`)

    谨慎

建表与改表(8)

  • CREATE TABLE users (id BIGINT PRIMARY KEY, name TEXT NOT NULL, status TEXT DEFAULT 'active', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);

    建一张新表,带上主键、非空与默认值

    主键别用业务字段(手机号会改、身份证涉隐私);`BIGINT` 而不是 `INT`,避免自增到 21 亿后翻车

  • ALTER TABLE users ADD COLUMN remark TEXT DEFAULT '';

    给已有表加一列

    带默认值加列在部分库里会重写整张表(大表要选低峰期);新列默认允许 NULL,要非空得补 NOT NULL 与默认值

  • ALTER TABLE users ALTER COLUMN id TYPE BIGINT;

    改字段类型

    收窄类型(大变小)或改字符集可能锁表重写数据;MySQL 的 MODIFY 必须把列定义整段重写一遍,漏掉的属性会被丢掉

  • ALTER TABLE users DROP COLUMN remark;

    删掉一列

    ⚠️ 列里的数据一并删除且**不可回滚**。先确认没有代码在读它:`DROP COLUMN` 在 Postgres 上是立即生效的(MySQL 8 也是),生产上建议先改名、观察一周再删

    危险
  • DROP TABLE users ;

    整张表连结构一起删掉

    ⚠️ 表与数据一起消失,事务里未必能回滚(DDL 在多数库是隐式提交)。Postgres 可加 `IF EXISTS` 避免「表不存在」报错,但不会帮你确认「删对了表」

    危险
  • TRUNCATE TABLE users ;

    清空表里的所有行,保留表结构

    ⚠️ 比 `DELETE FROM` 快得多(不逐行记日志)但也更狠:多数库**不可回滚**,还会重置自增列、连带清掉级联引用。执行前先 `SELECT COUNT(*)` 确认是哪张表

    危险
  • CREATE OR REPLACE VIEW v_ users AS SELECT id, name, email FROM users WHERE status = 'active' ;

    把常用查询存成一个视图

    视图不存数据、每次查询展开原表;`OR REPLACE` 方便迭代,但列名或类型变化时可能报错,需要先 DROP 再建

  • COMMENT ON COLUMN users .status IS '订单状态';

    给表或字段加注释说明

    Postgres / Oracle 用独立的 COMMENT 语句;MySQL 把注释写在列定义里(`status TEXT COMMENT '订单状态'`),改注释要 MODIFY COLUMN

索引与性能(8)

  • CREATE INDEX idx_users_email ON users ( id, name, email );

    给常用筛选 / 排序字段建索引

    索引让查询快、写入慢(每次写都要维护索引);等值条件在前、范围条件在后,顺序反了用不上

  • CREATE INDEX CONCURRENTLY idx_users_email ON users ( id, name, email );

    不锁表地建索引(大表上线的写法)

    Postgres 专属,且不能在事务块里执行、失败会留一个「无效索引」需手动删;MySQL 对应写法是 `ALTER TABLE … ADD INDEX …, ALGORITHM=INPLACE, LOCK=NONE`

  • DROP INDEX idx_users_email ;

    删掉一个索引

    ⚠️ 删掉后依赖它的查询会突然变慢(甚至拖垮库),先看有没有慢查询在用;MySQL 必须写成 `DROP INDEX 索引名 ON 表名`,Postgres 不许加表名

    谨慎
  • EXPLAIN SELECT id, name, email FROM users WHERE status = 'active' ;

    看优化器打算怎么执行这条语句

    关注三件事:扫了多少行(rows)、走的是索引还是全表(Seq Scan / ALL)、有没有临时排序(Sort / Using filesort)

  • EXPLAIN ANALYZE SELECT id, name, email FROM users WHERE status = 'active' ;

    真的跑一遍,给每个步骤的实际耗时与行数

    ⚠️ `ANALYZE` 会**真正执行**这条语句:写操作要先包在事务里再 ROLLBACK,或有条件时加 `BEGIN; … ROLLBACK;` 包起来

    谨慎
  • SELECT indexname, indexdef FROM pg_indexes WHERE tablename = ' users ';

    看一张表上已有哪些索引

    `pg_indexes` 是 Postgres 的系统视图;MySQL 用 `SHOW INDEX FROM 表名`。看结构时用 `SHOW CREATE TABLE` 最直观

  • SELECT indexrelname, idx_scan FROM pg_stat_user_indexes WHERE relname = ' users ' ORDER BY idx_scan;

    看索引到底有没有被用到(用来找多余索引)

    Postgres 专属;`idx_scan` 很小(尤其为 0)的索引用不上却仍在拖慢写入,可考虑删。统计从上次重置起算,进程重启也会清零

  • SELECT pg_size_pretty(pg_total_relation_size(' users ')) AS total;

    看一张表连索引一共占多大

    Postgres 的 `pg_*size` 系列函数;MySQL 用 `SELECT data_length, index_length FROM information_schema.tables WHERE table_name = '表名'`

事务与锁(8)

  • BEGIN;

    开始一个事务,之后的语句要么一起成功、要么一起回滚

    事务**开着不提交**会一直占着锁与被改的行,排查时优先看有没有卡在 idle in transaction 的会话

  • ROLLBACK;

    撤销当前事务里的所有改动

    只能撤销**当前事务**里的改动;事务已提交就回不去了(要靠备份或反向补数据)

  • COMMIT;

    提交事务,让改动生效

    提交之后才真正落盘(并开始占用 WAL / binlog);`COMMIT` 之后再写 `ROLLBACK` 不会有任何效果

  • SAVEPOINT sp1;

    在事务里打一个存档点,可只回滚到这一点

    适合「批量写入里某一条错了但其余的想保留」:整段仍在同一个事务里,最后还是要 COMMIT 才生效

  • SELECT * FROM users WHERE id = 1 FOR UPDATE;

    锁住这一行,别的事务改不了(读-改-写的经典写法)

    ⚠️ 锁在**事务提交前一直有效**:忘提交就把这一行卡死,别的事务会排队等(`lock_timeout` 到了就报错)。锁的粒度看有没有走索引,没走索引会升级成锁全表行

    谨慎
  • SELECT * FROM users WHERE status = 'pending' LIMIT 1 FOR UPDATE SKIP LOCKED;

    抢一条待处理任务,被别人占着就跳过(任务队列写法)

    `SKIP LOCKED` 让并发消费者各拿各的任务而不互相等;MySQL 8 / Postgres 9.5+ 支持。取到的这条在提交前对别人不可见

  • SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

    设置当前事务的隔离级别

    Postgres 默认 READ COMMITTED,MySQL InnoDB 默认 REPEATABLE READ;级别越高一致性越强、冲突与重试越多。这个词的**语义在各库并不完全一致**

  • SELECT pid, state, wait_event_type, query FROM pg_stat_activity WHERE datname = ' app_db ';

    看当前有哪些连接在干什么(谁卡住了)

    Postgres 看 `pg_stat_activity`(`wait_event_type = 'Lock'` 就是卡在等锁),MySQL 用 `SHOW FULL PROCESSLIST` 或 `information_schema.innodb_trx`

排错与元信息(8)

  • SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = ' public ' ORDER BY table_name;

    列出这个库里有哪些表

    `information_schema` 是标准视图、各库都有(MySQL 的 `table_schema` 就是库名);想连大小一起看用各库自己的系统表

  • SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = ' users ' ORDER BY ordinal_position;

    看一张表的字段、类型、是否可空与默认值

    `ordinal_position` 是列的物理顺序;同名表出现在多个 schema 时会都列出来,加上 `table_schema` 条件更准

  • SELECT index_name, column_name, seq_in_index FROM information_schema.statistics WHERE table_name = ' users ' ORDER BY index_name, seq_in_index;

    看索引由哪些列按什么顺序组成

    `seq_in_index` 就是组合索引里的顺序 —— 判断「这个条件能不能用上索引」要靠它,只看索引名看不出顺序

  • SELECT count(*) AS conns FROM pg_stat_activity WHERE datname = ' app_db ';

    数当前有多少连接,排查连接数打满

    连接数满通常不是「人太多」而是**连接没释放**(应用没关连接池 / 卡住的事务占着);调大上限前先看 `state` 那一列

  • SELECT query, calls, mean_exec_time FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10 ;

    按平均耗时排出最慢的语句

    Postgres 要先 `CREATE EXTENSION pg_stat_statements` 并重启;统计口径是「同一种语句模板合并」,看 `mean_exec_time` 也要看 `calls`

  • SELECT pg_terminate_backend(12345);

    踢掉一个卡住的连接(进程号从 pg_stat_activity 里拿)

    ⚠️ 直接掐断连接:**它正在跑的事务会回滚**,客户端会收到连接中断。先确认 `state` / `query` 是不是真该杀;MySQL 对应 `KILL 12345;`

    危险
  • VACUUM ANALYZE users ;

    回收垃圾行并更新统计信息,让优化器选对计划

    Postgres 的 `VACUUM` 清理失效行、`ANALYZE` 更新统计(两条常一起跑);MySQL 的 `ANALYZE TABLE` 只更新统计、不回收空间(要 `OPTIMIZE TABLE`,会锁表)

  • SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name = ' users ';

    看谁对这张表有什么权限

    报「权限不足」时先确认 `current_user` 到底是哪个账号(应用连接池里配的常不是你以为的那个);`information_schema` 只列当前用户看得见的授权

语句里加绿底的是你填的值,纯绿字是示例值;本页不连数据库、不执行任何语句,复制到自己客户端里跑。

SQL 编辑器

12 行 · 264 字符 · 高亮只做词法上色,不校验语法