Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Kích hoạt trong SQL Server - Nhận loại Giao dịch được thực hiện cho Bảng kiểm tra

Sau khi bạn sửa trình kích hoạt của mình để bao gồm tất cả ba hoạt động,

IF EXISTS (SELECT 1 FROM inserted)
BEGIN
  IF EXISTS (SELECT 1 FROM deleted)
  BEGIN
    SET @action = 'UPDATE';
  END
  ELSE
  BEGIN
    SET @action = 'INSERT';
  END
ELSE
BEGIN
  SET @action = 'DELETE';
END

Một giải pháp thay thế khác là ba trình kích hoạt riêng biệt, một trình kích hoạt cho mỗi hành động.

Hãy cảnh giác với MERGE mặc dù nếu bạn đang sử dụng nó ... Hoặc chuẩn bị cho nó khi bạn chuyển sang SQL Server 2008 trở lên.

CHỈNH SỬA

Tôi nghĩ những gì bạn có thể sau là một INSTEAD OF kích hoạt thay thế (thật mỉa mai). Đây là một ví dụ. Hãy xem xét một bảng rất đơn giản với một cột PK và một cột duy nhất:

CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO

Và một bảng nhật ký đơn giản để nắm bắt hoạt động:

CREATE TABLE dbo.myLog
(
    foobar_id INT, 
    oldValue  XML, 
    newValue  XML, 
    [action]  CHAR(6), 
    success   BIT
);
GO

INSTEAD OF sau trình kích hoạt sẽ chặn INSERT/UPDATE/DELETE các lệnh, cố gắng tái tạo công việc mà họ sẽ làm và ghi lại xem đó là thất bại hay thành công:

CREATE TRIGGER dbo.foobar_inst
ON dbo.foobar
INSTEAD OF INSERT, UPDATE
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @action  CHAR(6), @success BIT;

  SELECT @action  = 'DELETE', @success = 1;

  IF EXISTS (SELECT 1 FROM inserted)
  BEGIN
    IF EXISTS (SELECT 1 FROM deleted)
      SET @action = 'UPDATE';
    ELSE
      SET @action = 'INSERT';
  END

  BEGIN TRY
    IF @action = 'INSERT'
      INSERT dbo.foobar(id, x) SELECT id, x FROM inserted;

    IF @action = 'UPDATE'
      UPDATE f SET x = i.x FROM dbo.foobar AS f
        INNER JOIN inserted AS i ON f.id = i.id;

    IF @action = 'DELETE'
        DELETE f FROM dbo.foobar AS f
          INNER JOIN inserted AS i ON f.id = i.id;
  END TRY
  BEGIN CATCH
    ROLLBACK; -- key part here!

    SET @success = 0;
  END CATCH

  IF @action = 'INSERT'
    INSERT dbo.myLog SELECT i.id, NULL, 
      (SELECT * FROM inserted WHERE id = i.id FOR XML PATH),
      @action, @success FROM inserted AS i;

  IF @action = 'UPDATE'
    INSERT dbo.myLog SELECT i.id, 
      (SELECT * FROM deleted  WHERE id = i.id FOR XML PATH),
      (SELECT * FROM inserted WHERE id = i.id FOR XML PATH),
      @action, @success FROM inserted AS i;

  IF @action = 'DELETE'
    INSERT dbo.myLog SELECT d.id, 
      (SELECT * FROM deleted  WHERE id = d.id FOR XML PATH),
      NULL, @action, @success FROM deleted AS d;
END
GO

Hãy thử một số câu lệnh giao dịch ngầm, rất đơn giản:

-- these succeed:

INSERT dbo.foobar SELECT 1, 'x';
GO
INSERT dbo.foobar SELECT 2, 'y';
GO

-- fails with PK violation:

INSERT dbo.foobar SELECT 1, 'z';
GO

-- fails with UQ violation:

UPDATE dbo.foobar SET x = 'y' WHERE id = 1;
GO

Kiểm tra nhật ký:

SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;

Kết quả:

foobar_id oldValue                      newValue                      action success
--------- ----------------------------- ----------------------------- ------ -------
1         NULL                          <row><id>1</id><x>x</x></row> INSERT 1
2         NULL                          <row><id>2</id><x>y</x></row> INSERT 1
1         NULL                          <row><id>1</id><x>z</x></row> INSERT 0
1         <row><id>1</id><x>x</x></row> <row><id>1</id><x>y</x></row> UPDATE 0

Tất nhiên bạn có thể muốn các cột khác trên bảng nhật ký, chẳng hạn như người dùng, ngày / giờ, thậm chí có thể là câu lệnh gốc. Đây không phải là một giải pháp kiểm toán toàn diện hoàn toàn, chỉ là một ví dụ.

Như Mikael đã chỉ ra, điều này dựa trên thực tế rằng lô bên ngoài là một lệnh duy nhất bắt đầu một giao dịch ngầm. Hành vi sẽ phải được kiểm tra nếu lô bên ngoài là một giao dịch nhiều câu lệnh rõ ràng.

Cũng lưu ý rằng điều này không nắm bắt được "thất bại" trong trường hợp, ví dụ, CẬP NHẬT ảnh hưởng đến hàng không. Vì vậy, bạn cần xác định rõ ràng "thất bại" có nghĩa là gì - trong một số trường hợp, bạn có thể cần xây dựng cách xử lý lỗi của riêng mình trong mã bên ngoài, không phải trong trình kích hoạt.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Làm cách nào để tạo một số ngẫu nhiên cho mỗi hàng trong một lựa chọn T-SQL?

  2. Đặt bảng ngẫu nhiên nhưng có ngoại lệ

  3. Lỗi khi chèn ngày và giờ trong SQL Server 2005 datetime c #?

  4. Làm cách nào để kết nối với cơ sở dữ liệu SQL Server trong CodeIgniter?

  5. Sql Thích RegEx