【问题标题】:Constraint in SQL Server where value in Col1 can only have a limited choices of values in Col2SQL Server 中的约束,其中 Col1 中的值只能在 Col2 中选择有限的值
【发布时间】:2016-02-25 21:14:15
【问题描述】:

根据 C1 的值,我希望能够将有限数量的字符串选择插入 Col2。包含以下代码以进行澄清。

CREATE TABLE [dbo].[tester]
(
    [C1] [varchar](50) NULL,
    [C2] [varchar](50) NULL
) ON [PRIMARY]

INSERT INTO [dbo].[tester] ([C1], [C2])
VALUES ('ADDRESS', 'Primary')
GO

对于字符串 'address',我只想让 column2 选择字符串 'primary'、'secondary'、'home'。

INSERT INTO [dbo].[tester] ([C1], [C2])
VALUES ('phone', 'home')
GO

对于字符串 'phone',我想给 column2 选择只有 'leisure'、'work'、'business'。

【问题讨论】:

  • “给 column2 一个选择...”是什么意思?您是说当有人尝试在 C1 中插入“电话”并在 C2 中插入“外星人”时返回错误?
  • 你不能那样做。改用触发器

标签: sql sql-server tsql constraints


【解决方案1】:

通常,您将有两个单独的表 AddressTypesPhoneTypes,但如果您坚持,您可以使用 CHECK 约束并在那里写下您允许的选择:

CREATE TABLE [dbo].[tester](
    [C1] [varchar](50) NULL,
    [C2] [varchar](50) NULL
) ON [PRIMARY]
GO

SET ANSI_PADDING ON
GO

ALTER TABLE [dbo].[tester] WITH CHECK ADD CONSTRAINT [CK_Rules] CHECK 
(
([C1]='address' AND [C2] IN ('home', 'secondary', 'primary')) OR
([C1]='phone' AND [C2] IN ('leisure', 'work', 'business'))
)
GO

ALTER TABLE [dbo].[tester] CHECK CONSTRAINT [CK_Rules]
GO

测试

INSERT INTO [dbo].[tester] ([C1],[C2]) VALUES 
('address', 'home'),
('address', 'primary'),
('phone', 'business');

返回:

(3 row(s) affected)

但是

INSERT INTO [dbo].[tester] ([C1],[C2]) 
VALUES ('phone', 'primary');

返回:

The INSERT statement conflicted with the CHECK constraint "CK_Rules". 
The conflict occurred in database "AdventureWorks2014", table "dbo.tester".
The statement has been terminated.

【讨论】:

  • 我们可能会按照您在开始时的建议使用 2 个单独的表格。我们仍在为这个答案而战。
【解决方案2】:

您可以使用CASE 语句并将其放在TRY CATCH

如您所见,我将表格中的列设置为不接受 NULL

DECLARE @tester TABLE
(
    [C1] [varchar](50) NOT NULL,
    [C2] [varchar](50) NOT NULL
)

DECLARE @col1 VARCHAR(20) = 'ADDRESS' , @col2 VARCHAR(20) = 'work'

BEGIN TRY
    INSERT @tester
    SELECT  CASE  WHEN @col1 = 'address' AND @col2 IN ('primary', 'secondary', 'home') THEN @col1
                  WHEN @col1 = 'phone' AND @col2 IN ('leisure', 'work', 'business') THEN @col1 ELSE NULL END
            ,CASE WHEN @col1 = 'address' AND @col2 IN ('primary', 'secondary', 'home') THEN @col2
                  WHEN @col1 = 'phone' AND @col2 IN ('leisure', 'work', 'business') THEN @col2 ELSE NULL END
END TRY

BEGIN CATCH

END CATCH

SELECT * FROM @tester

【讨论】:

    【解决方案3】:

    如果不想改变表结构,看另外两个答案。

    不过,更好的处理方法是创建一个包含可能组合的表格。示例(可以更好地选择名称):

    CREATE TABLE contact_cat(
        id INT NOT NULL,
        desc NOT NULL VARCHAR(128),
        CONSTRAINT PK_contact_cat PRIMARY KEY(id)
    );
    CREATE TABLE contact_sub(
        id INT NOT NULL,
        desc NOT NULL VARCHAR(128),
        CONSTRAINT PK_contact_sub PRIMARY KEY(id)
    );
    CREATE TABLE contact_style(
        cat_id INT NOT NULL,
        sub_id INT NOT NULL,
        CONSTRAINT PK_contact_style PRIMARY KEY(cat_id,sub_id),
        CONSTRAINT FK_contact_style_contact_cat FOREIGN KEY(cat_id) REFERENCES contact_cat(id),
        CONSTRAINT FK_contact_style_contact_sub FOREIGN KEY(sub_id) REFERENCES contact_sub(id)
    );
    CREATE TABLE tester(
        cat_id INT NOT NULL,
        sub_id INT NOT NULL,
        information VARCHAR(128) NOT NULL,
        CONSTRAINT FK_tester_contact_style FOREIGN KEY(cat_id,sub_id) REFERENCES contact_style(cat_id,sub_id)
    );
    

    用 cat + sub 填充表格,然后在contact_style 中允许组合:

    INSERT INTO contact_cat(id,desc)VALUES(1,'Address');
    INSERT INTO contact_cat(id,desc)VALUES(2,'Phone');
    INSERT INTO contact_sub(id,desc)VALUES(1,'Primary');
    INSERT INTO contact_sub(id,desc)VALUES(2,'Leisure');
    INSERT INTO contact_style(cat_id,sub_id)VALUES(1,1); -- Address + Primary
    INSERT INTO contact_style(cat_id,sub_id)VALUES(2,2); -- Phone + Leisure
    

    tester添加信息:

    INSERT INTO tester(cat_id,sub_id,information)VALUES(1,1,'NY, 120 Boulevard Av'); -- succeeds
    INSERT INTO tester(cat_id,sub_id,information)VALUES(2,2,'555 77 88 99'); -- succeeds
    INSERT INTO tester(cat_id,sub_id,information)VALUES(1,2,'Blablabla'); -- fails
    INSERT INTO tester(cat_id,sub_id,information)VALUES(2,1,'More blah'); -- fails
    

    前两个语句成功,因为外键存在于contact_style 中允许的联系人样式中。最后两个失败是因为contact_style 中不存在外键。

    这种工作方式的好处:

    • 无需每次都重复联系人类别+子类别描述。
    • 当这些表缓存在内存中时,可以节省磁盘空间和内存;表尽可能缓存在 SQL Server 中,以加快查询响应速度。
    • 当需要新类别或子类别时,您不必重写任何约束(参见其他答案)。只需在适当的表中添加新猫/子猫的行,将行添加到contact_style 以反映新的组合并快速完成。

    【讨论】:

    • PS:最好还是分开存储地址和电话号码。
    • TT.,您应该看到程序员对您的答案感到不安。 :) 由于您的建议,我们正在考虑添加一些表格。约束:CONSTRAINT FK_tester_contact_style FOREIGN KEY(cat_id,sub_id) REFERENCES contact_style(cat_id,sub_id) 让每个人都开心。更多的来......
    • @user2241789 =) 很高兴听到,让他们蠕动 ;-)。与使用约束相比,我增加了一项好处。
    猜你喜欢
    • 1970-01-01
    • 2019-05-22
    • 2013-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-16
    • 1970-01-01
    相关资源
    最近更新 更多