Sql server 在SQL Server中插入无效时引发错误

Sql server 在SQL Server中插入无效时引发错误,sql-server,Sql Server,我有一个SQL Server 2012数据库,其中包含两个表: CREATE TABLE Products ( Id INT IDENTITY(1, 1) NOT NULL, Code NVARCHAR(50) NOT NULL, Name NVARCHAR(50) NOT NULL, CONSTRAINT PK_Product PRIMARY KEY CLUSTERED (Id ASC) ); CREATE TABLE Bloc

我有一个SQL Server 2012数据库,其中包含两个表:

CREATE TABLE Products 
(
     Id INT IDENTITY(1, 1) NOT NULL,
     Code NVARCHAR(50) NOT NULL,
     Name NVARCHAR(50) NOT NULL,

     CONSTRAINT PK_Product 
         PRIMARY KEY CLUSTERED (Id ASC)
);

CREATE TABLE BlockedProductCodes 
(
     Code NVARCHAR(50) NOT NULL,
     ReasonCode INT NOT NULL

     CONSTRAINT PK_BlockedProductCodes 
         PRIMARY KEY CLUSTERED (Code ASC)
);
如果产品代码存在于
BlockedProductCodes
表中,我希望能够防止产品插入
products
表中

我能想到的唯一方法是在插入前使用
触发器:

CREATE TRIGGER trg_Products_BEFORE_INSERT
ON Products
INSTEAD OF INSERT AS
BEGIN
    SET NOCOUNT ON;

    IF EXISTS (SELECT Code
               FROM BlockedProductCodes BPC
               INNER JOIN inserted I ON BPC.Code = I.Code)
    BEGIN
        RAISERROR('The product has been blocked!', 16, 1);
    END
    ELSE
    BEGIN
        INSERT Product (Id, Code, Name)
            SELECT Id, Code, Name
            FROM INSERTED
    END

    SET NOCOUNT OFF;
END
但这导致标识列出错:

当identity\u insert设置为OFF时,无法在表“Products”中为identity列插入显式值

有人能提出一种解决方法或更好的方法吗

请注意,此检查也在应用程序级别进行,但我希望在数据表级别强制执行

谢谢

更新:使用检查约束

我尝试了以下似乎有效的方法

CREATE FUNCTION dbo.IsCodeBlocked
(
   @code nvarchar(50)
)
RETURNS BIT
WITH SCHEMABINDING
AS
BEGIN
   DECLARE @ret bit 

   IF (@Code IN (SELECT Code FROM dbo.BlockedProductCodes))
      SET @ret = 1 
   ELSE  
      SET @ret = 0 

   RETURN @ret 
END
GO

ALTER TABLE Products
ADD CONSTRAINT CheckValidCode
   CHECK (dbo.IsCodeBlocked(Code) = 0);
GO

insert Products (Code, Name) values ('xyz', 'Test #1')
go

insert Products (Code, Name) values ('abc', 'Test #2')
-- Fails with "The INSERT statement conflicted with the 
-- CHECK constraint 'CheckValidCode'."
go

我不确定它是否特别“安全”或性能。我还将测试Damien建议的索引视图方法。

实现这一点的一种方法是滥用索引视图:

CREATE TABLE dbo.Products (
   Id     INT  IDENTITY (1, 1) NOT NULL,
   Code   NVARCHAR(50) NOT NULL,
   Name   NVARCHAR(50) NOT NULL,
   CONSTRAINT PK_Product PRIMARY KEY CLUSTERED (Id ASC)
);
GO
CREATE TABLE dbo.BlockedProductCodes (
   Code   NVARCHAR(50) NOT NULL,
   ReasonCode INT NOT NULL
   CONSTRAINT PK_BlockedProductCodes PRIMARY KEY CLUSTERED (Code ASC)
);
GO
CREATE TABLE dbo.Two (
    N int not null,
    constraint CK_Two_N CHECK (N > 0 and N < 3),
    constraint PK_Two PRIMARY KEY (N)
)
GO
INSERT INTO dbo.Two(N) values (1),(2)
GO
create view dbo.DRI_NoBlockedCodes
with schemabinding
as
    select
        1 as Row
    from
        dbo.Products p
            inner join
        dbo.BlockedProductCodes bpc
            on
                p.Code = bpc.Code
            inner join
        dbo.Two t
            on
                1=1
GO
CREATE UNIQUE CLUSTERED INDEX IX_DRI_NoBlockedCodes on dbo.DRI_NoBlockedCodes (Row)
我们得到:

Msg 2601, Level 14, State 1, Line 42
Cannot insert duplicate key row in object 'dbo.DRI_NoBlockedCodes' with unique index 'IX_DRI_NoBlockedCodes'. The duplicate key value is (1).
The statement has been terminated.
因此,如果您可以接受该错误消息,这可能是一种方法。注意,如果你有一个数字表,你可以用它来代替我的虚拟
Two

这里的技巧是以这样的方式构造视图,这样,如果
产品
BlockedProductCodes
表之间存在匹配,我们就会生成一个多行结果集。但我们还确保所有行都有一个常量列值,并且结果上有一个唯一的索引,因此会生成错误


注意,我使用了我的惯例,即在表名前面加上
DRI\ucode>,当它仅用于强制执行完整性约束时-我不希望任何人查询此视图(事实上,如上所示,此视图实际上必须始终为空)

因此,实际上您想要一个“反外键”?有趣的问题。检查约束不会做同样的事情吗?是的,就是这样-一个不熟悉此上下文中检查约束的人-我将看一看..@JamesZ-由于检查约束不能跨表工作,您是否认为它本身会包含禁止的代码?当然,这可能会奏效,但这确实意味着维护禁用列表需要修改模式。这当然很有效,而且从我所读到的来看,这可能是一个比使用UDF约束更健壮/可靠的选择-谢谢。出于兴趣,
DRI
首字母缩写代表什么?@Neilski-谢谢-好主意,我需要改进我的比赛!
Msg 2601, Level 14, State 1, Line 42
Cannot insert duplicate key row in object 'dbo.DRI_NoBlockedCodes' with unique index 'IX_DRI_NoBlockedCodes'. The duplicate key value is (1).
The statement has been terminated.