3

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

  • 此表每个中心/类别只能有一个 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

4 回答 4

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)
于 2010-03-26T00:37:12.757 回答
2

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

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

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 解决方案快得多。

于 2010-03-26T03:32:11.983 回答
0

您也可以使用类似的方式触发触发器(这实际上是矫枉过正 - 您可以通过假设数据库已经处于有效状态来使其更清洁 - 即 UNION 而不是 UNION all 等):

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
于 2010-03-26T00:42:08.380 回答
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:对不起我之前的(不正确的)答案。我没有仔细阅读这个问题。

于 2010-03-26T01:34:36.463 回答