我正在处理一个需要根据用户输入进行过滤,排序和分页的查询。现在我正在测试一个非常慢的案例,在检查查询计划时,“排序”占96%的时间。
数据模型真的不那么复杂,以下查询应该足够清楚,以了解发生了什么:
WITH OrderedRecords AS (
SELECT
A.Id
, A.col2
, ...
, B.Id
, B.col1
, ROW_NUMBER() OVER (ORDER BY B.col1 ASC) AS RowNumber
FROM A
LEFT JOIN B ON (B.SomeThing IS NULL) AND (A.BId = B.Id)
WHERE (A.col2 IN (...)) AND (B.Id IN (...))
)
SELECT
*
FROM OrderedRecords WHERE RowNumber Between x AND y
A是一个包含大约100k记录的表,但在该字段中将增长到数千万,而B是具有5个项目的类别类型表(并且这将永远不会增长任何更大,然后可能更多)。 A.Id和B.Id上有聚簇索引。
性能非常糟糕,我想知道是否有可能以某种方式解决这个问题。例如,如果订单是A.Id而不是B.col1,那么一切都非常快。也许我可以优化B.col1是某种索引。
我已经尝试在字段本身上添加索引,但这没有帮助。可能是因为表B中不同项目的数量非常小(本身与A相比)。
有什么想法吗?
答案 0 :(得分:1)
我认为这可能是问题的一部分:
LEFT JOIN B ON (B.SomeThing IS NULL) AND (A.Id = B.Id)
WHERE (A.col2 IN (...)) AND (B.Id IN (...)
你的LEFT JOIN逻辑上就像INNER JOIN一样,因为你有WHERE子句,因为只返回某些B.ID行。如果这是你的意图,那么继续使用内连接,这可能有助于优化器意识到你正在寻找有限数量的行。
答案 1 :(得分:0)
我建议你尝试一下。
对于B
表创建索引:
create index IX_B_1 on B (col1, Id, SomeThing)
对于A
表创建索引:
create index IX_A_1 on A (col2, BId) include (Id, ...)
在include
中填写表A
的所有其他列,SELECT
CTE中列出的所有列。
但是,如您所见,索引OrderedRecords
是占用空间的,并且可以获取关于表数据本身的大小。
因此,作为替代方案,您可以尝试从索引的IX_A_1
部分省略额外的列:
include
但在这种情况下,您将需要略微修改您的查询:
create index IX_A_2 on A (col2, BId) include (Id)