数据库查询语句大全
SELECT 是数据数据库里最常用的动词,但很多人只会SELECT * FROM table,库查然后卡在WHERE上。询语这就像只会开自动挡车,数据手动挡一碰就熄火。库查真正让查询语句变得有杀伤力的询语,是数据那些隐藏在文档角落里的子句和函数——它们才是性能的分水岭。
先看一个真实场景。库查上个月我帮一家电商公司排查慢查询,询语他们的数据订单表有两千万行,一条统计当日销售额的库查语句跑了四十七秒。DBA把SQL发给我,询语我一眼就看到了问题:WHERE DATE(create_time) = '2024-05-20'。数据这写法没毛病,库查但DATE()函数让索引彻底失效,询语全表扫描不可避免。改成WHERE create_time >= '2024-05-20 00:00:00' AND create_time < '2024-05-21 00:00:00',查询直接降到0.3秒。别小看这个改动,索引是数据库的命根子,任何在索引列上做函数运算的行为都是自残。
JOIN是另一个大坑。内连接、左连接、右连接,概念上清清楚楚,但实际写起来容易犯迷糊。我见过有人用LEFT JOIN做筛选,结果莫名其妙多出重复行——因为他没搞明白,LEFT JOIN保留的是左表全部记录,右表匹配不上就补NULL。这时候如果WHERE里写了right_table.id IS NOT NULL,那跟INNER JOIN没区别,但性能差远了。正确做法是:需要过滤右表条件时,直接放JOIN的ON子句里,或者干脆用INNER JOIN。
聚合函数配上GROUP BY,是报表查询的核心。但新手常犯的错是把非聚合列塞进SELECT,MySQL默认允许,Oracle直接报错。比如SELECT user_id, order_id, SUM(amount) FROM orders GROUP BY user_id,这里的order_id到底取哪条?逻辑上就是错的。真要取某个特定订单,得用子查询或者窗口函数。窗口函数是进阶利器,ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY create_time DESC)能轻松拿到每个用户的最新订单,这活儿用GROUP BY写出来得绕一大圈。
说到子查询,有个性能陷阱必须提:IN子查询有时候会坑你没商量。MySQL 5.6以前,WHERE id IN (SELECT user_id FROM ...)会把子查询结果物化成临时表,数据量大就完蛋。现在优化器聪明了,但保险起见,能改写成EXISTS的就改写。EXISTS是半连接,找到一条就停,IN得全查完再比较。实际测试中,小表用IN没问题,大表配合索引用EXISTS更稳。
字符串处理函数是日常高频工具。SUBSTRING_INDEX()切分逗号分隔的标签,CONCAT_WS()拼接字段时自动处理NULL,REPLACE()做数据清洗。这些函数看着简单,组合起来能解决很多业务问题。比如从JSON字段里提取某个键值,MySQL的JSON_EXTRACT()配合单引号转义,比在应用层解析快得多。千万别在循环里逐行调数据库,一次SELECT出来用CASE WHEN做条件分支,效率翻倍。
分页查询是另一个性能杀手。传统写法LIMIT 100000, 20,MySQL得先扫十万行再扔掉,越到后面越慢。优化方案是延迟关联:先查主键ID,再回表取数据。SELECT * FROM orders WHERE id > (SELECT id FROM orders ORDER BY id LIMIT 100000, 1) LIMIT 20。这招在千万级表上效果立竿见影。还有个细节:排序字段一定要有索引,否则文件排序会拖垮整个查询。
写SQL别追求花哨,能用简单语句解决就别套复杂结构。我见过有人用三层嵌套子查询实现一个简单过滤,不如拆成两个临时表JOIN来得清晰。但反过来说,该用窗口函数的时候就别死脑筋用GROUP BY硬凑——那是在跟数据库较劲。多看看执行计划,EXPLAIN输出的type列,从ALL到index到range到ref到const,每一级提升都意味着性能质变。记住:查询语句不是写给人看的诗,是写给优化器看的命令。它喜欢什么,你就给它什么。