我有一个带有两个表的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)
);
如果Products
表中存在产品代码,我希望能够阻止将产品插入BlockedProductCodes
表。
我能想到的唯一方法是使用BEFORE INSERT
触发器:
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_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建议的索引视图方法。
答案 0 :(得分:1)
实现此目的的一种方法是滥用索引视图:
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)
现在我们尝试插入:
INSERT INTO dbo.BlockedProductCodes (Code,ReasonCode) values ('abc',10)
GO
INSERT INTO dbo.Products (Code,Name) values ('abc','def')
我们得到:
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
表。
这里的技巧是以这样的方式构造视图,这样,如果Products
和BlockedProductCodes
表之间存在匹配,我们就会生成一个多行结果集。但我们也确保所有行都有一个常量列值,并且结果上有一个唯一索引 - 因此会生成错误。
请注意,当我只使用DRI_
表示名称时,我只使用了我的约定来强制执行完整性约束 - 我并不打算任何人都会查询这个视图(的确如上所示) ,这个观点必须总是空的)