Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Wyzwalacze programu SQL Server:wyzwalacze DML

W SQL Server, wyzwalacze to obiekty bazy danych, które są wykonywane za każdym razem, gdy w bazie danych lub na serwerze wystąpi zdarzenie wyzwalające.

Wyzwalacze odgrywają kluczową rolę w spełnianiu wymagań biznesowych, takich jak ostrzeganie osób docelowych, rozpoczęcie pracy lub inne operacje. Ponieważ wyzwalacze mogą obsługiwać wiele takich operacji, powinniśmy je zdefiniować ostrożnie, aby uniknąć wpływu na wydajność.

W tym artykule przyjrzymy się wyzwalaczom, rodzajom wyzwalaczy i różnym dostępnym opcjom wyzwalaczy. Ponadto omówimy niezbędne środki ostrożności podczas korzystania z wyzwalaczy DML.

Wyzwalacze w SQL

Wyzwalacz to specjalny typ procedury składowanej, która jest wykonywana po zdefiniowanych zdarzeniach, uruchamiając skrypt zdefiniowany w treści wyzwalacza. Istnieje kilka rodzajów wyzwalaczy:

  • Wyzwalacze DML – do wykonywania operacji DML, takich jak polecenia INSERT, UPDATE i DELETE na tabelach.
  • Wyzwalacze DDL – do wykonywania operacji DDL, takich jak CREATE, ALTER i DROP na dowolnych obiektach w bazie danych lub serwerze.
  • Wyzwalacze logowania – za próbę zalogowania się do instancji SQL Server podczas zdarzenia LOGON.

Wyzwalacze DML w SQL Server

Wyzwalacze DML są wyzwalane przez polecenia DML (INSERT, UPDATE lub DELETE) w tabelach lub widokach. Możemy tworzyć takie wyzwalacze w tych tabelach lub widokach tylko tam, gdzie znajdują się dane, tak aby akceptowały one na nich polecenia DML.

W zależności od czasu uruchomienia/wywołania wyzwalacze DML mogą być następujących typów:

  • DLA lub PO Typ wyzwalacza — wyzwalacz jest wywoływany po pomyślnym zakończeniu instrukcji DML w tabeli lub widoku. Uwaga:możliwe jest utworzenie wyzwalacza PO TYLKO dla tabel, a nie widoków.
  • ZAMIAST Typ wyzwalacza — wyzwalacz zostanie wywołany przed (ZAMIAST) skryptem DML wykonanym w tabeli lub widoku.

SQL Server tworzy dwie specjalne lub logiczne tabele o nazwie WSTAWIONA i ZAKTUALIZOWANE za każdym razem, gdy wyzwalacze DML są tworzone w tabelach lub widokach. Te tabele logiczne pomagają zidentyfikować zmiany rekordów, które zachodzą za pomocą operacji INSERT/UPDATE/DELETE. W ten sposób zapewnia skuteczne działanie wyzwalaczy DML.

  • WSTAWIONA tabela logiczna przechowuje kopie nowych rekordów rekordów zmodyfikowanych podczas operacji INSERT i UPDATE. Gdy nowy rekord jest dodawany do rzeczywistej tabeli, jest on również dodawany do tabeli WSTAWIONA. Podobnie, wszelkie zmiany w istniejących rekordach za pomocą instrukcji UPDATE przenoszą najnowsze wartości do tabeli INSERTED, a starsze – do tabeli logicznej DELETED.
  • USUNIĘTO tabela logiczna przechowuje kopie starszych wartości podczas operacji UPDATE i DELETE. Za każdym razem, gdy rekord jest aktualizowany, starsze wartości są kopiowane do tabeli DELETED. Za każdym razem, gdy rekord jest usuwany z rzeczywistej tabeli, rekordy są wstawiane do tabeli DELETED.

SQL Server ma wbudowane funkcje COLUMN_UPDATED() i AKTUALIZUJ() aby zidentyfikować obecność operacji INSERT lub UPDATE w określonej kolumnie.

  • COLUMN_UPDATED() zwraca wartości zmiennych z kolumn, na które miały wpływ operacje INSERT lub UPDATE.
  • AKTUALIZACJA() akceptuje nazwę kolumny jako parametr wejściowy i zwraca informację, czy kolumna zawiera jakiekolwiek zmiany danych w ramach operacji INSERT lub UPDATE.

NIE DO REPLIKACJI właściwość może być używana w wyzwalaczach DML, aby uniknąć uruchamiania ich w przypadku zmian nadchodzących w procesie replikacji.

Wyzwalacze DML można również tworzyć za pomocą .Net Framework Common Language Runtime (CLR).

System DMV sys.triggers przechowuje listę wszystkich wyzwalaczy w zakresie bazy danych. Możemy użyć poniższego zapytania, aby pobrać szczegóły wszystkich wyzwalaczy DML w bazie danych:

SELECT * 
FROM sys.triggers
WHERE type = 'TR';

Definicje wyzwalacza DML można wyświetlić, jeśli wyzwalacz nie jest zaszyfrowany. Używamy dowolnej z poniższych opcji:

sys.sql_modules

SELECT OBJECT_SCHEMA_NAME(object_id, db_id()) Schema_name, OBJECT_NAME(object_id) Trigger_Name, definition
FROM sys.sql_modules  
WHERE object_id = OBJECT_ID(<trigger_name>);   

OBJECT_DEFINITION() funkcja

SELECT OBJECT_DEFINITION (OBJECT_ID(<trigger_name>)) AS ObjectDefinition; 

sp_helptext procedura składowana

EXEC sp_helptext '<trigger_name>';

Wszystkie możliwe zdarzenia DML są dostępne w sys.events stół. Możemy je wyświetlić za pomocą poniższego zapytania:

SELECT * 
FROM sys.events;

Składnia wyzwalacza DML

CREATE TRIGGER <trigger_name>
ON <schema_name.table_name | schema_name.view_name > 
[ WITH <DML_trigger_option> [ ,...n ] ]  
{ FOR | AFTER | INSTEAD OF} <event_type>
AS { sql_statement | EXTERNAL NAME <method specifier> }  

Do celów demonstracyjnych utworzyłem dwie tabele o nazwie Sprzedaż i Historia sprzedaży z kilkoma kolumnami w testowej bazie danych:

CREATE TABLE Sales (SalesId int IDENTITY NOT NULL, SalesDate datetime, Itemcount int, price money);
CREATE TABLE SalesHistory (SalesId int NOT NULL, SalesDate datetime, Itemcount int, price money, ChangeType varchar(10), ChangeDate datetime DEFAULT GETDATE(), ChangedUser varchar(100) DEFAULT SUSER_NAME());
GO

Jak widać, Historia sprzedaży tabela ma 3 dodatkowe kolumny do śledzenia daty modyfikacji i nazwy użytkownika, który wywołał zmianę. W razie potrzeby możemy mieć jeszcze jedną kolumnę tożsamości zdefiniować i ustawić go również jako klucz podstawowy.

WSTAW wyzwalacz

Tworzymy prosty wyzwalacz INSERT w sekcji Sprzedaż tabele, aby WSTAWIĆ wszelkie nowe zmiany rekordów w Historii sprzedaży stół. Użyj poniższego skryptu:

CREATE TRIGGER TR_INS_Sales ON Sales
FOR INSERT 
AS
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    	,SalesDate
	,Itemcount
	,price
	,'INSERT'
  FROM inserted
END
GO

Aby wyjaśnić składnię wyzwalacza, utworzyliśmy wyzwalacz DML o nazwie TR_INS_Sales w sekcji Sprzedaż stół. Musi uruchomić wyzwalacz tylko dla operacji INSERT – wstawianie rekordów do Historii sprzedaży tabela z wstawionej tabeli.

Jak wiemy, wstawiono to logiczna tabela, która rejestruje zmiany zachodzące w tabeli źródłowej (Sprzedaż w naszym przypadku).

Widzimy kolejną specjalną tabelę logiczną usuniętą w wyzwalaczu UPDATE, ponieważ usunięto tabela nie dotyczy wyzwalaczy INSERT.

Dodajmy nowy rekord, aby sprawdzić, czy rekordy są wstawiane do Historii sprzedaży stół automatycznie.

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-01-01', 5, 100);

Mimo że wstawiliśmy tylko jeden wpis do Sprzedaż tabeli, otrzymujemy 2 wiersze z 1 wiersza, który ma wpływ wiadomość. Drugi rekord pojawia się z powodu operacji INSERT jako części wyzwalacza wywoływanego przez działanie INSERT w sekcji Sprzedaż tabela – wstawianie rekordu do Historii Sprzedaży tabela.

Zweryfikujmy rekordy w obu obszarach Sprzedaż i Historia sprzedaży stoły:

SELECT * 
FROM Sales

SELECT * 
FROM SalesHistory

Widzimy, że Data zmiany i ZmienionyUżytkownik są wypełniane automatycznie. To dlatego, że zaprojektowaliśmy naszą Historię tabela z wartościami domyślnymi jako GETDATE() i SUSER_NAME() .

Użytkownicy końcowi mogą zobaczyć za pomocą wyzwalacza lub w inny sposób, w jaki sposób ich INSERT została skontrolowana za pomocą dodatkowego 1 wiersza, którego dotyczy problem wiadomość. Jeśli chcesz monitorować zmiany bez informowania użytkowników, musisz zastosować USTAW ROWCOUNT ON Komenda. Pomija wyniki wyświetlane dla operacji DML, które mają miejsce wewnątrz wyzwalacza.

ZMIEŃmy nasz wyzwalacz za pomocą skryptu z SET ROWCOUNT ON opcja i zobacz ją w akcji:

ALTER TRIGGER TR_INS_Sales ON Sales
FOR INSERT 
AS
BEGIN
SET NOCOUNT ON
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'INSERT'
  FROM inserted
END
GO

Teraz wstawiamy kolejny rekord do Sprzedaż tabela:

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-02-01', 1, 50);

Widzimy tylko jeden 1 wiersz, którego dotyczy problem wiadomość. W związku z tym docelowi odbiorcy mogą nie zostać powiadomieni, że ich działania są w ogóle monitorowane.

Sprawdźmy, czy wyzwalacz INSERT został wywołany, weryfikując Sprzedaż i Historia sprzedaży tabele.

Tak, wydarzenie INSERT w sekcji Sprzedaż tabela została pomyślnie uruchomiona. Rekordy zostały wstawione do Historii sprzedaży tabeli bez powiadamiania użytkowników.

Dlatego jeśli utworzysz wyzwalacze do celów kontrolnych, SET NOCOUNT ON jest konieczne. Pozwala na audyt bez powiadamiania nikogo.

Aktywator AKTUALIZACJI

Przed utworzeniem rzeczywistego wyzwalacza UPDATE w sekcji Sprzedaż tabeli, ponownie odwołajmy się do specjalnych logicznych wstawianych i usuwanych tabel. Utwórz przykładowy wyzwalacz UPDATE w sekcji Sprzedaż tabela:

CREATE TRIGGER TR_UPD_Sales ON Sales
FOR UPDATE 
AS
BEGIN
SELECT * FROM inserted
SELECT * FROM deleted
END
GO

Wyzwalacz UPDATE został pomyślnie utworzony. Teraz wstawmy niepoprawnie nowy rekord. Później zaktualizujemy go, aby zweryfikować aktywator UPDATE w akcji:

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-02-01', 1, 50);

Mamy poniższe wpisy w sekcji Sprzedaż i Historia sprzedaży tabela:

Zaktualizujmy SalesId =3 w sekcji Sprzedaż tabela z nowymi wartościami. Zobaczymy dane we wstawionych i usuniętych tabelach:

UPDATE Sales
SET SalesDate = '2021-03-01'
	, Itemcount = 3
	, price = 500
WHERE SalesId = 3

Po wykonaniu operacji UPDATE wszystkie nowe lub zmodyfikowane wartości będą dostępne we wstawionej tabeli, a stare wartości będą dostępne w usuniętej tabeli:

Teraz zmodyfikujmy wyzwalacz UPDATE za pomocą poniższego skryptu i zweryfikujmy go w akcji:

ALTER TRIGGER TR_UPD_Sales ON Sales
FOR UPDATE 
AS
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'UPDATE'
  FROM inserted
END
GO

Wyzwalacz UPDATE został pomyślnie zmieniony i możemy ponownie wykonać ten sam skrypt UPDATE:

Teraz widzimy 1 wiersz, na który ma wpływ wiadomość dwukrotnie. Wskazuje wykonanie operacji UPDATE na Sprzedaż tabeli i operacji INSERT na Historii Sprzedaży stół. Sprawdźmy to, wybierając w obu tabelach:

Aktywność UPDATE jest śledzona w Historii sprzedaży tabeli jako nowy rekord. Przed tym rekordem mamy inny, pokazujący, kiedy rekord został wstawiony jako pierwszy.

USUŃ regułę

Do tej pory testowaliśmy DLA lub PO typ wyzwalaczy dla operacji INSERT lub UPDATE. Teraz możemy spróbować użyć ZAMIAST typ wyzwalacza DML dla operacji DELETE. Użyj poniższego skryptu:

CREATE TRIGGER TR_DEL_Sales ON Sales
INSTEAD OF DELETE 
AS
BEGIN
	RAISERROR ('Notify Sales Team', 16, 10);  
END
GO

Wyzwalacz DELETE został pomyślnie utworzony. Wyśle komunikat o błędzie do klienta, zamiast wykonać polecenie DELETE w sekcji Sprzedaż tabela.

Spróbujmy usunąć rekord SalesID =3 z działu Sprzedaż tabela za pomocą poniższego skryptu:

DELETE FROM Sales
WHERE SalesId = 3

Zablokowaliśmy użytkownikom usuwanie rekordów z Sprzedaż stół. Wyzwalacz zgłosił komunikat o błędzie.

Sprawdźmy też, czy rekord został usunięty z działu Sprzedaż tabeli i czy nastąpiły jakieś zmiany w Historii Sprzedaży tabela:

Ponieważ wykonaliśmy skrypt wyzwalacza przed rzeczywistą instrukcją DELETE przy użyciu wyzwalacza INSTEAD OF, operacja DELETE na SalesId =3 w ogóle się nie powiodła. W związku z tym żadne zmiany nie zostały odzwierciedlone w obu kategoriach Sprzedaż i Historia sprzedaży tabela.

Zmodyfikujmy wyzwalacz za pomocą poniższego skryptu, aby zidentyfikować próbę DELETE w tabeli:

ALTER TRIGGER TR_DEL_Sales ON Sales
INSTEAD OF DELETE 
AS
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'DELETE ATP'
  FROM deleted 
END
GO

Wyzwalacz został pomyślnie zmodyfikowany. Usuńmy SalesId =3 rekord z działu Sprzedaż stół ponownie:

Wykonanie instrukcji DELETE pokazuje 1 wiersz, którego dotyczy problem wiadomość dwukrotnie. Sprawdźmy rekordy w sekcji Sprzedaż i Historia sprzedaży tabele, aby zobaczyć, co się tam dokładnie dzieje:

Logika użyta w wyzwalaczu DELETE polegała na przechwytywaniu wszelkich prób DELETE na stole bez faktycznego usuwania rekordu z Sprzedaż tabeli przy użyciu ZAMIAST cyngiel. Możemy potwierdzić, że rekord nie został usunięty z działu Sprzedaż tabeli, a nowy rekord został wstawiony do Historii sprzedaży tabela.

Pojedynczy wyzwalacz do obsługi operacji INSERT, UPDATE i DELETE

Do tej pory stworzyliśmy 3 wyzwalacze do obsługi operacji INSERT, UPDATE i DELETE na jednej tabeli. Jeśli mamy wiele wyzwalaczy, trudno byłoby nimi zarządzać, zwłaszcza jeśli nie są odpowiednio udokumentowane. Mogą wystąpić problemy z wydajnością, jeśli programiści zastosowali sprzeczną logikę w wielu wyzwalaczach.

Osobiście polecam używanie jednego wyzwalacza z całą logiką, aby uniknąć potencjalnej utraty danych lub problemów z wydajnością. Możemy spróbować połączyć 3 wyzwalacze w jednym, aby uzyskać lepszą wydajność. Ale zanim to zrobimy, przyjrzyjmy się, jak UPUŚĆ istniejące wyzwalacze i jak wyłączyć lub włączyć wyzwalacze.

Upuść spust

Aby połączyć 3 wyzwalacze w jeden, najpierw musimy UPUŚCIĆ te 3 wyzwalacze. Jest to możliwe zarówno za pomocą metod SSMS, jak i T-SQL.

W programie SSMS rozwiń Test baza danych > Stoły > Sprzedaż tabela> Wyzwalacze .

Widzimy nasze 3 wyzwalacze utworzone do tej pory:

Aby upuścić wyzwalacz, po prostu kliknij go prawym przyciskiem myszy> Usuń > OK .

Jeśli wolisz używać T-SQL, zobacz poniższą składnię, aby usunąć wyzwalacz:

DROP TRIGGER <trigger_name>

Istnieje TR_INS_Sales wyzwalacz, który stworzyliśmy w sekcji Sprzedaż stół. Skrypt będzie wyglądał następująco:

DROP TRIGGER TR_INS_Sales

Ważne :Upuszczenie tabeli domyślnie powoduje usunięcie wszystkich wyzwalaczy.

Wyłącz i włącz wyzwalacz

Zamiast upuszczać spust, możemy go tymczasowo wyłączyć za pomocą Wyłącz wyzwalacz opcja przez SSMS lub T-SQL.

W SSMS kliknij prawym przyciskiem myszy Nazwę aktywatora> Wyłącz . Po wyłączeniu wyzwalacz nie zostanie uruchomiony, dopóki go nie włączysz.

Gdy wyzwalacz działa, Włącz opcja jest wyszarzona. Po wyłączeniu Włącz opcja stanie się widoczna i aktywna.

Jeśli wolisz używać T-SQL, możesz wyłączyć i włączyć wyzwalacze za pomocą poniższych skryptów:

-- To Disable all triggers on a specific table
DISABLE TRIGGER ALL ON <table_name>;

-- To Disable a specific trigger on a table
DISABLE TRIGGER <trigger_name> ON <table_name>;

-- To Enable all triggers on a specific table
ENABLE TRIGGER ALL ON <table_name>;

-- To Enable a specific trigger on a table
ENABLE TRIGGER <trigger_name> ON <table_name>;

Aby wyłączyć i włączyć naszą konkretną TR_INS_Sales wyzwalacz w sekcji Sprzedaż tabeli, używamy poniższych skryptów:

-- To Disable TR_INS_Sales trigger on Sales table
DISABLE TRIGGER TR_INS_Sales ON Sales;

-- To Enable TR_INS_Sales trigger on Sales table
ENABLE TRIGGER TR_INS_Sales ON Sales;

W ten sposób nauczyliśmy się DROP , WYŁĄCZ i WŁĄCZ wyzwalacze. Upuszczę 3 istniejące wyzwalacze i utworzę jeden wyzwalacz obejmujący wszystkie 3 operacje lub wstawianie, aktualizowanie i usuwanie za pomocą poniższego skryptu:

DROP TRIGGER TR_INS_Sales
DROP TRIGGER TR_UPD_Sales
DROP TRIGGER TR_DEL_Sales
GO

CREATE TRIGGER TR_INS_UPD_DEL_Sales ON Sales
FOR INSERT, UPDATE, DELETE
AS
BEGIN
IF (SELECT COUNT (*) FROM deleted) = 0
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'INSERT'
  FROM inserted
END
ELSE IF (SELECT COUNT (*) FROM inserted) = 0
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'DELETE'
  FROM deleted
END
ELSE IF (UPDATE (SalesDate) OR UPDATE (ItemCount) OR UPDATE (Price))
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'UPDATE'
  FROM inserted
END 
END
GO

Tworzenie pojedynczego wyzwalacza powiodło się. Użyliśmy logiki do zidentyfikowania operacji za pomocą wstawionych i usuniętych tabel.

W przypadku operacji INSERT usunięta tabela nie zostanie wypełniona. W przypadku operacji DELETE wstawiona tabela nie zostanie wypełniona. Możemy łatwo zidentyfikować te operacje. Jeśli te dwa warunki nie są zgodne, jest to operacja UPDATE i możemy użyć prostej instrukcji ELSE.

Użyłem UPDATE() funkcja, aby pokazać, jak to działa. Gdyby w tych kolumnach były jakieś aktualizacje, uruchomiona zostałaby akcja wyzwalacza UPDATE. Możemy również użyć COLUMNS_UPDATED() funkcja, którą omówiliśmy wcześniej, aby zidentyfikować operację UPDATE.

Przetestujmy nasz nowy wyzwalacz, wstawiając nowy rekord:

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-04-01', 4, 400);

Weryfikowanie rekordów w obszarze Sprzedaż i Historia sprzedaży tabele pokazują dane jak poniżej:

Spróbuj zaktualizować SalesId =2 rekord:

UPDATE Sales
SET price = 250
WHERE SalesId = 2;

Wypróbujmy skrypt DELETE za pomocą tej procedury na SalesId =4 rekord:

DELETE FROM Sales
WHERE SalesId = 4;

Jak możemy zauważyć, SalesId =4 został usunięty z Sprzedaży tabela, ponieważ jest to DLA lub PO wyzwalacz, dzięki czemu operacja DELETE powiodła się w sekcji Sprzedaż tabeli, a następnie wstaw rekord do Historia sprzedaży tabela.

Cel wyzwalaczy DML

Wyzwalacze DML działają skutecznie w następujących scenariuszach:

  1. Śledź historyczne zmiany operacji INSERT, UPDATE i DELETE w określonej tabeli.
  2. Kontroluj zdarzenia DML zachodzące w tabeli bez ujawniania aktywności audytu użytkownikom.
  3. Zapobiegaj zmianom DML na stole za pomocą ZAMIAST wyzwala i ostrzega użytkowników za pomocą określonego komunikatu o błędzie.
  4. Wysyłaj powiadomienia do osób docelowych, gdy osiągną dowolne wstępnie zdefiniowane warunki.
  5. Uruchom zadanie agenta SQL Server lub dowolny inny proces po osiągnięciu dowolnych wstępnie zdefiniowanych warunków.

Możesz ich używać do innych wymagań logiki biznesowej, które możesz zaimplementować za pomocą instrukcji T-SQL.

Wniosek

Treść wyzwalacza DML jest podobna do procedury składowanej. Możemy wdrożyć dowolną wymaganą logikę biznesową, ale musimy zachować ostrożność podczas pisania tej logiki, aby uniknąć potencjalnych problemów.

Mimo że SQL Server obsługuje tworzenie wielu wyzwalaczy w jednej tabeli, lepiej jest skonsolidować je w jednym wyzwalaczu. W ten sposób możesz łatwo utrzymywać wyzwalacze i szybciej je rozwiązywać. Za każdym razem, gdy wyzwalacze DML są wdrażane na potrzeby kontroli, upewnij się, że SET NOCOUNT ON opcja jest efektywnie używana.

W następnym artykule przeanalizujemy wyzwalacze DDL i wyzwalacze logowania.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL Server - połącz wiersze w listę oddzieloną przecinkami

  2. Naturalne łączenie w SQL Server

  3. Zapytanie o pierwszy wiersz aktualizacji SQL

  4. Dlaczego w moich wartościach w kolumnie IDENTITY są luki?

  5. Sprawdź/zmień poziom zgodności bazy danych w SQL Server (SSMS)