Jak odfiltrować wykorzystanie funkcji skalarnej wartości zdefiniowanej przez użytkownika z danych audytu programu SQL Server?

12

Mamy bazę danych SQL Server, która ma specyfikację kontroli bazy danych, która kontroluje wszystkie działania wykonywane w bazie danych.

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

Odkryliśmy, że niektóre zapytania zapisują w dzienniku kontroli użycie funkcji skalarnej dla każdego wiersza w zestawie wyników. Kiedy tak się dzieje, dziennik zapełnia się, zanim będziemy mogli go ETL umieścić w ostatecznym miejscu spoczynku i mamy luki w logowaniu.

Niestety z powodów zgodności nie możemy po prostu przestać sprawdzać każdego EXECUTEoświadczenia.

Naszym pierwszym podejściem do rozwiązania tego problemu jest użycie WHEREklauzuli dotyczącej Audytu serwera w celu odfiltrowania działania. Kod wyglądał następująco:

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

Niestety, SQL Server nie zezwala na relacyjny operator IN (prawdopodobnie dlatego, że nie chce zapytać za każdym razem, gdy musi zapisywać w dzienniku kontroli).

Chcielibyśmy uniknąć zapisywania przechowywanego proc, który koduje na stałe object_idw WHEREklauzuli, ale takie jest nasze obecne podejście do najlepszego rozwiązania tego problemu. Czy istnieje alternatywne podejście, które powinniśmy rozważyć?

Zauważyliśmy, że gdy funkcja skalarna jest używana w rekurencyjnym CTE, powoduje to, że zapytanie zapisuje się w dzienniku kontroli dla każdego wiersza w zestawie wyników.

Niektóre funkcje o wartościach skalarnych są dostarczane przez dostawcę, których nie możemy usunąć ani przenieść do alternatywnej bazy danych.

Mark Iannucci
źródło
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.- To jeden z najwspanialszych efektów ubocznych skalarnych UDF, jakie kiedykolwiek słyszałem i dużo słyszałem.
Erik Darling
3
Czy istnieje opcja tworzenia UDF, których nie chcesz kontrolować w osobnej bazie danych (która nie jest kontrolowana) i wywoływania ich za pomocą 3-częściowej nazwy?
Scott Hodgin
@ ScottHodgin, podoba mi się to obejście, ale w naszych okolicznościach istnieją pewne Wartościowe Funkcje Skalarne, które są dostarczane przez dostawcę, których nie możemy usunąć ani przenieść do alternatywnej bazy danych.
Mark Iannucci
Osoby podążające za tym mogą zastanawiać się, co powoduje, że zapytanie zapisuje w dzienniku kontroli dla każdego wiersza w zestawie wyników; zauważyliśmy, że ma to miejsce, gdy funkcja skalarna jest używana w rekurencyjnym CTE.
Mark Iannucci

Odpowiedzi:

6

Jest kilka opcji, które udało mi się uruchomić. Wszystkie opcje dotyczą odmian predykatów filtrów. UWAGA: należy wyłączyć Audyt serwera, aby wprowadzić zmiany, a następnie włączyć go ponownie .

Po pierwsze, najbardziej ogólnym podejściem jest odfiltrowanie wszystkich skalarnych UDF. Możesz to zrobić za pomocą class_typepola audytu. Dokumentacja wskazuje, że to pole jest VARCHAR(2), ale nie pozwala na określenie ciągu. Dostałem jednak następujące rzeczy do pracy:

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

(więcej informacji na temat tego dochodzenia tutaj: Tajemnica audytu serwera: Filtrowanie typu_klasa powoduje błąd Msg 25713 )

Następne najbardziej ogólne podejście nie wchodzi w grę, ponieważ stwierdzono, że jest to baza danych dostarczona przez dostawcę i dlatego nie można wprowadzać żadnych zmian. Opiszę to ostatnie.

Najmniej ogólne podejście (ale takie, które zdecydowanie działa) to odfiltrowanie konkretnej nazwy funkcji:

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

Lub, jeśli wiele nazw:

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

Chociaż nie jest to bardzo ogólne, podejście to powinno być dobre, ponieważ liczba funkcji do odfiltrowania powinna być dość mała i nie będzie zbyt często, gdy wprowadzane są nowe funkcje.

Wreszcie, dla innych osób, które zmagają się z tą sytuacją i nie są ograniczone do dokonywania zmian: możesz umieścić funkcje we własnym schemacie, a następnie odfiltrować tylko ten schemat. Jest to bardziej ogólne niż oddzielne filtrowanie funkcji. Zakładając, że utworzysz Schemat o nazwie fni umieścisz w nim funkcje:

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

RÓWNIEŻ, w odniesieniu do następujących dwóch komentarzy w pytaniu:

Niestety, SQL Server nie zezwala na relacyjny operator IN (prawdopodobnie dlatego, że nie chce zapytać za każdym razem, gdy musi zapisywać w dzienniku kontroli).

i:

Chcielibyśmy uniknąć zapisywania przechowywanego proc, który na stałe koduje object_id w klauzuli WHERE

INOperator nie jest problemem. To prawda, że ​​nie jest obsługiwane, ale jest tylko skrótem do listy ORwarunków. Rzeczywistym problemem jest użycie T-SQL. Dozwolone są tylko literały - ciągi lub liczby - Więc i tak nie byłbyś w stanie wykonać Procedury składowanej. Nie można także korzystać z wbudowanych funkcji.

Solomon Rutzky
źródło
dziękuję za tę odpowiedź. Jesteśmy w trakcie wdrażania tej zmiany i zaakceptuję tę odpowiedź, gdy potwierdzimy, że działa ona w naszym środowisku.
Mark Iannucci
1
@MarkIannucci Thanks! Ponadto poprawiłem drobny błąd w mojej idealnej sugestii. Skopiowałem i wkleiłem z testów, w których filtrowałem funkcje FOR zamiast funkcji WSZYSTKO, ALE. Zmieniłem, =aby być <>w mojej odpowiedzi. Właśnie go przetestowałem i działa zgodnie z reklamą :-)
Solomon Rutzky