Sql-Server

與單獨的 SELECT 相比,使用 OR 條件的索引查找要慢得多

  • July 30, 2014

基於這些問題和給出的答案:

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]

它包含 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;

結果顯示在這裡:

執行計劃

第三個 SELECT 還將更多數據載入到 SQL Server 記憶體記憶體中。

為什麼第三個 SELECT 比前兩個 SELECT(16 毫秒)慢得多(8.5 秒)?如何使用 OR 提高第三個選擇的性能?我想執行以下 SQL 命令,但在我看來,在這種情況下,創建游標和執行單獨的查詢比單個選擇要快得多。

SELECT MIN(cas) from PenData p WHERE IDUkazatel IN (SELECT IDUkazatel FROM  ...)

編輯

正如大衛建議的那樣,我將滑鼠懸停在粗箭頭上:

肥箭

對於前兩個查詢,它所要做的就是在聚集索引中掃描到該值的第一個條目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

也就是說,您似乎有一個包含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;

在理想情況下,SQL Server 查詢優化器會為您執行此重寫,但現在並不總是考慮此選項。

引用自:https://dba.stackexchange.com/questions/72726