Come posso aggirare SQL Server - Variazione del piano di esecuzione della funzione valore tabella in linea in base ai parametri?

StackOverflow https://stackoverflow.com/questions/450091

Domanda

Ecco la situazione:
Ho una funzione del valore della tabella con un parametro DateTime, per non dire TDF (P_DATE), che filtra circa due milioni di righe selezionando quelle con data di colonna più piccole di P_Date e calcola alcuni valori aggregati su altre colonne.
Funziona benissimo, ma se p_date è una funzione di valore scalare personalizzata (restituendo la fine della giornata nel mio caso) il piano di esecuzione viene modificato e la query passa da 1 secondo a 1 minuto di esecuzione.

Una tabella di prova del concetto: 1.000 prodotti, 2 milioni di righe:

CREATE TABLE [dbo].[POC](
    [Date] [datetime] NOT NULL,
    [idProduct] [int] NOT NULL,
    [Quantity] [int] NOT NULL
) ON [PRIMARY]

La funzione del valore della tabella in linea:

CREATE FUNCTION tdf (@p_date datetime)
RETURNS TABLE 
AS
RETURN 
(
    SELECT idProduct, SUM(Quantity) AS TotalQuantity,
         max(Date) as LastDate
    FROM POC
    WHERE (Date < @p_date)
    GROUP BY idProduct
)

La funzione valore scalare:

CREATE FUNCTION [dbo].[EndOfDay] (@date datetime)
RETURNS datetime
AS
BEGIN
    DECLARE @res datetime
    SET @res=dateadd(second, -1,
         dateadd(day, 1, 
             dateadd(ms, -datepart(ms, @date),
                 dateadd(ss, -datepart(ss, @date),
                    dateadd(mi,- datepart(mi,@date),
                         dateadd(hh, -datepart(hh, @date), @date))))))
    RETURN @res
END

Domanda 1: funziona alla grande

SELECT * FROM [dbo].[tdf] (getdate())

Il piano di fine esecuzione:Costo aggregato flusso 13% <--- Costo scansione indice cluster 86%

Domanda 2 - Non così eccezionale

SELECT * FROM [dbo].[tdf] (dbo.EndOfDay(getdate()))

Il piano di fine esecuzione:Costo aggregato flusso 4% <--- Costo filtro 12% <--- Costo scansione indice cluster 86%

È stato utile?

Soluzione

L'overhead è la tua funzione scalare.

Il TVF qui viene espanso come una macro in linea, quindi

SELECT * FROM [dbo].[tdf] (getdate())

diventa

SELECT     idProduct, SUM(Quantity) AS TotalQuantity, max(Date) as LastDate
    FROM         POC
    WHERE     Date < getdate()
    GROUP BY idProduct

Quando si utilizza la funzione scalare di fine giornata, SQL non può valutare EOD(GETDATE()) come costante.Non riesco a trovare rapidamente il mio articolo su come SQL valuta queste cose, mi dispiace.

Immagino che venga valutato per ogni riga, non in anticipo come desideri.

Calcolerei la dichiarazione EOD separatamente:

DECLARE @eod datetime;
SET @eod = dbo.EndOfDay(getdate());
SELECT * FROM [dbo].[tdf] (@eod)

Lo userei anche per la funzione EOD:

DATEADD(second, -1, DATEADD(day, 1, (DATEDIFF(day, 0, @date))))

MODIFICARE:Altra domanda a cui ho risposto

Altri suggerimenti

Puoi riscrivere EndOfDay anche come UDF in linea e utilizzare UDF in linea nidificati. Esempi:

Molti UDF in linea nidificati sono molto veloci

Calcolo del terzo mercoledì del mese con UDF in linea

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top