Esiste un modo per impedire alle UDF scalari nelle colonne calcolate di inibire il parallelismo?


29

Molto è stato scritto sui pericoli degli UDF scalari in SQL Server. Una ricerca casuale restituirà una gran quantità di risultati.

Ci sono alcuni punti in cui un UDF scalare è l'unica opzione, però.

Ad esempio: quando si ha a che fare con XML: XQuery non può essere usato come definizione di colonna calcolata. Un'opzione documentata da Microsoft è l'uso di un UDF scalare per incapsulare il tuo XQuery in un UDF scalare e quindi utilizzarlo in una colonna calcolata.

Ciò ha vari effetti e alcune soluzioni alternative.

  • Esegue riga per riga quando viene eseguita una query sulla tabella
  • Forza l'esecuzione di tutte le query sul tavolo

È possibile aggirare l'esecuzione riga per riga progettando schematicamente la funzione e persistendo nella colonna calcolata o indicizzandola. Nessuno di questi metodi può impedire la serializzazione forzata di query che colpiscono la tabella, anche quando l'UDF scalare non è referenziato.

C'è un modo noto per farlo?

Risposte:


31

se:

  • stanno eseguendo SQL Server 2014 o versioni successive; e
  • sono in grado di eseguire la query con il flag di traccia 176 attivo; e
  • la colonna calcolata è PERSISTED

In particolare, sono richieste almeno le seguenti versioni :

  • Aggiornamento cumulativo 2 per SQL Server 2016 SP1
  • Aggiornamento cumulativo 4 per SQL Server 2016 RTM
  • Aggiornamento cumulativo 6 per SQL Server 2014 SP2

MA per evitare un bug (riferimento per il 2014 e per il 2016 e il 2017 ) introdotto in tali correzioni, applicare invece:

Il flag di traccia è efficace come –Topzione di avvio , sia a livello globale che di sessione utilizzando DBCC TRACEON, e per query con OPTION (QUERYTRACEON)o una guida di piano.

Il flag di traccia 176 impedisce l'espansione della colonna calcolata persistente.

Il caricamento iniziale dei metadati eseguito durante la compilazione di una query porta in tutte le colonne, non solo quelle a cui viene fatto direttamente riferimento. Questo rende tutte le definizioni di colonne calcolate disponibili per la corrispondenza, il che è generalmente una buona cosa.

Come sfortunato effetto collaterale, se una delle colonne caricate (calcolate) utilizza una funzione scalare definita dall'utente, la sua presenza disabilita il parallelismo per l'intera query, anche quando la colonna calcolata non viene effettivamente utilizzata .

Il flag di traccia 176 aiuta in questo, se la colonna è persistente, non caricando la definizione (poiché l'espansione viene ignorata). In questo modo, una funzione scalare definita dall'utente non è mai presente nell'albero delle query di compilazione, quindi il parallelismo non è disabilitato.

Lo svantaggio principale del flag di traccia 176 (oltre ad essere solo leggermente documentato) è che impedisce anche la corrispondenza dell'espressione di query con colonne calcolate persistenti: se la query contiene un'espressione corrispondente a una colonna calcolata persistente, il flag di traccia 176 impedirà che l'espressione venga sostituita da un riferimento alla colonna calcolata.

Per ulteriori dettagli, consultare il mio articolo su SQLPerformance.com, colonne calcolate correttamente persistenti .

Dato che la domanda menziona XML, in alternativa alla promozione di valori usando una colonna calcolata e una funzione scalare, potresti anche usare un indice XML selettivo, come hai scritto in Indici XML selettivi: niente male .


10

Oltre all'eccellente # 1 di @ Paul , in realtà esiste un # 2 che:

  • funziona fino a SQL Server 2005,
  • non richiede l'impostazione di un flag di traccia,
  • non non richiede che la colonna calcolata sia PERSISTED, e
  • (a causa della mancata richiesta del flag di traccia 176), non impedisce la corrispondenza dell'espressione di query alle colonne calcolate persistenti

Gli unici inconvenienti (per quanto ne so) sono:

  • non funziona nel database SQL di Azure (almeno non ancora, sebbene funzioni su Amazon RDS SQL Server e SQL Server su Linux) e
  • è un po 'fuori dalla zona di comfort di molti DBA

E questa opzione è: SQLCLR

Giusto. Un aspetto interessante degli UDF scalari SQLCLR è che, se non fanno alcun accesso ai dati (né utente né sistema), non vietano il parallelismo. E questa non è solo teoria o marketing. Anche se non ho tempo (al momento) per scrivere un articolo completamente dettagliato, ho provato e dimostrato questo.

Ho usato la configurazione iniziale dal seguente post sul blog (speriamo che l'OP non lo consideri una fonte inaffidabile 🙃):

Bad Idea Jeans: Suggerimenti su più indici

Ed eseguito i seguenti test:

  1. Ha eseguito la query iniziale così com'è ─⇾ Parallelismo (come previsto)
  2. Aggiunta una colonna calcolata non persistente definita come ([c2] * [c3])─⇾ parallelismo (come previsto)
  3. Rimossa quella colonna calcolata e aggiunta una colonna calcolata non persistente che faceva riferimento a un UDF scalare T-SQL (creato con SCHEMABINDING) definito come RETURN (@First * @Second);─⇾ NO parallelismo (come previsto)
  4. Rimossa la colonna calcolata T-SQL UDF e aggiunta una colonna calcolata non persistente che faceva riferimento a una UDF scalare SQLCLR (provata con entrambe IsDeterministic = truee = false) definita come return SqlInt32.Multiply(First, Second);─⇾ parallelismo (woo hoo !!)

Quindi, sebbene SQLCLR non funzioni per tutti, ha certamente i suoi vantaggi per quelle persone / situazioni / ambienti che si adattano bene. E, dato che si riferisce a questa domanda specifica - l'esempio dato riguarda l'uso di XQuery - avrebbe sicuramente funzionato per questo (e, a seconda di ciò che viene specificamente fatto, potrebbe anche essere un po 'più veloce 😎).

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.