SQL 服务器:看起来有序的唯一组合键
SQL Server: Unique Composite Key that looks order
我创建了一个小测试 table,其中包含 2 个整数列 pkId1
和 pkId2
。我将主键设置为包含两列的组合,(首先是 pkId1
,然后是 pkId2
)。
现在我按以下顺序插入了一些值:(1,1)
、(1,2)
、(2,1)
和 (2,2)
。我原以为 (2,1)
会失败,但事实并非如此。我怎样才能强制它失败?
我可以将其签入我的应用程序,但我想在 SQL 端进行。
另外,我想过使用存储过程,但我想知道在 table.
的设计器中是否有为复合键设置的设置
编辑
pkId1
和 pkId2
的顺序很重要。例如,如果 (2,1) 已经在 table 中,则不应接受 (1,2)。这是因为该行表示两个实体之间的 link。因此,应该读出 (2,1) 从 2 到 1 的 link 和 (1,2) 从 1 到 2 的 link。
对于(1,1),(2,2)等两列相同的值,它们被接受,因为它们是一种特殊情况。
一个选项是创建一个 PERSISTED 列
例子
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TestTable](
[pkId1] [int] NOT NULL,
[pkId2] [int] NOT NULL,
[pkuc] AS (case when [pkId1]<[pkId2]
then concat([pkId1],'-',[pkId2])
else concat([pkId2],'-',[pkId1])
end) PERSISTED NOT NULL UNIQUE
) ON [PRIMARY]
GO
测试
Insert Into [dbo].[TestTable] (pkId1,pkId2) values (1,2)
Insert Into [dbo].[TestTable] (pkId1,pkId2) values (2,1)
Select * from [dbo].[TestTable]
已更新Table
pkId1 pkId2 pkuc
1 2 1-2
出现了两个相对简单的选项。
1.检查约束和“代替”触发器
如果我们创建约束 pkId1 <= pkId2
,则可以满足要求,如 中所述。但是,问题是这也会阻止 (2,1) 的插入。
为了解决这个问题,我们在 table 上创建一个 INSTEAD OF
触发器来交换值:
CREATE TRIGGER TrgINS ON dbo.Table
INSTEAD OF INSERT AS
SET NOCOUNT ON;
INSERT dbo.Table (pkId1, pkId2)
SELECT
IIF(pkId1 > pkId2, pkId2, pkId1),
IIF(pkId1 > pkId2, pkId1, pkId2)
FROM inserted;
GO
2。强制执行约束的索引视图
我们在 table 上创建了一个索引视图,并交换了值。视图本身的唯一约束强制要求,因此 (2,1) 的单个插入不会被阻止, (2,1) 或 (1,2) 的进一步插入将被阻止:
CREATE VIEW vwTableUnique
WITH SCHEMABINDING AS
SELECT
IIF(pkId1 > pkId2, pkId2, pkId1) AS pkId1,
IIF(pkId1 > pkId2, pkId1, pkId2) AS pkId2
FROM dbo.Table;
GO
CREATE UNIQUE CLUSTERED INDEX CX_vwTableUnique ON dbo.Table (pkId1, pkId2);
GO
我创建了一个小测试 table,其中包含 2 个整数列 pkId1
和 pkId2
。我将主键设置为包含两列的组合,(首先是 pkId1
,然后是 pkId2
)。
现在我按以下顺序插入了一些值:(1,1)
、(1,2)
、(2,1)
和 (2,2)
。我原以为 (2,1)
会失败,但事实并非如此。我怎样才能强制它失败?
我可以将其签入我的应用程序,但我想在 SQL 端进行。 另外,我想过使用存储过程,但我想知道在 table.
的设计器中是否有为复合键设置的设置编辑
pkId1
和 pkId2
的顺序很重要。例如,如果 (2,1) 已经在 table 中,则不应接受 (1,2)。这是因为该行表示两个实体之间的 link。因此,应该读出 (2,1) 从 2 到 1 的 link 和 (1,2) 从 1 到 2 的 link。
对于(1,1),(2,2)等两列相同的值,它们被接受,因为它们是一种特殊情况。
一个选项是创建一个 PERSISTED 列
例子
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TestTable](
[pkId1] [int] NOT NULL,
[pkId2] [int] NOT NULL,
[pkuc] AS (case when [pkId1]<[pkId2]
then concat([pkId1],'-',[pkId2])
else concat([pkId2],'-',[pkId1])
end) PERSISTED NOT NULL UNIQUE
) ON [PRIMARY]
GO
测试
Insert Into [dbo].[TestTable] (pkId1,pkId2) values (1,2)
Insert Into [dbo].[TestTable] (pkId1,pkId2) values (2,1)
Select * from [dbo].[TestTable]
已更新Table
pkId1 pkId2 pkuc
1 2 1-2
出现了两个相对简单的选项。
1.检查约束和“代替”触发器
如果我们创建约束 pkId1 <= pkId2
,则可以满足要求,如
为了解决这个问题,我们在 table 上创建一个 INSTEAD OF
触发器来交换值:
CREATE TRIGGER TrgINS ON dbo.Table
INSTEAD OF INSERT AS
SET NOCOUNT ON;
INSERT dbo.Table (pkId1, pkId2)
SELECT
IIF(pkId1 > pkId2, pkId2, pkId1),
IIF(pkId1 > pkId2, pkId1, pkId2)
FROM inserted;
GO
2。强制执行约束的索引视图
我们在 table 上创建了一个索引视图,并交换了值。视图本身的唯一约束强制要求,因此 (2,1) 的单个插入不会被阻止, (2,1) 或 (1,2) 的进一步插入将被阻止:
CREATE VIEW vwTableUnique
WITH SCHEMABINDING AS
SELECT
IIF(pkId1 > pkId2, pkId2, pkId1) AS pkId1,
IIF(pkId1 > pkId2, pkId1, pkId2) AS pkId2
FROM dbo.Table;
GO
CREATE UNIQUE CLUSTERED INDEX CX_vwTableUnique ON dbo.Table (pkId1, pkId2);
GO