Триггер SQL выдает ошибку, но все еще вставляет

#sql #sql-server

#sql #sql-server

Вопрос:

Я работаю над триггером, который должен блокировать вставку, когда @verkoperstatus равно 0; это работает, но по какой-то причине он также останавливает вставку, когда @verkoperstatus равно 1. Что может быть основной причиной этого?

 CREATE TRIGGER [dbo].[verkoper_check] ON [dbo].[Verkoper]
FOR INSERT,UPDATE
AS
BEGIN
DECLARE @verkoperstatus bit 
DECLARE @gebruikersnaam varchar(25)
SELECT @gebruikersnaam = gebruikersnaam FROM inserted 
SELECT @verkoperstatus = verkoper FROM Gebruiker WHERE gebruikersnaam = @gebruikersnaam
    IF @verkoperstatus = 0
        BEGIN
            RAISERROR('Geen verkoper!',18,1);
            ROLLBACK;
        END
    ELSE
        BEGIN
            COMMIT;
        END
END
  

Он должен вставляться, когда @verkoperstatus равно 1, и вызывать ошибку, когда @verkoperstatus равно 0.

Таблица Gebruiker — это ссылки, которые включают столбец ‘gebruikersnaam’ и столбец ‘verkoper’. Значение столбца ‘gebruikersnaam’ является идентифицирующим столбцом, который (в данном конкретном случае — ‘Lars’). Verkoper — это столбец битов, который указывает, является ли он продавцом или нет, поэтому он имеет значение 0 или 1.

Цель, которую я пытаюсь достичь, — вставить в таблицу Verkoper, если ‘gebruikersnaam’ имеет значение ‘verkoper’, равное единице. Это означает, что если в Gebruiker есть строка с ‘gebruikersnaam’ из Lars, а значение verkoper равно 1. Это будет разрешенная вставка в таблицу Verkoper.

Поскольку в справочнике есть следующие столбцы: ‘gebruikersnaam’, ‘banknaam’, ‘rekeningnummer’, ‘controleoptienaam’ и ‘creditcardnummer’. Когда ‘gebruikersnaam’ соответствует ‘gebruikersnaam’ из таблицы Gebruikers И имеет значение 1 в столбце ‘verkoper’, этой записи будет разрешено вставляться в таблицу Verkoper.

На данный момент в столбце Gebruikers есть строка, которая включает в себя gebruikersnaam ‘Lars’ и значение verkoper ‘1’. Это означает, что любая вставка SQL с gebruikersnaam ‘Lars’ должна быть разрешена в таблице Verkoper.

Однако это работает не так, как я считаю нужным.

Это таблицы, упомянутые выше:

 CREATE TABLE Verkoper (
    gebruikersnaam varchar(25) NOT NULL,
    banknaam varchar(255) NULL,
    rekeningnummer varchar(32) NULL,
    controleoptienaam char(10) NOT NULL,
    creditcardnummer integer NULL,
    CONSTRAINT pk_Verkoper PRIMARY KEY (gebruikersnaam),
    CONSTRAINT fk_Verkoper_Gebruikersnaam FOREIGN KEY (gebruikersnaam) REFERENCES Gebruiker(gebruikersnaam),
    CONSTRAINT ck_rekening CHECK (rekeningnummer is NOT NULL OR creditcardnummer is NOT NULL),
    CONSTRAINT ck_controleoptie CHECK (controleoptienaam IN('Post', 'Creditcard'))
)

CREATE TABLE  Gebruiker(
    gebruikersnaam varchar(25) NOT NULL,
    voornaam varchar(25) NOT NULL,
    achternaam varchar(25) NOT NULL,
    adresregel_1 varchar(255) NULL,
    adresregel_2 varchar(255) NULL,
    postcode char(7) NULL,
    plaatsnaam varchar(255) NULL,
    land varchar(255) NULL,
    geboortedag char(10) NOT NULL,
    mailbox varchar(255) NOT NULL,
    wachtwoord varchar(255) NOT NULL,
    verkoper bit NOT NULL,

CONSTRAINT pk_gebruiker PRIMARY KEY (gebruikersnaam),
)
  

Для вставок я использую следующие данные:

 INSERT INTO Gebruiker VALUES ('Lars', 'Lars', 'Last_name', null, null, null, null, null, '04/04/2019', 'lars@mymailbox.cloud', 'MyPassword', 1)

INSERT INTO Verkoper VALUES ('Lars', 'ING', 'NL32ABN32492809', 'Post', null)

  

Комментарии:

1. Ваш триггер ошибочен; предполагается, что вы будете использовать только INSERT / UPDATE одну строку за раз. Триггер срабатывает один раз для каждого оператора DML, а не один раз для каждой строки для оператора DML. Вероятно, именно поэтому вы думаете, что триггер «не срабатывает»; это потому, что он делает не то, что вы намереваетесь.

2. Вы также совершаете транзакцию, которую не запускали.

3. @GSerg триггер уже находится в транзакции. Вы не можете запустить его в триггере, поскольку SQL Server не поддерживает «вложенные преобразования». Хотя, я признаю, COMMIT это не нужно, так как это должно обрабатываться вне триггера.

4. @Larnu Я не говорю, что нет транзакции для фиксации. Я говорю, что триггер зафиксирует транзакцию, запущенную вызывающим кодом. Вызывающий код вряд ли будет подготовлен к этому.

5. да, я согласен, @GSerg . Я пытался добавить некоторые дополнительные детали к вашему комментарию, поскольку это можно понимать как означающее (по OP), что они должны запустить транзакцию внутри триггера; что делать неправильно.

Ответ №1:

Это непроверено, однако я подозреваю, что это та логика, которая вам действительно нужна:

 CREATE TRIGGER [dbo].[verkoper_check] ON [dbo].[Verkoper]
FOR INSERT,UPDATE
AS BEGIN

    IF EXISTS(SELECT 1
              FROM inserted i
                   JOIN Gebruiker G ON i.gebruikersnaam = G.gebruikersnaam
              WHERE G.verkoper = 0) BEGIN

        RAISERROR('Geen verkoper!',18,1);
        ROLLBACK;
    END;
END;
  

Комментарии:

1. Это, к сожалению, возвращает Msg 3609, Level 16, State 1, Line 1 The transaction ended in the trigger. The batch has been aborted.

2. Почему это нежелательно, @Lars? Это именно то, что вы описали, чего хотите добиться.

3. Ну, вставка не проходит, хотя так и должно быть, по крайней мере, я так считаю. Поскольку verkoper = 1 на вставке, которую я пытаюсь достичь. INSERT INTO Verkoper VALUES ('Lars', 'ING', 'NL32ABN32492809', 'Post', null) учетная запись Lars имеет значение Verkoper, равное 1.

4. Это INSERT утверждение для меня бессмысленно @Lars. Я не знаю, в какие столбцы вставляются эти значения, и я понятия не имею, как это связано с вашей таблицей Gebruiker . Отредактируйте свой вопрос, чтобы включить DDL и ваши таблицы, а затем предоставьте образец базы данных и ожидайте результатов (все это должно быть как text ).

5. Я отредактировал основной вопрос и надеюсь, что включил достаточно информации, чтобы сделать мой вопрос более понятным.