L'indice cerca molto più lentamente con la condizione OR rispetto ai SELECT separati


8

Sulla base di queste domande e delle risposte fornite:

SQL 2008 Server: la perdita di prestazioni potrebbe essere collegata a una tabella molto grande

La tabella di grandi dimensioni con dati storici alloca troppo SQL Server 2008 Std. memoria - perdita di prestazioni per altri database

Ho una tabella in un database SupervisionP definita in questo modo:

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]

Contiene circa 211 milioni di file.

Corro la seguente dichiarazione:

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;

Il risultato è mostrato qui:

Progetto esecutivo

Il terzo SELECT carica anche molti più dati nella cache di memoria di SQL Server.

Perché il terzo SELECT è molto più lento (8,5 s) dei primi due SELECT (16 ms)? Come posso migliorare le prestazioni della terza selezione con OR? Voglio correre seguendo il comando SQL ma mi sembra che la creazione del cursore e l'esecuzione di query separate sia molto più veloce di una singola selezione in questo caso.

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

MODIFICARE

Come ha suggerito David, ho sorvolato la grassa freccia:

FatArrow

Risposte:


11

Per le prime due query tutto ciò che deve fare è scansionare nell'indice cluster alla prima voce per quel valore di IDUkazatel- a causa dell'ordine dell'indice quella riga sarà il valore più basso per cas per quel valore di IDUkazatel.

Nella seconda query questa ottimizzazione non ha valore e probabilmente sta cercando la prima riga per IDUkazatel=24poi scansionare l'indice fino all'ultima riga con IDUkazatel=25per trovare il valore minimo di castutte quelle righe.

Se passi con il mouse su quella freccia grassa vedrai che sta leggendo molte righe (sicuramente tutte quelle per 24, probabilmente anche quelle per 25), mentre le frecce sottili nell'output del piano per le altre due mostrano l' topazione che la causa solo considera una riga.

Puoi provare a eseguire ogni query e quindi ottenere il minimo per i minimi trovati:

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

Detto questo, sembra che tu abbia una tabella con IDUkazatelvalori piuttosto che una ORclausola esplicita . Il codice seguente funzionerà con quella disposizione, è sufficiente sostituire il nome della tabella @Tcon il nome della tabella contenente i IDUkazatelvalori:

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;

In un mondo ideale, Query Optimizer di SQL Server eseguirà questa riscrittura per te, ma oggi non considera sempre questa opzione.


Puoi riscrivere l'ultimo senza tabella derivata SELECT TOP (1) min_cas=MIN(CAS) ... ORDER BY min_cas;(ma immagino che il piano sarà lo stesso del tuo.)
ypercubeᵀᴹ
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.