当前位置: 首页 > 面试题库 >

MSSQL:更新语句避免了CHECK约束

骆利
2023-03-14
问题内容

在MS2000中工作,我有一个名为JobOwners的表,该表将Jobs(JPSID)映射到拥有它们的雇员(EmpID)。它还包含他们开始拥有该工作的日期(DateStarted),他们停止拥有该工作的日期(DateEnded)以及所有权是否处于活动状态(IsActive)。看起来像这样。

CREATE TABLE JobOwners
(
    LogID int NOT NULL IDENTITY(1,1) PRIMARY KEY,
    JPSID int NOT NULL FOREIGN KEY REFERENCES JobsPerShift(JPSID),
    EmpID int NOT NULL FOREIGN KEY REFERENCES Employees(EmpID),
    DateStarted datetime,
    DateEnded datetime,
    IsActive tinyint NOT NULL   
)

尽管没有活动的重复应该没问题,但不应有活动的JPSID重复。通过一些研究,我发现可以使用CHECK约束上的函数来完成此操作。

CREATE FUNCTION CheckActiveCount(@JPSID INT) 
RETURNS INT AS 
BEGIN
    DECLARE @result INT
    SELECT @result = COUNT(*) FROM JobOwners WHERE JPSID = @JPSID AND IsActive = 1
    RETURN @result
END
GO

ALTER TABLE JobOwners 
 ADD CONSTRAINT CK_JobOwners_IsActive
 CHECK ((IsActive = 1 AND dbo.CheckActiveCount(JPSID) <= 1) OR (IsActive = 0))

这足够好用。由于没有其他活动的JPSID 2,它将允许我插入具有IsActive 1的JPSID2。它将使我能够插入具有IsActive 0的JPSID
2,因为当IsActive为0时不应用该检查。但是,再次将JPSID 2与IsActive 1插入,因为它与约束冲突。见下文。

INSERT INTO JobOwners
 VALUES(2,2,NULL,NULL,1)

(1 row(s) affected)

INSERT INTO JobOwners
 VALUES(2,2,NULL,NULL,0)

(1 row(s) affected)

INSERT INTO JobOwners
 VALUES(2,3,NULL,NULL,1)

INSERT statement conflicted with COLUMN FOREIGN KEY constraint...

如果我尝试将不活动的记录之一更新为活动,则会出现问题。由于某种原因,它允许我。

UPDATE JobOwners SET IsActive = 1
 WHERE LogID = 3

(1 row(s) affected)

如果我再次运行同一条语句,则它与约束冲突,但不是第一次冲突。这个应用程序的前端永远不会将非活动记录更改为活动记录,它只会插入一个新记录,但这仍然不是我希望该表允许的内容。

我想知道是否最好将活跃的工作负责人分开,并为工作负责人的历史提供一个单独的表,但是我不确定这里的最佳做法。任何帮助将不胜感激。

谢谢你,


问题答案:

存在一个已知问题,其中某些操作将导致检查约束,从而导致绕过UDF。该错误已在Connect上列出(在被破坏之前,所有链接都被孤立),并且已经得到确认,但由于无法修复而被关闭。这意味着我们需要依靠变通办法。

我的第一个解决方法可能是代替更新触发器。感谢Martin使我保持诚实并进一步进行了测试-
我发现我无法防止在同一条语句中将两行更新为1。我已经纠正了逻辑并添加了一个事务来帮助防止出现竞争情况:

CREATE TRIGGER dbo.CheckJobOwners ON dbo.JobOwners
INSTEAD OF UPDATE
AS
BEGIN
  SET NOCOUNT ON;
  BEGIN TRANSACTION;

  UPDATE j SET IsActive = 1 -- /* , other columns */
    FROM dbo.JobOwners AS j INNER JOIN inserted AS i
    ON i.LogID = j.LogID
    WHERE i.IsActive = 1 AND NOT EXISTS 
    (    -- since only one can be active, we don't need an expensive count:
      SELECT 1 FROM dbo.JobOwners AS j2
        WHERE j2.JPSID = i.JPSID
        AND j2.IsActive = 1 AND j2.LogID <> i.LogID
    )
    AND NOT EXISTS 
    (    -- also need to protect against two rows updated by same statement: 
      SELECT 1 FROM inserted AS i2
        WHERE i2.JPSID = i.JPSID
        AND i2.IsActive = 1 AND i2.LogID <> i.LogID
    );

  -- *if* you want to report errors:
  IF (@@ROWCOUNT <> (SELECT COUNT(*) FROM inserted WHERE IsActive = 1))
    RAISERROR('At least one row was not updated.', 11, 1);

  -- assume setting active = 0 always ok & that IsActive is not nullable
  UPDATE j SET IsActive = 0 -- /* , other columns */
    FROM dbo.JobOwners AS j INNER JOIN inserted AS i
    ON j.LogID = i.LogID
    WHERE i.IsActive = 0;

  COMMIT TRANSACTION;
END
GO

(我之所以选择代替触发器而不是代替触发器的唯一原因是,您只更新需要更新的行,而不必在事实发生后回滚(这不会让您仅在无效情况下回滚无效的更新)。
-行更新))。

关于此问题,这里有很多很好的讨论:

https://web.archive.org/web/20171013131650/http://sqlblog.com/blogs/tibor_karaszi/archive/2009/12/17/be-
careful-with-constraints-calling-
udfs.aspx



 类似资料:
  • 问题内容: INSERT语句与CHECK约束“ ”冲突。在数据库“ C:\ DOCUMENTS AND SETTINGS \ KARTHIKEYAN \ DESKTOP \ KOK \ DB \ INFT3009_ASS1_C3104855.MDF”的表“ dbo.Members”的列“ ”中发生了冲突。该语句已终止。 我在Visual Studio 2008 Express中使用.MDF文件。我

  • 问题内容: 我有2个表格,Products和ShoppingCart,我想根据ShoppingCart中指定的产品名称和数量来更新和减少Products表中产品的“数量”。我怎样才能做到这一点? 表:产品字段:产品名称,产品数量 表:购物车字段:ProductName,ProductQty 访问数据库 问题答案: 您应该有一个产品ID。然而: 您还应该在字段和表名中删除空格。

  • 我想更新表中几行的主键。如果所有行都已更新,则键将再次是唯一的,但第一行的更新会导致与第二行的键发生临时冲突。有没有优雅的方法来解决这个问题? 例子: 错误:重复的键值违反了唯一约束“pk_erichtest”

  • 问题内容: 我有一个方法可以依次调用其他4种方法来检查特定条件,并且每当一个方法返回Truthy时立即返回(而不检查以下方法)。 这似乎是很多行李代码。与其执行每行2行的if语句,不如执行以下操作: 但这是无效的Python。我在这里错过了一个简单,优雅的解决方案吗?顺便说一句,在这种情况下,这四种检查方法可能很昂贵,因此我不想多次调用它们。 问题答案: 您可以使用循环: 这样做还有一个好处,就是

  • 问题内容: 我正在尝试编写一个Java程序,该程序根据用户选择的内容初始化某些布局。我想做的是尝试避免编写一堆if语句,以便在需要添加更多布局的情况下可以对代码进行伸缩以供将来使用。我听说实现此目标的最佳方法是使用多态性,但是我对多态性的理解仍然有些模糊。 说我要实现这种情况: 我当时在考虑为类的实现创建一个接口。令我困惑的是它在main()中是如何工作的,我是否仍然不需要条件if或switch语

  • 问题内容: 我已经编写了类似这样的方法。但是我猜这应该进行重构。谁能建议最好的方法来避免使用多个if语句? 我在这里不能使用大小写转换,因为我的“条件”是。 问题答案: 您可以使用来保存您的解决方案: 为了使此代码起作用,您需要像在类中那样进行定义和定义,如下所示: