返回首页

SQL速查手册 - MyTools在线工具

🧰 MyTools

SQL 速查手册

全部
SQL
说明
SELECT * FROM users;
查询表中所有列所有行
SELECT id, name FROM users;
只查询指定列
SELECT name AS user_name FROM users;
列别名(AS 可省略)
SELECT DISTINCT city FROM users;
去重查询
SELECT * FROM users WHERE name = 'Tom';
等值条件查询
SELECT * FROM users LIMIT 10 OFFSET 20;
分页:跳过 20 条取 10 条
SELECT u.name, o.amount FROM users u JOIN orders o ON o.user_id = u.id;
表别名 + 连接查询
SELECT '固定值' AS label, NOW() AS ts;
SELECT 可直接输出常量与函数结果
SELECT COUNT(*) FROM users;
统计行数
SELECT VERSION();
查看数据库版本
SELECT * FROM users WHERE age > 18 AND city = '北京';
AND 组合条件
SELECT * FROM users WHERE city IN ('北京', '上海');
IN 匹配多个值
SELECT * FROM users WHERE age BETWEEN 18 AND 30;
BETWEEN 闭区间范围
SELECT * FROM users WHERE name LIKE '张%';
LIKE 模糊匹配,% 任意多字符
SELECT * FROM users WHERE name LIKE '_三';
_ 匹配单个字符
SELECT * FROM users WHERE email IS NULL;
判空必须用 IS NULL(= NULL 恒为假)
SELECT * FROM users WHERE NOT (age < 18);
NOT 取反
SELECT * FROM users ORDER BY age DESC, id ASC;
多列排序
SELECT * FROM users ORDER BY created_at DESC LIMIT 5;
取最新 5 条
SELECT city, COUNT(*) AS cnt FROM users GROUP BY city;
按城市分组计数
SELECT AVG(age), MIN(age), MAX(age) FROM users;
平均值/最小值/最大值
SELECT SUM(amount) FROM orders WHERE user_id = 1;
求和
SELECT city, COUNT(*) FROM users GROUP BY city HAVING COUNT(*) > 10;
HAVING 过滤分组结果(WHERE 不能用聚合)
SELECT COUNT(DISTINCT city) FROM users;
去重计数
SELECT COUNT(*), COUNT(email) FROM users;
COUNT(*) 算所有行,COUNT(列) 忽略 NULL
SELECT city, GROUP_CONCAT(name) FROM users GROUP BY city;
MySQL:组内拼接字符串(PG 用 STRING_AGG)
SELECT STRING_AGG(name, ",") FROM users;
PostgreSQL:组内拼接字符串
SELECT CASE WHEN age < 18 THEN '未成年' ELSE '成年' END AS stage FROM users;
CASE WHEN 条件表达式
SELECT * FROM a INNER JOIN b ON a.id = b.a_id;
内连接:只返回两表都匹配的行
SELECT * FROM a LEFT JOIN b ON a.id = b.a_id;
左连接:左表全保留,右表无匹配填 NULL
SELECT * FROM a RIGHT JOIN b ON a.id = b.a_id;
右连接:右表全保留
SELECT * FROM a FULL OUTER JOIN b ON a.id = b.a_id;
全外连接(MySQL 不支持,可用 UNION 模拟)
SELECT * FROM a LEFT JOIN b ON a.id = b.a_id WHERE b.a_id IS NULL;
左反连接:左表有、右表没有的行
SELECT * FROM a CROSS JOIN b;
笛卡尔积:所有行两两组合
SELECT * FROM users u JOIN orders o USING (user_id);
USING:同名列连接,结果列不重复
SELECT * FROM emp e JOIN emp m ON e.manager_id = m.id;
自连接:同一张表按不同角色连接
SELECT * FROM a NATURAL JOIN b;
自然连接:按所有同名列自动连接(慎用)
SELECT * FROM a LEFT JOIN b ON a.id = b.a_id AND b.status = 1;
连接条件放 ON:左表行仍全部保留
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > 100);
IN 子查询
SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
EXISTS 相关子查询(大表常比 IN 快)
SELECT * FROM (SELECT city, COUNT(*) c FROM users GROUP BY city) t WHERE c > 10;
派生表:子查询作临时表(必须有别名)
SELECT name, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_cnt FROM users u;
标量子查询作一列
WITH t AS (SELECT * FROM users WHERE age > 18) SELECT * FROM t;
WITH 定义 CTE,提升可读性
WITH a AS (SELECT ...), b AS (SELECT ... FROM a) SELECT * FROM b;
多个 CTE 可相互引用
WITH RECURSIVE dept_tree AS (SELECT id, parent_id, name FROM dept WHERE id = 1 UNION ALL SELECT d.id, d.parent_id, d.name FROM dept d JOIN dept_tree t ON d.parent_id = t.id) SELECT * FROM dept_tree;
递归 CTE 查组织架构树
SELECT * FROM users WHERE age > (SELECT AVG(age) FROM users);
与整体平均值比较
SELECT name, city, ROW_NUMBER() OVER (PARTITION BY city ORDER BY age DESC) AS rn FROM users;
组内编号:分区排序后给序号(1,2,3 不重复)
SELECT name, score, RANK() OVER (ORDER BY score DESC) FROM exam;
排名:并列同号且跳号(1,1,3)
SELECT name, score, DENSE_RANK() OVER (ORDER BY score DESC) FROM exam;
密集排名:并列同号不跳号(1,1,2)
SELECT name, score, LAG(score) OVER (ORDER BY day) FROM daily;
取上一行值(同比环比常用)
SELECT name, score, LEAD(score, 1, 0) OVER (ORDER BY day) FROM daily;
取下一行值,可指定默认值
SELECT name, SUM(amount) OVER (PARTITION BY city) AS city_total FROM orders;
分组聚合但不折叠行
SELECT name, SUM(amount) OVER (ORDER BY day ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cum FROM orders;
累计求和(滑动/累计窗口)
SELECT * FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY city ORDER BY created_at DESC) rn FROM users) t WHERE rn = 1;
经典:每组取最新一条
SELECT name, AVG(score) OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) FROM exam;
移动平均(3 行窗口)
INSERT INTO users (name, age) VALUES ('Tom', 20);
插入单行
INSERT INTO users (name, age) VALUES ('Tom', 20), ('Jerry', 22);
批量插入多行
INSERT INTO users (name, age) VALUES ('Tom', 20) RETURNING id;
PostgreSQL:插入后返回生成的 id
INSERT INTO users (id, name) VALUES (1, 'Tom') ON DUPLICATE KEY UPDATE name = VALUES(name);
MySQL UPSERT:存在则更新
INSERT INTO users (id, name) VALUES (1, 'Tom') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;
PostgreSQL UPSERT:冲突则更新
INSERT INTO users (id, name) VALUES (1, 'Tom') ON CONFLICT (id) DO NOTHING;
PostgreSQL:冲突则忽略
UPDATE users SET age = age + 1 WHERE city = "北京";
条件更新(不带 WHERE 会全表更新!)
DELETE FROM users WHERE created_at < "2020-01-01";
条件删除(不带 WHERE 会清空表!)
TRUNCATE TABLE users;
快速清空表并重置自增(不可回滚部分场景)
BEGIN; UPDATE ...; COMMIT;
事务:BEGIN 后执行,COMMIT 提交
BEGIN; UPDATE ...; ROLLBACK;
回滚事务,撤销所有未提交修改
CREATE TABLE users (id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, email VARCHAR(100) UNIQUE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);
建表:主键自增/非空/唯一/默认值
CREATE TABLE orders (id BIGINT PRIMARY KEY, user_id BIGINT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id));
外键约束
CREATE TABLE users (id BINARY(16) PRIMARY KEY);
MySQL 8 也可用 UUID 作主键(存 BINARY(16))
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
加列
ALTER TABLE users DROP COLUMN phone;
删列
ALTER TABLE users MODIFY COLUMN age INT NOT NULL DEFAULT 0;
MySQL:修改列定义
ALTER TABLE users RENAME TO members;
重命名表
DROP TABLE IF EXISTS users;
删表(IF EXISTS 防报错)
CREATE TABLE t (status VARCHAR(10) CHECK (status IN ('active', 'disabled')));
CHECK 约束限制取值
CREATE INDEX idx_users_city ON users (city);
为 WHERE 高频列建索引
CREATE UNIQUE INDEX idx_users_email ON users (email);
唯一索引:兼做约束与加速
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at);
联合索引:遵循最左前缀原则
CREATE INDEX idx_users_name_lower ON users (LOWER(name));
函数索引(PG/MySQL 8+)
DROP INDEX idx_users_city ON users;
删除索引
EXPLAIN SELECT * FROM users WHERE city = "北京";
查看执行计划,关注 type/rows/key
EXPLAIN ANALYZE SELECT ...;
真实执行并返回耗时(MySQL 8/PG 均支持)
SELECT * FROM orders WHERE YEAR(created_at) = 2024;
反例:列上用函数导致索引失效
SELECT * FROM users WHERE phone LIKE "%8888";
反例:前置 % 模糊查询无法走索引
SELECT COALESCE(nickname, name, "匿名") FROM users;
返回第一个非 NULL 的值
SELECT IFNULL(NULL, 0);
MySQL:NULL 时返回默认值(PG 用 COALESCE)
SELECT NULLIF(a, b);
a=b 时返回 NULL(常用于防除零:a/NULLIF(b,0))
SELECT ROUND(price, 2), CEIL(x), FLOOR(x), ABS(x) FROM t;
四舍五入/向上/向下取整/绝对值
SELECT CONCAT(first_name, " ", last_name) FROM users;
拼接字符串
SELECT SUBSTRING(name, 1, 3), LENGTH(name), UPPER(name) FROM users;
截取/长度/大写
SELECT REPLACE(url, "http://", "https://") FROM sites;
字符串替换
SELECT TRIM(" abc ");
去除首尾空格
SELECT DATE_FORMAT(created_at, "%Y-%m-%d %H:%i:%s") FROM users;
MySQL:日期格式化(PG 用 TO_CHAR)
SELECT DATE_ADD(NOW(), INTERVAL 7 DAY);
MySQL:当前时间加 7 天
SELECT DATEDIFF("2024-06-01", "2024-05-01");
MySQL:两个日期相差天数
SELECT EXTRACT(YEAR FROM created_at) FROM users;
提取年/月/日(标准 SQL,PG 通用)
SELECT NOW()::DATE;
PostgreSQL:类型转换简写(等价 CAST(NOW() AS DATE))
SELECT `name` FROM `user`; -- MySQL
MySQL 用反引号包裹保留字;PG 用双引号 "name"
SELECT * FROM t LIMIT 10 OFFSET 5; -- MySQL/PG
SQL Server 写法:OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY
SELECT * FROM t WHERE name ILIKE '%abc%'; -- PG
PG 专有:不区分大小写的 LIKE;MySQL 默认排序规则已不区分
SELECT * FROM t WHERE name ~ '^A'; -- PG
PG 支持正则匹配操作符 ~
SELECT id SERIAL PRIMARY KEY; -- PG
PG 自增列用 SERIAL/IDENTITY;MySQL 用 AUTO_INCREMENT
SELECT TO_CHAR(now(), 'YYYY-MM-DD'); -- PG
PG 日期格式化用 TO_CHAR;MySQL 用 DATE_FORMAT
SELECT * FROM t LIMIT 10; -- PG 分页取前10也可写 FETCH FIRST 10 ROWS ONLY
标准 SQL 分页写法与 LIMIT 等价
SHOW TABLES; -- MySQL
PG 等价:\dt 或查 information_schema.tables

SQL 速查手册:查询、JOIN 类型、窗口函数与方言差异的速查页,写 SQL 卡壳时的一分钟检索。

适用场景

  • 记不清 LEFT JOIN 与 INNER JOIN 的结果差异时对照图解
  • 窗口函数(ROW_NUMBER/RANK)的语法与用例速查
  • MySQL 与 PostgreSQL 语法差异的对照

常见问题

窗口函数和 GROUP BY 的区别一句话?

GROUP BY 把多行折叠成一行(丢明细),窗口函数每行都保留、额外附上"窗口内计算结果"(排名、累计、移动平均)。要明细又要聚合就用窗口函数。

SQL 方言差异大吗?

基础 SQL 标准通用,函数名、日期处理、分页写法(LIMIT vs TOP vs FETCH)差异明显。换数据库时重点核对这些部分。

相关工具