我们在Azure数据库(高级层)上有大约35M行的Fact表,此表启用了集群列存储索引,以提高查询性能。
我们使用类似的下面代码在Fact表上进行了分页(对Elastic Search进行索引):
SELECT *
FROM [SPENDBY].[FactInvoiceDetail]
ORder by id
offset 1000000 rows fetch next 1000 rows only
但是这个查询执行速度很慢,甚至超过10分钟,它还没有完成。如果我们更改为使用TOP
,它的效果非常好,大约需要30秒:
SELECT TOP 1000 *
FROM [SPENDBY].[FactInvoiceDetail]
WHERE ID > 1000000
ORDER BY Id
偏移量获取查询的估计执行计划:
我不确定我是否理解offset-fetch
查询是否在群集列存储索引上执行得非常糟糕。
此表在外键上有很多非集群B树索引,在Fact表的Id
上有一个唯一索引,以提高性能
此偏移量获取查询的执行计划:
答案 0 :(得分:4)
这里有一些问题。
1) Ordering BTree index is not a covering index for the paging query.
2) The rows must be reconstructed from the CCI.
3) The offset is large.
分页查询需要在排序列上使用BTree索引来计算应返回哪些行,如果该BTree索引不包含所有请求的列,则每行需要进行行查找。这是"嵌套循环"查询计划中的运算符。
但是行存储在CCI中,这意味着每列都在一个单独的数据结构中,并且对于每一行,读取单个行需要每列一个逻辑IO。这就是为什么这个查询特别昂贵的原因。为什么CCI是分页查询的不良选择。排序列上的聚簇索引,或包含剩余请求列的订购列上的非聚集索引会更好。
这里的次要和较小的问题是大偏移量。 SQL必须跳过偏移行,计算它们。因此,这将读取BTree叶级别页面的前N页以跳过行。
答案 1 :(得分:2)
这句话:
SELECT TOP 1000 *
FROM [SPENDBY].[FactInvoiceDetail]
WHERE ID > 1000000
ORDER BY Id
完全适用于ID为>的(群集?)ID字段索引(是主键?)准备好1000000
另一个语句对将满足偏移量1000000行的ID值进行排序和搜索
偏移量1000000行不等于WHERE ID>优化器为1000000,除非ID值没有间隙。
答案 2 :(得分:1)
这里的主要问题是OFFSET值很大..
偏移1000000行仅获取下一个1000行
SELECT orderid, orderdate, custid, filler
FROM dbo.Orders
ORDER BY orderdate DESC, orderid DESC
OFFSET 50 ROWS FETCH NEXT 10 ROWS ONLY;
我按列排序,因为列中的键列和列都包括在内。这导致以下计划..
这里要注意的关键点是SQLServer最终读取Offset + fetch(50 + 10)行然后最后过滤10行
因此,使用大的偏移量,即使使用合适的索引,也将以1000000 + 1000行读取结束,这是非常巨大的
如果您可以询问,sql server会在扫描后立即过滤掉1000行,这可以帮助您进行查询..通过重写您的查询,可以可能(未针对您的架构进行测试)
WITH CLKeys AS
(
SELECT ID
FROM yourtable
ORDER BY ID desc
OFFSET 500000 ROWS FETCH FIRST 10 ROWS ONLY
)
SELECT K.*, O.rest of columns
FROM CLKeys AS K
CROSS APPLY (SELECT columns needed other than id
FROM yourtable AS A
WHERE A.id= K.id) AS O
ORDER BY Id desc;
<强>参考文献:强>
http://sqlmag.com/t-sql/offsetfetch-part-1#comment-25061