Come filtrare l'utilizzo della funzione definita dall'utente definita scalare dai dati di controllo di SQL Server?


12

Abbiamo un database SQL Server che ha una specifica di controllo del database che controlla tutte le azioni eseguite sul database.

CREATE DATABASE AUDIT SPECIFICATION [dbAudit]
FOR SERVER AUDIT [servAudit]
ADD (EXECUTE ON DATABASE::[DatabaseName] BY [public])

Abbiamo scoperto che alcune query scriveranno nel registro di controllo l'uso di una funzione scalare per ogni riga in un set di risultati. Quando ciò accade, il registro si riempie prima che possiamo ETL nel suo luogo di riposo finale e abbiamo un gap nella nostra registrazione.

Sfortunatamente per motivi di conformità, non possiamo semplicemente smettere di controllare ogni EXECUTEaffermazione.

Il nostro primo pensiero per l'approccio a questo problema è utilizzare la WHEREclausola su Server Audit per filtrare l'attività. Il codice era simile al seguente:

WHERE [object_id] not in (Select object_id from sys.objects where type = 'FN' )

Sfortunatamente, SQL Server non consente l'operatore relazionale IN (probabilmente perché non desidera eseguire query ogni volta che deve scrivere nel registro di controllo).

Vorremmo evitare di scrivere un proc memorizzato che codifica in modo duro object_idnella WHEREclausola, ma questo è il nostro pensiero attuale sul modo migliore per affrontare questo problema. Esiste un approccio alternativo che dovremmo considerare?

Abbiamo notato che quando la funzione scalare è in uso in un CTE ricorsivo, fa sì che la query scriva nel registro di controllo per ogni riga del set di risultati.

Esistono alcune funzioni con valori scalari fornite da un fornitore che non è possibile eliminare o spostare in un database alternativo.


6
We've found that some queries will write to the audit log the use of a scalar function for every row in a result set.- Questo è uno dei più magnifici effetti collaterali degli UDF scalari che io abbia mai sentito, e ho sentito molto.
Erik Darling,

3
Esiste un'opzione per creare UDF che non vuoi controllare in un database separato (che non è controllato) e invocarli tramite un nome in 3 parti?
Scott Hodgin,

@ScottHodgin, mi piace la soluzione alternativa, ma nelle nostre circostanze ci sono alcune funzioni a valore scalare che vengono fornite da un fornitore che non possiamo eliminare o spostare in un database alternativo.
Mark Iannucci,

Quelli che seguono potrebbero chiedersi quale caso fa sì che la query scriva nel registro di controllo per ogni riga in un set di risultati; abbiamo notato che si verifica quando la funzione scalare è in uso in un CTE ricorsivo.
Mark Iannucci il

Risposte:


6

Ci sono alcune opzioni che sono riuscito a far funzionare. Tutte le opzioni riguardano le variazioni dei predicati del filtro. NOTA: è necessario disattivare l'Audit Server al fine di apportare le modifiche, e poi ri- abilitare esso.

Innanzitutto, l'approccio più generico è filtrare tutti gli UDF scalari. Puoi farlo utilizzando il class_typecampo di controllo. La documentazione indica che questo campo è VARCHAR(2), ma non consente di specificare una stringa. Tuttavia, ho ottenuto quanto segue per funzionare:

ALTER SERVER AUDIT [servAudit]
WHERE ([class_type] <> 20038); -- EXECUTE Scalar UDF

(maggiori informazioni su questa indagine qui: Mistero di controllo del server: il filtro class_type ottiene il messaggio 25713 di errore )

Il prossimo approccio più generico non è un'opzione poiché è stato affermato che si tratta di un database fornito dal fornitore e quindi non è possibile apportare modifiche. Quindi lo tratterò per ultimo.

L'approccio meno generico (ma sicuramente funzionante) è filtrare il nome specifico della funzione:

ALTER SERVER AUDIT [servAudit]
WHERE ([object_name]<>'function_name');

Oppure, se più nomi:

ALTER SERVER AUDIT [servAudit]
WHERE ([object_name]<>'function_name1' AND [object_name]<>'function_name2');

Anche se non molto generico, questo approccio dovrebbe andare bene poiché il numero di funzioni da filtrare dovrebbe essere abbastanza piccolo e non sarà molto frequente l'introduzione di nuove funzioni.

Infine, per gli altri che affrontano questa situazione e non sono limitati ad apportare modifiche: è possibile inserire le funzioni nel proprio schema e quindi filtrare solo quello schema. Questo è più generico del filtraggio individuale delle funzioni. Supponendo che si crei uno schema denominato fne si inseriscano le funzioni:

ALTER SERVER AUDIT [servAudit]
WHERE ([schema_name]<>'fn');

ANCHE, per quanto riguarda i seguenti due commenti alla domanda:

Sfortunatamente, SQL Server non consente l'operatore relazionale IN (probabilmente perché non desidera eseguire query ogni volta che deve scrivere nel registro di controllo).

e:

Vorremmo evitare di scrivere un proc memorizzato che codifica in modo rigido object_id nella clausola WHERE

L' INoperatore non è il problema. È vero, non è supportato, ma è solo una scorciatoia per un elenco di ORcondizioni. Il vero problema è l'uso di T-SQL. Sono ammessi solo valori letterali - stringhe o numeri. Quindi non saresti stato in grado di eseguire una Stored procedure comunque. Né è possibile utilizzare le funzioni integrate.


grazie per questa risposta. Stiamo implementando questo cambiamento e accetterò questa risposta quando confermeremo che funziona nel nostro ambiente.
Mark Iannucci il

1
@MarkIannucci Grazie! Inoltre, ho appena corretto un bug minore nel mio suggerimento ideale. Avevo copiato e incollato dai test in cui stavo filtrando le funzioni FOR anziché le funzioni NULLA. Ho cambiato il =per essere <>nella mia risposta. L'ho appena provato e funziona come pubblicizzato :-)
Solomon Rutzky il
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.