当前位置:首页 > 综合

sql查询前100条数据

写SQL查前100条数据,询前最直接的条数办法是 SELECT * FROM table_name LIMIT 100;。这条语句在MySQL、询前PostgreSQL、条数SQLite里都能跑。询前但你要真拿它去面试或者干活,条数光知道这一句远远不够——不同数据库对“取前N条”的询前实现方式差别不小,用错了轻则报错,条数重则把全表扫一遍。询前

先说MySQL。条数LIMIT 100 的询前意思很明确:扫到100条就停。但这里有个细节,条数如果没有 ORDER BY,询前数据库返回的条数100条是不确定的。我见过有人拿这个去查“最新的询前100条订单”,结果跑出来一堆半年前的数据。原因很简单,InnoDB 默认按主键顺序返回,但主键顺序不等于时间顺序。所以只要涉及“前100条”这个语义,就必须想清楚:这个“前”是按什么排的?

加上排序之后,写法变成 SELECT * FROM orders ORDER BY created_at DESC LIMIT 100;。这条语句的逻辑是:先按创建时间倒序排,再取前100条。问题在于,如果 created_at 上没有索引,MySQL 会把整张表读出来做 filesort,数据量一大就直接拖垮数据库。我自己的经验是,只要 ORDER BY 的字段没有索引,LIMIT 100 反而比不加 LIMIT 更慢,因为排序本身要消耗大量临时空间。

SQL Server 的写法不一样。它不支持 LIMIT,得用 SELECT TOP 100 * FROM table_name;。如果要排序,就是 SELECT TOP 100 * FROM orders ORDER BY created_at DESC;。注意 TOP 和 ORDER BY 的位置关系,TOP 写在 SELECT 后面,ORDER BY 写在最后。Oracle 又是另一套,早期版本用 ROWNUM,而且 ROWNUM 和 ORDER BY 的执行顺序有坑——ROWNUM 是在排序之前分配的,所以 SELECT * FROM (SELECT * FROM orders ORDER BY created_at DESC) WHERE ROWNUM <= 100; 才是对的。Oracle 12c 之后有了 FETCH FIRST 100 ROWS ONLY,总算跟上了其他数据库的节奏。

PostgreSQL 支持 LIMIT,也支持 FETCH FIRST。但 PG 有个特点,LIMIT 和 ORDER BY 配合时,如果排序字段有索引,它会走索引扫描,效率很高。如果没有索引,PG 的排序策略和 MySQL 不太一样,小数据量下表现还行,数据量大了照样慢。我试过在一张两千万行的表上跑 ORDER BY 无索引字段 LIMIT 100,PG 花了将近40秒,MySQL 直接超时。

分页场景下,LIMIT 100 只是第一页。第二页写成 LIMIT 100 OFFSET 100。OFFSET 越大,数据库要跳过的行越多,性能越差。有人测过,OFFSET 到100万的时候,查询时间可能从几毫秒涨到几秒。解决办法是用游标分页,比如记住上一页最后一条的 id,下一页写成 WHERE id > last_id ORDER BY id LIMIT 100;。这种方式不管翻到第几页,性能都稳定。

还有一个容易忽略的点:LIMIT 100 返回的“100条”是行数,不是去重后的结果。如果查询里有 JOIN,一条主表记录可能对应多条从表记录,返回的100行里可能只有20条不同的主表数据。这时候要么加 DISTINCT,要么先用子查询把主表筛出来再 JOIN。我见过一个报表查询,开发以为 LIMIT 100 是100个用户,实际上返回的是100条订单明细,用户数只有十几个。

最后说一个实际案例。之前有个同事写了个定时任务,每5分钟查一次 SELECT * FROM logs ORDER BY id DESC LIMIT 100;,然后把结果同步到另一个库。跑了半年没问题,后来日志表涨到8000万行,查询开始变慢。原因是他只建了主键索引,ORDER BY id DESC 虽然能走主键,但 MySQL 优化器在某些版本下会选择全表扫描再排序。后来改成 WHERE id > (SELECT MAX(id) - 100 FROM logs) ORDER BY id DESC LIMIT 100;,速度才恢复正常。这个例子说明,LIMIT 100 写起来简单,但背后的执行计划得看具体环境和数据量。

分享到: