Ошибка проверки SQL Server при правильном вводе - PullRequest
3 голосов
/ 07 июля 2011

Я использую проверочное ограничение для таблицы, чтобы ограничить, какие значения вставляются в таблицу.

Вот объяснение того, что я пытаюсь сделать

Если какой-либо продукт(седан) связан с определенным ObjLevel (Toyota), тогда тот же Продукт не может быть связан с другим конкретным ObjLevel (Lexus)

После того, как я применяю проверочное ограничение к таблице, любая вставка, содержащая ObjLevel «toyota» или«lexus» завершается ошибкой ..

create table ObjLevel(
OLID int identity,
Name varchar(50) not null
)

insert into ObjLevel values('Ford')
insert into ObjLevel values('Toyota')
insert into ObjLevel values('Lexus')
insert into ObjLevel values('GM')
insert into ObjLevel values('Infiniti')


create table ObjInstance(
OLIID int identity (20,1),
OLID int
)

insert into ObjInstance values(1)
insert into ObjInstance values(2)
insert into ObjInstance values(3)
insert into ObjInstance values(4)
insert into ObjInstance values(5)


create table Product(
PID int identity(50,1),
Name varchar(20)
)

insert into Product values ('sedan')
insert into Product values ('coupe')
insert into Product values ('hatchback')


create table ObjInstanceProd(
OLIID int,
PID int
)


create FUNCTION [dbo].[fnObjProd] (@Pid int) RETURNS bit WITH EXECUTE AS CALLER AS

BEGIN
   DECLARE @rv bit
   DECLARE @cnt int
   SET @cnt = 0
   SET @rv = 0

      SET @cnt= 
            (Select Count(*) from ObjInstanceProd olip
            join ObjInstance oli 
            on olip.OLIID = oli.OLIID
            join ObjLevel ol
            on ol.OLID = oli.OLID
            where ol.Name in ('Toyota','Lexus')
            and PID = @Pid)

            if(@cnt>0)
                SET @rv = 1

   RETURN @rv
END


ALTER TABLE [dbo].[ObjInstanceProd]  WITH CHECK ADD  CONSTRAINT [CK_OLIP] CHECK  ([dbo].[fnObjProd]([PID])=0)


--Insert Statement    
insert into ObjInstanceProd(OLIID,PID) values (22,51)

Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the CHECK constraint "CK_OLIP". The conflict occurred in database "tmp", table "dbo.ObjInstanceProd", column 'PID'.
The statement has been terminated.


--Execute Function    
select [dbo].[fnObjProd] (51) 
0

Изначально таблица ObjInstanceProd пуста. Поэтому, независимо от того, какое значение я добавлю в таблицу, до тех пор, пока функция в ограничении возвращает 0, она должна приниматьэто .. Но это не так. Функция корректно возвращает 0 (когда выполняется независимо), но по какой-то причине проверочное ограничение возвращает 1

1 Ответ

6 голосов
/ 07 июля 2011

Когда срабатывает ограничение CHECK, строка уже находится в таблице. Поэтому функция вызывается, и поскольку запрос возвращает строку, функция возвращает 1, а не 0. Попробуйте это. Удалите ограничение, успешно вставьте строку и выполните этот запрос:

SELECT OLIID, PID, dbo.fnObjProd([PID]) FROM dbo.ObjInstanceProd;

Должно возвращаться 1 для каждого значения PID. Попробуйте добавить ограничение сейчас. Это не удастся по той же причине.

Рассматривали ли вы использование триггера для этого? Если вы используете проверочное ограничение, это превратит любую многострочную вставку или обновление в курсор за сценой. Это может абсолютно убить производительность и параллелизм в зависимости от того, как вы касаетесь ваших таблиц. Вот простой триггер INSTEAD OF INSERT для предотвращения ввода неверных значений одной операцией, даже для многострочной вставки:

CREATE TRIGGER dbo.trObjProd 
ON dbo.ObjInstanceProd
INSTEAD OF INSERT AS
BEGIN
    SET NOCOUNT ON;

    IF NOT EXISTS
    (
      SELECT 1 FROM inserted
      WHERE EXISTS 
      (
        SELECT 1
            FROM dbo.ObjInstanceProd AS olip
            INNER JOIN dbo.ObjInstance AS oli 
            ON olip.OLIID = oli.OLIID
            INNER JOIN dbo.ObjLevel AS ol
            ON ol.OLID = oli.OLID
        WHERE 
            ol.Name in ('Toyota','Lexus')
            AND olip.PID = inserted.PID
      )
  )
  BEGIN
    INSERT ObjInstanceProd(OLIID, PID)
        SELECT OLIID, PID FROM inserted;
  END
  ELSE
  BEGIN
    RAISERROR('At least one value was not good.', 11, 1); 
    SELECT OLIID, PID FROM inserted;
  END
END
GO

Если вы собираетесь придерживаться функции, это гораздо более эффективный подход, однако вам нужно определить способ определить, что текущая вставляемая строка исключена из проверки - я не мог определить, как сделать это, потому что нет никаких ограничений на dbo.ObjInstanceProd. OLIID, PID уникален?

ALTER FUNCTION [dbo].[fnObjProd]
(
    @Pid INT
) 
RETURNS BIT
WITH EXECUTE AS CALLER 
AS
BEGIN
    RETURN 
    (
        SELECT CASE WHEN EXISTS 
        (
            SELECT 1 
                FROM dbo.ObjInstanceProd AS olip
                INNER JOIN dbo.ObjInstance AS oli 
                ON olip.OLIID = oli.OLIID
                INNER JOIN dbo.ObjLevel AS ol
                ON ol.OLID = oli.OLID
            WHERE 
                ol.Name in ('Toyota','Lexus')
                AND olip.PID = @Pid
        ) THEN 1 ELSE 0 END
    );
END
GO
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...