SQL 数据库中的约束

发布于 2024-09-26 17:44:31 字数 481 浏览 1 评论 0原文

我需要在 T-SQL 中有一个表,该表将具有以下结构:

KEY     Various_Columns       Flag
 1          row 1              F
 2          row_2              F
 3          row_3              T
 4          row_4              F

要么没有行,要么最多一行可以具有值为 T 的 Flag 列。我的开发人员声称这可以通过检查约束来实现放在桌子上。

问题:

  1. 是否可以在数据库级别将这样的约束放在数据库本身上(即行间约束),而不是放在更新或插入行的业务规则中
  2. 这样的表是否为正常形式?
  3. 或者正常形式需要删除 Flag 列,取而代之的是(比如说)另一个简单的表或变量,其中包含具有 Flag=T 的行的值,即在上面的情况下 row=3。

I need to have a table in T-SQL which will have the following structure

KEY     Various_Columns       Flag
 1          row 1              F
 2          row_2              F
 3          row_3              T
 4          row_4              F

Either no rows, or at most one row can have the Flag column with the value T. My developer claims that this can be achieved with a check constraint placed on the table.

Questions:

  1. Can such a constraint be placed on the database itself (ie an inter-row constraint) at the database level, rather than in business rules for updating or inserting rows
  2. Is such a table in normal form?
  3. Or would normal form require removing the Flag column, and instead (say) had another simple table or variable containing the value of row which had Flag=T, ie in the above case row=3.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

哥,最终变帅啦 2024-10-03 17:44:31

1 否。检查约束是每行。也没有其他约束可以做到这一点。

您需要以下之一:

  • (所有版本)
  • 带有筛选器 Flag = T 的触发器
  • 索引视图,以及 Flag 上的唯一索引 (SQL Server 2000+)筛选索引 (SQL Server 2008)

2 足够好

3 确实太过分了。您将相同的数据分开以避免上述解决方案之一。但使用单行表、ID 列的 FK 以及 Flag 上的唯一约束

1 No. A check constraint is per row. No other constraint will do this either.

You need one of:

  • a trigger (all versions)
  • indexed view with filter Flag = T, and unique index on Flag (SQL Server 2000+)
  • filtered index (SQL Server 2008)

2 Good enough

3 Overkill really. You're splitting the same data up to avoid one the solutions above. But using a one row table, FK for the ID columns, and a unique constraint on Flag

_蜘蛛 2024-10-03 17:44:31

我的开发人员声称这可以
通过检查约束实现
放在桌子上。

SQL Server 不直接**支持 CHECK 约束中的子查询(这是完整 SQL-92 的要求;一般来说,SQL Server 仅符合入门级 SQL-92)。

虽然几乎肯定有更好的方法在 SQL Server 中强制执行此约束,但纯粹出于兴趣,它确实可以使用行级 CHECK 约束和 UNIQUE 约束来实现,例如这是一种方法:

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL, 
 Flag CHAR(1) DEFAULT 'F' NOT NULL
    CHECK (Flag IN ('F', 'T')), 
 Flag_key INTEGER UNIQUE, 
 CHECK (
        (Flag = 'F' AND Flag_key = key_col)
        OR
        (Flag = 'T' AND Flag_key = NULL)
       )
);

这里的问题是您需要“手动”维护 Flag_key 列的值。将列 + CHECK 替换为计算列将意味着自动维护值:

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL, 
 Flag CHAR(1) DEFAULT 'F' NOT NULL
    CHECK (Flag IN ('F', 'T')), 
 Flag_key AS (
              CASE WHEN Flag = 'F' THEN key_col
                   ELSE NULL END
             ), 
 UNIQUE (Flag_key)
);

** 虽然 SQL Server 不直接支持 CHECK 中的子查询约束,在某些情况下有一个使用用户定义函数 (UDF) 的解决方法,例如请

CREATE FUNCTION dbo.CountTFlags ()
RETURNS INTEGER
AS
BEGIN
DECLARE @return INTEGER;
SET @return = (
               SELECT COUNT(*)
                 FROM YourStuff
                WHERE Flag = 'T'
              );
RETURN @return;
END;

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL, 
 Flag CHAR(1) DEFAULT 'F' NOT NULL
    CHECK (Flag IN ('F', 'T')), 
 CHECK (1 >= dbo.CountTFlags())
);

注意,UDF 方法并非在所有情况下都有效,因此需要谨慎。重要的一点是,将为受影响的每一行评估 UDF(而不是如您所期望的那样在 SQL 语句或事务级别评估)。在这种情况下,约束对于受影响的每一行都必须成立,因此——我认为! ——很安全。有关更多详细信息,请参阅 CHECK 约束问题作者:大卫·波塔斯


就我个人而言,我会简单地使用第二个表来建模Flag,它只涉及键和外键,例如

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL
);

CREATE TABLE YourStuffFlag
(
 key_col INTEGER NOT NULL UNIQUE
    REFERENCES YourStuff (key_col)
);

[我的]表是正常形式吗?

您应该以第五范式(5NF)为目标。您是否实现了这一点取决于 Various_Columns 的设计。我不认为您的 Flag 不符合 5NF 的要求,并且我没有看到任何更新、删除或插入异常(这是规范化的要点,但 5NF 设计仍然可能表现出异常)。也就是说,要切换获取 flag 属性的行,我的双表设计需要一个 UPDATE 语句,而您的单表设计则需要两个;)

My developer claims that this can be
achieved with a check constraint
placed on the table.

SQL Server does not directly** support subqueries in CHECK constraints (a requirement for Full SQL-92; SQL Server is only compliant with Entry Level SQL-92, broadly speaking).

While there are almost certainly better ways of enforcing this constraint in SQL Server, purely out of interest it can indeed be achieved using a row-level CHECK constraint and a UNIQUE constraint e.g. here's one way:

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL, 
 Flag CHAR(1) DEFAULT 'F' NOT NULL
    CHECK (Flag IN ('F', 'T')), 
 Flag_key INTEGER UNIQUE, 
 CHECK (
        (Flag = 'F' AND Flag_key = key_col)
        OR
        (Flag = 'T' AND Flag_key = NULL)
       )
);

The issue here is that you will need to maintain the Flag_key column's values 'manually'. Replacing the column + CHECK with a calculated column would mean the values are maintained automatically:

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL, 
 Flag CHAR(1) DEFAULT 'F' NOT NULL
    CHECK (Flag IN ('F', 'T')), 
 Flag_key AS (
              CASE WHEN Flag = 'F' THEN key_col
                   ELSE NULL END
             ), 
 UNIQUE (Flag_key)
);

** While SQL Server does not directly support subqueries in CHECK constraints, there is a workaround in some cases using a user defined function (UDF) e.g.

CREATE FUNCTION dbo.CountTFlags ()
RETURNS INTEGER
AS
BEGIN
DECLARE @return INTEGER;
SET @return = (
               SELECT COUNT(*)
                 FROM YourStuff
                WHERE Flag = 'T'
              );
RETURN @return;
END;

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL, 
 Flag CHAR(1) DEFAULT 'F' NOT NULL
    CHECK (Flag IN ('F', 'T')), 
 CHECK (1 >= dbo.CountTFlags())
);

Note that the UDF approach won't work in every case and that caution is required. The important point is that UDF will be evaluated for each row affected (rather than at the SQL statement or transaction level, as you may expect). In this case, the constraint needs to be true for every row affected and therefore -- I think! -- it is safe. For more details, see Trouble with CHECK Constraints by David Portas.


Personally, I would simply use a second table to model Flag, which would only involve keys and a foreign key e.g.

CREATE TABLE YourStuff
(
 key_col INTEGER NOT NULL UNIQUE, 
 Various_Columns VARCHAR(8) NOT NULL
);

CREATE TABLE YourStuffFlag
(
 key_col INTEGER NOT NULL UNIQUE
    REFERENCES YourStuff (key_col)
);

Is [my] table in normal form?

You should by aiming for Fifth normal form (5NF). Whether you have achieved this depends upon the design of Various_Columns. I do not believe that your Flag falls fowl of the requirements for 5NF and I do not see any update, delete or insert anomalies (which is the point of normalization but a 5NF design can still exhibit anomalies). That said, to switch the row that gets the flag attibute, my two-table design requires a single UPDATE statement while your single-table design requires two ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文