Voj*_*nal 8 performance sql-server-2008 sql-server execution-plan query-performance
基于这些问题和给出的答案:
SQL 2008 Server - 性能损失可能与非常大的表有关
包含历史数据的大表分配了过多的 SQL Server 2008 Std。内存 - 其他数据库的性能损失
我在数据库 SupervisionP 中有一个表,定义如下:
CREATE TABLE [dbo].[PenData](
[IDUkazatel] [smallint] NOT NULL,
[Cas] [datetime2](0) NOT NULL,
[Hodnota] [real] NULL,
[HodnotaMax] [real] NULL,
[HodnotaMin] [real] NULL,
CONSTRAINT [PK_Data] PRIMARY KEY CLUSTERED
(
[IDUkazatel] ASC,
[Cas] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[PenData] WITH NOCHECK ADD CONSTRAINT [FK_Data_Ukazatel] FOREIGN KEY([IDUkazatel])
REFERENCES [dbo].[Ukazatel] ([IDUkazatel])
ALTER TABLE [dbo].[PenData] CHECK CONSTRAINT [FK_Data_Ukazatel]
Run Code Online (Sandbox Code Playgroud)
它包含 cca 2.11 亿行。
我运行以下语句:
DECLARE @t1 DATETIME;
DECLARE @t2 DATETIME;
SET @t1 = GETDATE();
SELECT min(cas) from PenData p WHERE IDUkazatel=24
SELECT min(cas) from PenData p WHERE IDUkazatel=25
SET @t2 = GETDATE();
SELECT DATEDIFF(millisecond,@t1,@t2) AS elapsed_ms;
SET @t1 = GETDATE();
SELECT min(cas) from PenData p WHERE IDUkazatel=24 OR IDUkazatel=25
SET @t2 = GETDATE();
SELECT DATEDIFF(millisecond,@t1,@t2) AS elapsed_ms;
Run Code Online (Sandbox Code Playgroud)
结果如下所示:
第三个 SELECT 还将更多数据加载到 SQL Server 内存缓存中。
为什么第三个 SELECT(8.5 秒)比前两个 SELECT(16 毫秒)慢得多?如何使用 OR 提高第三个选择的性能?我想运行以下 SQL 命令,但在我看来,在这种情况下,创建游标和运行单独的查询比单个选择快得多。
SELECT MIN(cas) from PenData p WHERE IDUkazatel IN (SELECT IDUkazatel FROM ...)
Run Code Online (Sandbox Code Playgroud)
编辑
正如大卫所建议的那样,我将鼠标悬停在粗箭头上:
Dav*_*ett 11
对于前两个查询,它所要做的就是在聚集索引中扫描到该值的第一个条目IDUkazatel
- 因为该行的索引顺序将是该值的 cas 的最低值IDUkazatel
。
在第二个查询中,此优化不是值,它可能会寻找第一行,IDUkazatel=24
然后向下扫描索引直到最后一行,IDUkazatel=25
以找到cas
所有这些行的最小值。
如果您将鼠标悬停在那个粗箭头上,您会看到它正在读取许多行(当然是 24 行的所有行,也可能是 25 行的所有行),而其他两个计划输出中的细箭头显示top
导致它仅考虑一排。
您可以尝试运行每个查询,然后获取找到的最小值的最小值:
SELECT MIN(cas)
FROM (
SELECT cas=MIN(cas) FROM PenData p WHERE p.IDUkazatel = 24
UNION ALL
SELECT cas=MIN(cas) FROM PenData p WHERE p.IDUkazatel = 25
) AS minimums
Run Code Online (Sandbox Code Playgroud)
也就是说,您似乎有一个带有IDUkazatel
值的表,而不是一个显式的OR
子句。下面的代码将适用于这种安排,只需将表名替换为@T
包含IDUkazatel
值的表名:
SELECT
MinCas = MIN(CA.PartialMinimum)
FROM @T AS T
CROSS APPLY
(
SELECT
PartialMinimum = MIN(PD.Cas)
FROM dbo.PenData AS PD
WHERE
PD.IDUkazatel = T.IDUkazatel
) AS CA;
Run Code Online (Sandbox Code Playgroud)
在理想情况下,SQL Server 查询优化器会为您执行这种重写,但今天它并不总是考虑这个选项。