2012-01-12 1 views
2

У меня есть две таблицы:SQL Server ВМЕСТО сбоя запуска INSERT

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, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'abc' to data type int.

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

+0

Может быть глупый вопрос, но возможно, что триггер включает четыре вставных оператора, а не два? Я замечаю, что у вас нет 'GO' там ... –

ответ

6

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

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

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

+2

+1; пользователи не должны вставлять текст в столбец с названием «возраст», который измеряется в годах ('int'). –

+0

+1 для ** быть явным ** комментарием - нужно ** всегда ** быть явным в программировании SQL Server :-) –

+0

Пустой 'varchar' без длины предоставил результат в столбце с 1 символом в таблице или определение параметра. В инструкции 'CAST' или' CONVERT' длина по умолчанию 30 (почему это всего 30 в CAST/CONVERT и почему именно это должно быть ** 30 ** символов - я не знаю) –

1

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

4

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

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

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

+2

+1 ... вставленный принимает типы данных базовой таблицы, пользователи в этом случае, насколько я видел. Таким образом, ошибка. –

+1

+1 для обозначения столбцов и упоминания о последствиях не предоставления аргумента длины столбцам 'varchar' - я даже не знал, что это можно сделать! –

+0

@marc_s Btw, Chuck Norris is 71) +1 – noname

0

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

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 но вы потеряете данные, содержащиеся в возрасте

Надежда, что помогает,

Маркус