是否可以为此规则中的数据库约束?

时间:2010-03-26 00:25:22

标签: sql-server check-constraints

我希望确保我的数据具有以下检查(约束?)的约束

  • 此表每个集线器/类别只能有一个BorderColour。 (例如#FFAABB)
  • 但它可以有多个空值。 (对于此字段,所有其他行都为空)

表架构

ArticleId INT PRIMARY KEY NOT NULL IDENTITY
HubId TINYINT NOT NULL
CategoryId INT NOT NULL
Title NVARCHAR(100) NOT NULL
Content NVARCHAR(MAX) NOT NULL
BorderColour VARCHAR(7) -- Can be nullable.

我在猜我是否必须制定检查限制?但我不确定如何等等。

样本数据。

1, 1, 1, 'test', 'blah...', '#FFAACC'
1, 1, 1, 'test2', 'sfsd', NULL
1, 1, 2, 'Test3', 'sdfsd dsf s', NULL
1, 1, 2, 'Test4', 'sfsdsss', '#AABBCC'

现在..如果我添加以下行,我应该得到一些SQL错误....

INSERT INTO tblArticle VALUES (1, 2, 'aaa', 'bbb', '#ABABAB')

任何想法?

4 个答案:

答案 0 :(得分:3)

CHECK约束通常应用于单行,但是,您可以使用UDF作弊:

CREATE FUNCTION dbo.CheckSingleBorderColorPerHubCategory
(
    @HubID tinyint,
    @CategoryID int
)
RETURNS BIT
AS BEGIN
    RETURN CASE
        WHEN EXISTS
        (
            SELECT HubID, CategoryID, COUNT(*) AS BorderColorCount
            FROM Articles
            WHERE HubID = @HubID
                AND CategoryID = @CategoryID
                AND BorderColor IS NOT NULL
            GROUP BY HubID, CategoryID
            HAVING COUNT(*) > 1
        ) THEN 1
        ELSE 0
    END
END

然后创建约束并引用UDF:

ALTER TABLE Articles
ADD CONSTRAINT CK_Articles_SingleBorderColorPerHubCategory
CHECK (dbo.CheckSingleBorderColorPerHubCategory(HubID, CategoryID) = 1)

答案 1 :(得分:2)

如果您运行SQL2008,则可以使用另一个可用选项。此版本的SQL具有称为筛选索引的功能。

使用此功能,您可以创建一个唯一索引,其中包括除BorderColour为null之外的所有行。

CREATE TABLE [dbo].[UniqueExceptNulls](
    [HubId] [tinyint] NOT NULL,
    [CategoryId] [int] NOT NULL,
    [BorderColour] [varchar](7) NULL,
)

GO

CREATE UNIQUE NONCLUSTERED  INDEX UI_UniqueExceptNulls
ON [UniqueExceptNulls] (HubID,CategoryID)
WHERE BorderColour IS NOT NULL

这种方法比我在其他答案中的方法更清晰,因为它不需要创建额外的计算列。它也不要求你在表中有一个唯一的列,尽管你应该拥有它。

最后,它也将比UDF / Check Constraint解决方案快得多。

答案 2 :(得分:0)

你也可以用这样的东西做一个触发器(这实际上是矫枉过正的 - 你可以通过假设数据库已经处于有效状态 - 即UNION而不是UNION等来使它更清晰):

IF EXISTS (
    SELECT COUNT(BorderColour)
    FROM (
             SELECT INSERTED.HubId, INSERTED.CategoryId, INSERTED.BorderColour
             UNION ALL
             SELECT HubId, CategoryId, BorderColour
             FROM tblArticle
             WHERE EXISTS (
                 SELECT *
                 FROM INSERTED
                 WHERE tblArticle.HubId = INSERTED.HubId
                       AND tblArticle.CategoryId = INSERTED.CategoryId
             )
    ) AS X
    GROUP BY HubId, CategoryId
    HAVING COUNT(BorderColour) > 1
)
RAISEERROR

答案 3 :(得分:0)

如果表中有唯一列,则可以通过在计算机列上创建唯一约束来完成此操作。

以下示例创建了一个表,其行为与您在需求中描述的一样,并且应该比基于UDF的检查约束执行得更好。您还可以通过使计算列保持不变来进一步提高性能。

CREATE TABLE [dbo].[UQTest](
    [Id] INT IDENTITY(1,1) NOT NULL,
    [HubId] TINYINT NOT NULL,
    [CategoryId] INT NOT NULL,
    [BorderColour] varchar(7) NULL,
    [BorderColourUNQ]  AS (CASE WHEN [BorderColour] IS NULL 
                               THEN cast([ID] as varchar(50))
                               ELSE cast([HuBID] as varchar(3)) + '_' + 
                                    cast([CategoryID] as varchar(20)) END
                           ),
 CONSTRAINT [UQTest_Unique] 
 UNIQUE  ([BorderColourUNQ])
) 

上述实现的一个可能不合需要的方面是它允许类别/集线器同时定义Null和颜色。如果这是一个问题,请告诉我,我会调整我的答案来解决这个问题。

PS:对我以前的(不正确的)答案感到抱歉。我没有仔细阅读这个问题。