SQL Server INSTEAD OF INSERT Ошибка триггера - PullRequest
2 голосов
/ 13 января 2012

У меня есть две таблицы:

CREATE TABLE users
(
    id int PRIMARY KEY IDENTITY,
    firstname varchar(30),
    lastname varchar(30),
    age int
)

CREATE TABLE rejectedUsers
(
    id int PRIMARY KEY IDENTITY,
    firstname nvarchar,
    lastname nvarchar,
    age nvarchar
)

И триггер:

CREATE TRIGGER checkNumeric
ON users
INSTEAD OF INSERT
AS
INSERT INTO users SELECT firstname, lastname, age FROM inserted WHERE ISNUMERIC(age) = 1;
INSERT INTO rejectedUsers SELECT firstname, lastname, age FROM inserted WHERE ISNUMERIC(age) = 0;

-- goes fine
INSERT INTO users
VALUES
('Vlad', 'P', 21)

-- fails

INSERT INTO users
VALUES
('Chuck', 'Norris', 'abc')

Второе утверждение выдает мне ошибку:

Msg 245,Уровень 16, состояние 1, строка 1
Преобразование не удалось при преобразовании значения varchar 'abc' в тип данных int.

Я ожидаю, что триггер выполнит вставку в rejectedUsers, что не так

Ответы [ 4 ]

6 голосов
/ 13 января 2012

Это ошибка времени выполнения, а не ошибка компилятора.Типы сравниваются с базовой таблицей во время выполнения, независимо от какой-либо вспомогательной логики, которую вы могли закодировать вместо триггера.Вставленная псевдотаблица создается на основе базовой таблицы, а не типов переменных, которые вы передаете в произвольный оператор вставки.

Не думаю, что вы сможете обеспечить прозрачность типовпросто ударив вместо триггера перед ним.Если вы хотите, чтобы люди могли создавать специальные запросы, в которые они могут добавлять любые типы данных, вам нужно будет сначала использовать те из промежуточной таблицы, которая размещает нежелательные файлы.Или делайте то, что делает большинство людей: используйте интерфейс для принудительного применения типа данных.Если вы используете хранимую процедуру, которая принимает параметр @age в качестве INT, они не могут получить ничего рядом с таблицей с 'abc' ... также age не имеет большого значения, потому что она может измениться завтра или на следующий день.Почему бы не принять день рождения вместо этого?

Наконец, пожалуйста, не определяйте столбцы или переменные как varchar / nvarchar без длины.В некоторых случаях они состоят из 1 символа, в других - 30. Будьте явными и расскажите всем, что вы на самом деле имеете в виду.

4 голосов
/ 13 января 2012

Ваш код здесь:

INSERT INTO users
VALUES('Chuck', 'Norris', 'abc')

попытается вставить abc в столбец age, который имеет тип int - действительно ли это int ?? Я считаю, что даже если у вас есть триггер INSTEAD OF INSERT, таблица Inserted, которая находится внутри триггера, будет иметь ту же структуру, что и ваша таблица Users - и этот столбец age является столбцом INT и может не имеет дело с abc как значением!

В качестве общей рекомендации, я бы всегда явно указывал, какие столбцы вы имеете в виду во вставках - поэтому я бы написал вышеупомянутый запрос как:

INSERT INTO users(firstname, lastname, age)
VALUES('Chuck', 'Norris', 71)

и я бы изменил это:

INSERT INTO users 
    SELECT firstname, lastname, age 
    FROM inserted 
    WHERE ISNUMERIC(age) = 1;

на это:

INSERT INTO users(firstname, lastname, age)
    SELECT firstname, lastname, age 
    FROM inserted 
    WHERE ISNUMERIC(age) = 1;

Таким образом, у вас не будет никаких неприятных сюрпризов, если какая-то таблица будет изменена и внезапно получит новый столбец, который ваш "универсальный" INSERT не заполнит ...

В качестве примечания: я надеюсь, что вы понимаете это, указав следующее:

CREATE TABLE rejectedUsers
(
    id int PRIMARY KEY IDENTITY,
    firstname nvarchar,
    lastname nvarchar,
    age nvarchar
)

Вы фактически создаете таблицу с большим количеством nvarchar(1) столбцов, которые могут содержать колоссальный максимум 1 символа каждый! Вы также должны всегда предоставлять явные длины для своих varchar и nvarchar столбцов .....

1 голос
/ 13 января 2012

Вы не можете вставить 'abc' в age, так как это поле int.Измените тип данных возраста в users на VARCHAR(10) или что-то подобное, если вы действительно хотите это сделать, или вставьте вместо него int.

0 голосов
/ 21 ноября 2012

Просто дикая догадка, но, возможно, попробуйте изменить отклоненный синтаксис на следующий

INSERT INTO rejectedUsers SELECT firstname, lastname, age FROM inserted WHERE ISNUMERIC(age) >= 0 and ISNUMERIC(age) > 120;

Это предотвратит переход людей в возрасте старше 120 лет или в возрасте менее 0 лет (или то, что вы когда-либо решите, должно быть минимальным / максимальным возрастом), но теоретически, если это разрешит пропуск varchar в int, нажмите «abc», чтобы отклонить также.

Если проблема с конверсией не устранена, попробуйте следующее:

INSERT INTO rejectedUsers SELECT firstname, lastname, case when age between 0 and 120 then age else 0 end as age FROM inserted WHERE ISNUMERIC(age) = 0;

Если я правильно понимаю ваше использование триггера (никогда не использовал их), это снова проверит, что возраст находится в пределах диапазона, в противном случае отправьте 0, но вы потеряете данные, содержащиеся в возрасте

Надеюсь, что помог,

Marcus

...