2010-03-31 13 views
166

Alcuni esempi per mostrare, solo in caso:Multi-TABLE funzione valutata vs Inline Table funzione valutata

Inline valori di tabella

CREATE FUNCTION MyNS.GetUnshippedOrders() 
RETURNS TABLE 
AS 
RETURN SELECT a.SaleId, a.CustomerID, b.Qty 
    FROM Sales.Sales a INNER JOIN Sales.SaleDetail b 
     ON a.SaleId = b.SaleId 
     INNER JOIN Production.Product c ON b.ProductID = c.ProductID 
    WHERE a.ShipDate IS NULL 
GO 

Multi Table Dichiarazione Valued

CREATE FUNCTION MyNS.GetLastShipped(@CustomerID INT) 
RETURNS @CustomerOrder TABLE 
(SaleOrderID INT   NOT NULL, 
CustomerID  INT   NOT NULL, 
OrderDate  DATETIME NOT NULL, 
OrderQty  INT   NOT NULL) 
AS 
BEGIN 
    DECLARE @MaxDate DATETIME 

    SELECT @MaxDate = MAX(OrderDate) 
    FROM Sales.SalesOrderHeader 
    WHERE CustomerID = @CustomerID 

    INSERT @CustomerOrder 
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty 
    FROM Sales.SalesOrderHeader a INNER JOIN Sales.SalesOrderHeader b 
     ON a.SalesOrderID = b.SalesOrderID 
     INNER JOIN Production.Product c ON b.ProductID = c.ProductID 
    WHERE a.OrderDate = @MaxDate 
     AND a.CustomerID = @CustomerID 
    RETURN 
END 
GO 

C'è un vantaggio nell'usare un tipo (in-line o multi statement) sull'altro r? Esistono alcuni scenari in cui uno è migliore dell'altro o le differenze sono puramente sintattiche? Mi rendo conto che le due query di esempio stanno facendo cose diverse ma c'è un motivo per scriverle in quel modo?

Leggere su di loro e i vantaggi/le differenze non sono stati realmente spiegati.

+0

anche uno dei più enorme vantaggio della funzione inline è che si può selezionare ROWID colonne (timestamp), mentre non è possibile inserire i dati TIMESTAMP alla tabella ritorno funzione multistato! – Artru

+3

Grazie per un filo eccellente. Ho imparato molto Tuttavia, una cosa da tenere a mente è quando ALTERe una funzione che è stata ITV su MSTV, il profiler pensa che tu stia modificando un ITV. Qualunque cosa tu faccia per ottenere la sintassi direttamente da un punto di vista MSTV, la ricompilazione fallisce sempre, in genere attorno alla prima istruzione dopo BEGIN. L'unico modo per aggirare questo era DROP la vecchia funzione e CREARE il nuovo come MSTV. – Fandango68

risposta

121

Nella ricerca del commento di Matt, ho rivisto la mia dichiarazione originale. È corretto, ci sarà una differenza di prestazioni tra una funzione di stima della tabella inline (ITVF) e una funzione di stima di tabelle con più istruzioni (MSTVF) anche se entrambi eseguono semplicemente un'istruzione SELECT. SQL Server tratterà un ITVF in qualche modo come un VIEW in quanto calcolerà un piano di esecuzione utilizzando le ultime statistiche sulle tabelle in questione. Un MSTVF equivale a riempire l'intero contenuto dell'istruzione SELECT in una variabile di tabella e quindi a unirsi a esso. Pertanto, il compilatore non può utilizzare alcuna statistica di tabella sulle tabelle nel MSTVF. Quindi, a parità di condizioni (che raramente sono), l'ITVF funzionerà meglio del MSTVF. Nei miei test, la differenza di prestazioni nei tempi di completamento era trascurabile, tuttavia dal punto di vista delle statistiche, era evidente.

Nel tuo caso, le due funzioni non sono equivalenti dal punto di vista funzionale. La funzione MSTV esegue una query aggiuntiva ogni volta che viene chiamata e, soprattutto, filtri sull'ID cliente. In una query di grandi dimensioni, l'ottimizzatore non sarebbe in grado di sfruttare gli altri tipi di join in quanto sarebbe necessario chiamare la funzione per ogni ID cliente passato.Tuttavia, se si ri-scritto la funzione MSTV in questo modo:

CREATE FUNCTION MyNS.GetLastShipped() 
RETURNS @CustomerOrder TABLE 
    (
    SaleOrderID INT   NOT NULL, 
    CustomerID  INT   NOT NULL, 
    OrderDate  DATETIME NOT NULL, 
    OrderQty  INT   NOT NULL 
    ) 
AS 
BEGIN 
    INSERT @CustomerOrder 
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty 
    FROM Sales.SalesOrderHeader a 
     INNER JOIN Sales.SalesOrderHeader b 
      ON a.SalesOrderID = b.SalesOrderID 
     INNER JOIN Production.Product c 
      ON b.ProductID = c.ProductID 
    WHERE a.OrderDate = (
         Select Max(SH1.OrderDate) 
         FROM Sales.SalesOrderHeader As SH1 
         WHERE SH1.CustomerID = A.CustomerId 
         ) 
    RETURN 
END 
GO 

In una query, l'ottimizzatore sarebbe in grado di chiamare tale funzione una sola volta e costruire una migliore piano di esecuzione ma ancora non sarebbe meglio di un equivalente , ITVS non parametrizzato o VIEW.

Gli ITVF devono essere preferiti su un MSTVF quando fattibile perché i tipi di dati, il nullable e le regole di confronto dalle colonne nella tabella mentre si dichiarano tali proprietà in una funzione di tabella con più istruzioni e, soprattutto, si otterranno piani di esecuzione migliori da l'ITVF. Nella mia esperienza, non ho trovato molte circostanze in cui un ITVF era un'opzione migliore di una VISTA, ma il chilometraggio può variare.

Grazie a Matt.

aggiunta

Da quando ho visto questo venire di recente, qui è un'analisi eccellente fatto da Wayne Sheffield confrontando la differenza di prestazioni tra Inline Tabella funzioni valutate e funzioni multi-dichiarazione.

His original blog post.

Copy on SQL Server Central

+37

Questo semplicemente non è vero - Le funzioni multi-statement sono molto spesso un enorme impatto sulle prestazioni perché impediscono a Query Optimiser di utilizzare le statistiche. Se avessi $ 1 per ogni volta che ho visto che l'uso di funzioni multi-statement causava una scelta molto scarsa del piano di esecuzione (soprattutto perché di solito stima il numero di righe restituite come 1), avrei abbastanza per comprare una piccola macchina. –

+0

La spiegazione migliore che abbia mai trovato è la prima risposta e il post correlato: http://stackoverflow.com/questions/4109152/table-valued-function-killing-my-query-performance Da non perdere il documento correlato, puoi leggerlo rapidamente ed è estremamente interessante. – JotaBe

+0

Sarà disponibile un aggiornamento per questa risposta per SQL Server 2017? Https://www.youtube.com/watch?time_continue=2&v=szTmo6rTUjM – Ralph

3

I vostri esempi, penso, rispondono molto bene alla domanda. La prima funzione può essere eseguita come una singola selezione ed è una buona ragione per utilizzare lo stile in linea. Il secondo potrebbe probabilmente essere fatto come una singola istruzione (usando una sotto-query per ottenere la data massima), ma alcuni programmatori potrebbero trovare più facile leggere o più naturale farlo in più affermazioni come hai fatto tu. Alcune funzioni semplicemente non possono essere eseguite in un'unica istruzione, e quindi richiedono la versione multi-statement.

Suggerisco di utilizzare il più semplice (in linea) quando possibile, e di utilizzare le multi-dichiarazioni quando necessario (ovviamente) o quando la preferenza personale/leggibilità rende possibile la digitazione aggiuntiva.

+0

Grazie per la risposta. Quindi, in pratica, la multiistruzione è solo da usare quando la funzione è più complicata di quanto sia fattibile in una funzione inline, per motivi di leggibilità? Ci sono dei vantaggi in termini di prestazioni a livello di multi-statement? – AndrewC

+0

Non lo so, ma non lo penserei. Probabilmente è meglio consentire al server SQL di individuare le ottimizzazioni che potresti provare a fare manualmente (usando variabili, tabelle temporanee o altro). Sebbene tu possa certamente fare alcuni test delle prestazioni per dimostrare/confutare questo in casi specifici. – Ray

+0

Molte grazie ancora. Potrei approfondire questo aspetto quando avrò più tempo! :) – AndrewC

-2

se avete intenzione di fare una query è possibile unire nella vostra funzione con valori in linea Tabella come:

SELECT 
    a.*,b.* 
    FROM AAAA a 
     INNER JOIN MyNS.GetUnshippedOrders() b ON a.z=b.z 

si comporterà modesti in testa e funzionare bene.

se si tenta di utilizzare la tabella di Bilancio Multi valutato in una query simile, si avrà problemi di prestazioni:

SELECT 
    x.a,x.b,x.c,(SELECT OrderQty FROM MyNS.GetLastShipped(x.CustomerID)) AS Qty 
    FROM xxxx x 

perché si eseguirà il tempo funzione di 1 per ogni riga restituita, come il set di risultati diventa grande, verrà eseguito più lentamente e più lentamente.

+0

Ah, quindi diresti che la linea è molto migliore in termini di prestazioni? – AndrewC

+1

No, entrambi restituiscono una tabella, il che rende invalido il secondo SQL mentre si tenta di inserire una tabella in una colonna. – cjk

+1

@ck, ho aggiornato la query che hai commentato. i parametri della funzione utilizzata nella seconda funzione lo prestano per essere utilizzato come query secondaria, con conseguente peggioramento delle prestazioni. –

24

Internamente, SQL Server considera una funzione con valori di tabella in linea molto simile sarebbe una vista e tratta una funzione con valori di tabella con istruzioni multiple simile a come sarebbe una stored procedure.

Quando una funzione valutata inline viene utilizzata come parte di una query esterna, il processore di query espande la definizione UDF e genera un piano di esecuzione che accede agli oggetti sottostanti, utilizzando gli indici su questi oggetti.

Per una funzione con valore di tabella con più istruzioni, viene creato un piano di esecuzione per la funzione stessa e memorizzato nella cache del piano di esecuzione (una volta che la funzione è stata eseguita la prima volta). Se le funzioni con valori di tabella a più istruzioni vengono utilizzate come parte di query più ampie, l'ottimizzatore non sa cosa restituisce la funzione, e quindi fa alcune ipotesi standard - in effetti assume che la funzione restituirà una singola riga e che i ritorni di sarà possibile accedere alla funzione utilizzando una scansione della tabella su una tabella con una singola riga.

Dove le funzioni con valori di tabella a più istruzioni possono avere scarsi risultati quando restituiscono un numero elevato di righe e vengono associate contro le query esterne. I problemi di prestazioni dipendono principalmente dal fatto che l'ottimizzatore produrrà un piano assumendo che venga restituita una singola riga, che non sarà necessariamente il piano più appropriato.

Come una regola generale, abbiamo scoperto che, ove possibile, le funzioni di valore in tabella dovrebbero essere utilizzate preferibilmente rispetto a quelle multi-statement (quando l'UDF verrà utilizzato come parte di una query esterna) a causa di questi potenziali problemi di prestazioni .

+2

Sebbene possa trattare funzioni con valori di tabelle a più istruzioni simili a una stored procedure, la stored procedure identica dal punto di vista funzionale è molto più veloce di una funzione valutata a livello di tabella per dataset di grandi dimensioni. Sto attaccando con proc memorizzati su funzioni con valori di tabella multi-statement. – Kekoa

+5

A meno che non sia necessario unirsi a questi risultati in un'altra query. –

+0

perché non usare entrambi? Un processo memorizzato che restituisce il risultato di una funzione con valori di tabella con più istruzioni. Il meglio di entrambi i mondi. – Robino

11

C'è un'altra differenza. È possibile inserire, aggiornare ed eliminare una funzione con valori di tabella incorporata, proprio come una vista. Restrizioni analoghe si applicano - non è possibile aggiornare le funzioni utilizzando gli aggregati, non è possibile aggiornare le colonne calcolate e così via.

Problemi correlati