Mic*_*elD 6 performance sql-server execution-plan execution
我们有一个表 CustomerNote,有 4 列 ID、CustomerID、Note、Date
CustomerID asc, Date desc 上有一个索引
当执行以下查询时
select top 30
Date
from CustomerNote
where CustomerID in (1,5)
order by Date desc
Run Code Online (Sandbox Code Playgroud)
使用了索引,但它仍然获取 customerID 1 和 5 的所有 CustomerNote,然后排序/置顶,导致大量 CPU 使用。
这是由于“in”子句中的多个值造成的。我知道“in”子句的值永远不会超过 10 个,因此如果 sql server 迭代 10 个,为每个 customerID 获取至少 30 个值并进行合并、排序和顶部,这将是一个更好的方法。是否有查询提示或选项可以实现此目的?
您可能最好手动编写所需的转换,但本着寻找优化器可以通过最少的更改为您做的事情的精神:
SELECT TOP (30)
CN.[Date]
FROM dbo.CustomerNote AS CN
WHERE
CN.CustomerID IN (SELECT 1 UNION SELECT 5)
ORDER BY
CN.[Date] DESC;
Run Code Online (Sandbox Code Playgroud)
是的,如果优化器能够在不改变语法的情况下为您探索这种选项,那就太好了。
CREATE TABLE dbo.CustomerNote
(
CustomerID integer NOT NULL,
[Date] datetime NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
WITH
N (n) AS
(
SELECT
SV.number
FROM master.dbo.spt_values AS SV
WHERE
SV.[type] = N'P'
AND SV.number >= 1
)
INSERT dbo.CustomerNote
WITH (TABLOCKX)
(
CustomerID,
[Date]
)
SELECT
C.n,
DATEADD
(
MINUTE,
-D.n * RAND(CHECKSUM(NEWID())) * 1000,
GETDATE()
)
FROM N AS C
CROSS JOIN N AS D
WHERE
C.n BETWEEN 1 AND 9;
CREATE NONCLUSTERED INDEX
[IX dbo.CustomerNote CustomerID, Date-]
ON dbo.CustomerNote
(CustomerID, [Date] DESC);
Run Code Online (Sandbox Code Playgroud)
这会为 10 个不同的客户加载 2047 个随机日期值:
SELECT CN.CustomerID, NumRows = COUNT_BIG(*)
FROM dbo.CustomerNote AS CN
GROUP BY CN.CustomerID
ORDER BY CN.CustomerID;
Run Code Online (Sandbox Code Playgroud)
客户ID | 行数 |
---|---|
1 | 2047 |
2 | 2047 |
3 | 2047 |
4 | 2047 |
5 | 2047 |
6 | 2047 |
7 | 2047 |
8 | 2047 |
9 | 2047 |
运行解决方案代码会生成一个执行后计划,其中从客户 1 的有序索引查找中读取14 行,从客户 5 的类似查找中读取17 行:
该计划不会读取每个客户的所有 2047 行。
另请注意,此解决方案不需要排序运算符。
尽管合并串联需要满足一些排序条件,但该解决方案非常通用。
如果您想投影不同的列,则该列需要成为索引键的一部分(而不是作为包含)以满足排序要求;或者您可以仅获取表的一个键,并在找到所需的少量键后作为单独的步骤查找额外的列。
带有额外专栏的其他演示:
VALUES
如果您加入表值参数、临时表或子句,似乎可能会更好。这意味着每个CustomerID
将在相关中单独查询APPLY
然后排序。
select top (30)
cn.Date
from (values
(1),
(5)
) v(CustomerID)
cross apply (
select top (30) cn.*
from CustomerNote cn
where v.CustomerID = cn.CustomerID
order by
cn.Date desc
) cn
order by
cn.Date desc;
Run Code Online (Sandbox Code Playgroud)