禅道SQL Server分页问题
禅道SQL Server分页问题
禅道从 MySQL 切到 SQL Server 后,最先炸的是分页。
MySQL 里随手写:
1 | SELECT id, name |
SQL Server 里不认这个写法。
当时页面报错大概就是 SQL 语法错误,查日志才看到 LIMIT。
MySQL 的 LIMIT
MySQL 常见两种:
1 | -- 取前10条 |
第二个里面:
1 | 20 是 offset |
SQL Server 取前几条
SQL Server 取前 10 条常用 TOP:
1 | SELECT TOP (10) id, name |
注意 TOP 是放在 SELECT 后面,不是 SQL 最后。
SQL Server 分页
SQL Server 2012 以后可以用 OFFSET FETCH。
1 | SELECT id, name |
这个比较像 MySQL 的:
1 | LIMIT 20, 10 |
但 SQL Server 这里有个很重要的点:分页必须要有稳定的 ORDER BY。
不要写这种:
1 | SELECT id, name |
会直接不对。
实际改写例子
原 MySQL:
1 | SELECT id, name, code, status |
SQL Server:
1 | SELECT id, name, code, status |
第二页:
1 | SELECT id, name, code, status |
没有排序字段怎么办
有些列表原来 MySQL 写得比较随意,没有明显排序。SQL Server 分页时必须补一个排序字段。
一般优先用主键:
1 | ORDER BY id DESC |
如果业务要求按创建时间:
1 | ORDER BY openedDate DESC, id DESC |
这里最好带上 id 做二级排序,不然创建时间相同的时候,翻页可能出现数据顺序飘。
性能问题
OFFSET 很大时性能会变差。比如:
1 | OFFSET 100000 ROWS FETCH NEXT 20 ROWS ONLY |
这种要看执行计划,至少排序字段上要有合适索引。
例如列表常按 deleted + id 查:
1 | CREATE NONCLUSTERED INDEX IX_zt_project_deleted_id |
不是所有表都这么加,这里只是说明思路。要看真实查询条件。
当时记下来的坑
LIMIT不能直接搬到 SQL Server。OFFSET FETCH必须配ORDER BY。- 排序字段要稳定,最好带主键兜底。
- 大页码分页要看索引,不然越翻越慢。
- SQL Server 的对象名最好带 schema,比如
dbo.zt_project。
参考
- SELECT - ORDER BY / OFFSET FETCH:https://learn.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql
- TOP 文档:https://learn.microsoft.com/en-us/sql/t-sql/queries/top-transact-sql
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 无尽infinite!

