1

我正在处理一个需要根据用户输入进行过滤、排序和分页的查询。现在我正在测试一个非常慢的案例,在检查查询计划后,“排序”需要 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 相比)。

有任何想法吗?

4

2 回答 2

1

我认为这可能是问题的一部分:

   LEFT JOIN B ON (B.SomeThing IS NULL) AND (A.Id = B.Id)
    WHERE (A.col2 IN (...)) AND (B.Id IN (...)

由于您有 WHERE 子句,您的 LEFT JOIN 在逻辑上将像 INNER JOIN 一样运行,因为只会返回某些 B.ID 行。如果这是您的意图,那么继续使用内部联接,这可能有助于优化器意识到您正在寻找有限数量的行。

于 2013-10-30T13:27:46.367 回答
0

我建议你尝试以下。

对于B表创建索引:

create index IX_B_1 on B (col1, Id, SomeThing)

对于A表创建索引:

create index IX_A_1 on A (col2, BId) include (Id, ...)

在CTE中列出的include表的所有其他列中。ASELECTOrderedRecords

但是,如您所见,索引IX_A_1占用空间,并且可以占用大约表数据本身的大小。

因此,作为替代方案,您可以尝试从include部分索引中省略额外的列:

create index IX_A_2 on A (col2, BId) include (Id)

但在这种情况下,您将不得不稍微修改您的查询:

;WITH OrderedRecords AS (
    SELECT 
        AId = A.Id
        , A.col2
        -- remove other A columns from here
        , bid = 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
    R.*, A.OtherColumns
FROM OrderedRecords R
    join A on A.Id = R.AId
WHERE R.RowNumber Between x AND y
于 2013-10-30T15:07:43.787 回答