跨多个表的SQL唯一约束

Cor*_*ryC 22 sql-server database-design unique-constraint

我试图在多个表中创建一个唯一约束.我在这里找到了类似的问题,但是他们并没有完全捕捉到我想要做的精神.

作为一个例子,我有三个表,t_Analog,t_Discrete,t_Message

CREATE TABLE t_Analog(
    [AppName] [nvarchar](20) NOT NULL,
    [ItemName] [nvarchar](32) NOT NULL,
    [Value] [float] NOT NULL,
    CONSTRAINT [uc_t_Analog] UNIQUE(AppName, ItemName)
)

CREATE TABLE t_Discrete(
    [AppName] [nvarchar](20) NOT NULL,
    [ItemName] [nvarchar](32) NOT NULL,
    [Value] [bit] NOT NULL,
    CONSTRAINT [uc_t_Discrete] UNIQUE(AppName, ItemName)
)

CREATE TABLE t_Message(
    [AppName] [nvarchar](20) NOT NULL,
    [ItemName] [nvarchar](32) NOT NULL,
    [Value] [nvarchar](256) NOT NULL,
    CONSTRAINT [uc_t_Message] UNIQUE(AppName, ItemName)
)
Run Code Online (Sandbox Code Playgroud)

我的目标是在所有3个表中使AppName和ItemName唯一.例如,应用程序X中的项目名称Y不能同时存在于模拟和离散表中.

请注意,这个例子是设计的,每个Type的实际数据是不同的,大到足以组合表并添加一个非常难看的Type列.

如果您对此方法有任何建议,我很乐意听到他们的意见!

----开始编辑2012-04-26 13:28 CST ----

谢谢大家的答案!

似乎可能有理由修改此数据库的架构,这很好.

将表组合到单个表中实际上并不是一个可行的选项,因为每个类型的列数量不超过30个(不幸的是,修改这些列不是一个选项).这可能导致每行中没有使用大部分列,这似乎是一个坏主意.

添加第4个表,如John Sikora和其他人提到的,可能是一个选项,但我想首先验证这一点.

修改架构为:

CREATE TABLE t_AllItems(
    [id] [bigint] IDENTITY(1,1) NOT NULL,
    [itemType] [int] NOT NULL,
    [AppName] [nvarchar](20) NOT NULL,
    [ItemName] [nvarchar](32) NOT NULL,
    CONSTRAINT [pk_t_AllItems] PRIMARY KEY CLUSTERED ( [id] )
    CONSTRAINT [uc_t_AllItems] UNIQUE([id], [AppName], [ItemName])
) ON [PRIMARY]

CREATE TABLE t_Analog(
    [itemId] [bigint] NOT NULL,
    [Value] [float] NOT NULL,
    FOREIGN KEY (itemId) REFERENCES t_AllItems(id)
)

CREATE TABLE t_Discrete(
    [itemId] [bigint] NOT NULL,
    [Value] [bit] NOT NULL,
    FOREIGN KEY (itemId) REFERENCES t_AllItems(id)
)

CREATE TABLE t_Message(
    [itemId] [bigint] NOT NULL,
    [Value] [nvarchar](256) NOT NULL,
    FOREIGN KEY (itemId) REFERENCES t_AllItems(id)
)
Run Code Online (Sandbox Code Playgroud)

关于这种方法,我只有一个问题.这是否会强制子表中的唯一性?

例如,是否存在"项目",其中"id"9表格t_Analog的"itemId"为9,"value"为9.3,同时t_Message的"itemId"9的"值"为"富"?

我可能不完全理解这种额外的表格方法,但我并不反对.

如果我错了,请纠正我.

Joh*_*kor 12

添加第4个表专门针对您想要唯一的值,然后使用一对多关系将这些表中的这些键链接到其他表中.例如,您将拥有一个唯一的表,其中包含ID,AppName和ItemName以构成其3列.然后将此表链接到其他人.

有关如何执行此操作,这是一个很好的示例 使用SQL Server创建一对多关系

编辑:这是我会做的,但考虑到您的服务器需求,您可以更改所需的内容:

CREATE TABLE AllItems(
    [id] [int] IDENTITY(1,1) NOT NULL,
    [itemType] [int] NOT NULL,
    [AppName] [nvarchar](20) NOT NULL,
    [ItemName] [nvarchar](32) NOT NULL,
    CONSTRAINT [pk_AllItems] PRIMARY KEY CLUSTERED ( [id] ASC )
) ON [PRIMARY]

CREATE TABLE Analog(
    [itemId] [int] NOT NULL,
    [Value] [float] NOT NULL
)

CREATE TABLE Discrete(
    [itemId] [int] NOT NULL,
    [Value] [bit] NOT NULL
)

CREATE TABLE Message(
    [itemId] [bigint] NOT NULL,
    [Value] [nvarchar](256) NOT NULL
)

ALTER TABLE [Analog] WITH CHECK 
    ADD CONSTRAINT [FK_Analog_AllItems] FOREIGN KEY([itemId])
REFERENCES [AllItems] ([id])
GO
ALTER TABLE [Analog] CHECK CONSTRAINT [FK_Analog_AllItems]
GO

ALTER TABLE [Discrete] WITH CHECK 
    ADD CONSTRAINT [FK_Discrete_AllItems] FOREIGN KEY([itemId])
REFERENCES [AllItems] ([id])
GO
ALTER TABLE [Discrete] CHECK CONSTRAINT [FK_Discrete_AllItems]
GO

ALTER TABLE [Message] WITH CHECK 
    ADD CONSTRAINT [FK_Message_AllItems] FOREIGN KEY([itemId])
REFERENCES [AllItems] ([id])
GO
ALTER TABLE [Message] CHECK CONSTRAINT [FK_Message_AllItems]
GO
Run Code Online (Sandbox Code Playgroud)

从我可以告诉你的语法很好,我只是简单地改变它,因为我更熟悉它,但要么应该工作.

  • 你走在正确的轨道上,但这还不够好.问题在于没有任何限制来阻止ID号100出现在每个表中.要更严格地使用项目类型,请参阅[此答案](http://stackoverflow.com/a/10077883/562459)和[此答案](http://stackoverflow.com/a/5471265/562459). (5认同)

Tim*_*ner 9

虽然您可能会或可能不想像其他答案那样改变您的架构,但索引视图可以应用您正在讨论的约束:

CREATE VIEW v_Analog_Discrete_Message_UK WITH SCHEMABINDING AS
SELECT a.AppName, a.ItemName
FROM dbo.t_Analog a, dbo.t_Discrete b, dbo.t_Message c, dbo.Tally t
WHERE (a.AppName = b.AppName and a.ItemName = b.ItemName)
    OR (a.AppName = c.AppName and a.ItemName = c.ItemName)
    OR (b.AppName = c.AppName and b.ItemName = c.ItemName)
    AND t.N <= 2
GO
CREATE UNIQUE CLUSTERED INDEX IX_AppName_ItemName_UK
    ON v_Analog_Discrete_Message_UK (AppName, ItemName)
GO
Run Code Online (Sandbox Code Playgroud)

你将需要一个"Tally"或数字表,或者必须另外生成一个即时,Celko风格:

-- Celko-style derived numbers table to 100k
select a.N + b.N * 10 + c.N * 100 + d.N * 1000 + e.N * 10000 + 1 as N
from (select 0 as N union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) a
      , (select 0 as N union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) b
      , (select 0 as N union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) c
      , (select 0 as N union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d
      , (select 0 as N union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) e
order by N
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,我发现这是最优雅的解决方案 (4认同)