【问题标题】:SQL Delete Update Foreign Key Primary Key ConstraintSQL删除更新外键主键约束
【发布时间】:2011-11-20 18:58:49
【问题描述】:

假设我有一个表,其中已经包含“IsDelete Char(1)”列。 每当我为此表进行删除过程时, 我没有发出实际的删除命令。

eg. DELETE FROM TableName 

但我发出更新命令。

eg. UPDATE TableName SET IsDelete = '1' .....

因此,如果我想为这些表创建参照完整性,我不知道该怎么做。 因为我没有做出实际的删除命令。 请解释一下。

【问题讨论】:

    标签: mysql sql-server tsql ddl sql-delete


    【解决方案1】:

    如果您不希望从表中删除记录,请更改表的权限,以便除 sys_admin 之外的任何人都无法删除。

    通常,当您添加 IS_Deleted 列时,最好重命名表,然后使用仅选择活动记录的旧表名创建一个视图。这将防止你的很多代码被破坏。

    如果您希望在对表执行删除操作时进行更新,则可以通过触发器完成。在 SQL Server 中,触发器对整个批处理进行操作,因此请确保触发器可以处理多行删除。

    【讨论】:

      【解决方案2】:

      首先,IsDelete 列最好使用 bit 数据类型。

      至于参照完整性,您可以使用REFERENCES 以及不使用IsDelete 列。我看不出有什么问题。

      对于连接表,您可以使用:

      SELECT *
      FROM tbl1 
           JOIN tbl2 ON tbl1.id=tbl2.id 
                        and tbl1.IsDelete=0
                        and tbl2.IsDelete=0
      

      已编辑

      至于部门和员工。

      在现实生活中,关闭一个部门并不意味着解雇其员工。通常,您需要先将员工转移到另一个部门(或解雇他们),然后再关闭部门。在这种情况下,外键不会有任何问题。

      但在现实生活中,经常会发生部门先关门,然后管理层才考虑如何处理人的情况。

      在这种情况下,如果您有以下参考,您仍然不会有任何问题。

      ALTER TABLE [Employees]  WITH CHECK ADD  CONSTRAINT [FK_Employees_Departments] FOREIGN KEY([Department_id])
      REFERENCES [Departments] ([Department_id])
      

      【讨论】:

      • 我想说的是让我们假设有 2 个表,分别称为 Department 和 Employee。两个表都有 IsDelete 列。因此,如果我首先删除部门“例如,更新部门设置 Department.IsDelete = 1 where ....”。如果 Employee 中已存在已删除的部门,则可能会破坏 Department 和 Employee 之间的引用完整性。因此,在这种情况下,我该如何进行参照完整性约束。因为我不想写那么多代码来检查。
      • 查看 Bogdan Sahlean 的回答,他举了很好的例子。
      【解决方案3】:

      检查这个用 SQL Server 测试的声明式解决方案(参见源代码中的 cmets):

      CREATE TABLE dbo.SalesOrder
      (
          SalesOrderID INT IDENTITY(1,1)
          ,OrderDate DATETIME NOT NULL
          ,IsDeleted CHAR(1) NOT NULL DEFAULT 'N'
          ,CONSTRAINT PK_SalesOrder PRIMARY KEY (SalesOrderID)
          ,CONSTRAINT CK_SalesOrder_IsDeleted CHECK(IsDeleted IN ('Y','N'))
      );
      
      CREATE TABLE dbo.SalesOrderDetail
      (
          SalesOrderDetailID INT IDENTITY(1,1)
          ,Qty DECIMAL(8,2) NOT NULL
          ,UnitPrice DECIMAL(8,2) NOT NULL
          ,IsDeleted CHAR(1) NOT NULL DEFAULT 'N'
          ,SalesOrderID INT NOT NULL
      );
      
      --We need this index to create the next foreign key constraint
      CREATE UNIQUE NONCLUSTERED INDEX IUN_SalesOrder_SalesOrderID_IsDeleted
      ON  dbo.SalesOrder(SalesOrderID, IsDeleted);
      
      --If we "delete" (UPDATE dbo.SalesOrder SET IsDeleted = "Y" ...) a row from dbo.SalesOrder table, 
      --then this modification (... SET IsDeleted = "Y" ...) will be propagated to dbo.SalesOrderDetail table
      --because of ON UPDATE CASCADE clause
      ALTER TABLE dbo.SalesOrderDetail
      ADD CONSTRAINT FK_SalesOrderDetail_SalesOrder_SalesOrderID_IsDeleted
      FOREIGN KEY (SalesOrderID, IsDeleted) REFERENCES dbo.SalesOrder(SalesOrderID, IsDeleted)
      ON UPDATE CASCADE;
      
      INSERT  dbo.SalesOrder (OrderDate)
      SELECT  '20110101'
      UNION ALL
      SELECT  '20110202'
      UNION ALL
      SELECT  '20110303';
      
      INSERT  dbo.SalesOrderDetail (Qty, UnitPrice, SalesOrderID)
      SELECT  1,10,1 UNION ALL SELECT 1,11,1 UNION ALL SELECT 1,12,1
      UNION ALL 
      SELECT 2,20,2
      UNION ALL 
      SELECT 3,30,3 UNION ALL SELECT 3,31,2;
      
      SELECT  *
      FROM    dbo.SalesOrder
      SELECT  *
      FROM    dbo.SalesOrderDetail 
      
      --Test "DELETE"/UPDATE statement
      UPDATE  dbo.SalesOrder 
      SET     IsDeleted = 'Y'
      WHERE   SalesOrderID = 1;
      
      --Now, we can check SalesOrderDetail rows ([Status] values WHERE [SalesOrderID]=1)
      SELECT  *
      FROM    dbo.SalesOrder
      SELECT  *
      FROM    dbo.SalesOrderDetail 
      
      DROP TABLE dbo.SalesOrderDetail;    
      DROP TABLE dbo.SalesOrder;
      

      【讨论】:

      • 假设我将您的代码更改为 FOREIGN KEY (SalesOrderID, IsDeleted) REFERENCES dbo.SalesOrder(SalesOrderID, IsDeleted) ON UPDATE NO ACTION;因为我只想禁止 SaleOrder 表的删除操作。然后,SaleOrder 表是好的,因为它在删除时返回外键限制消息(SET IsDeleted = 'Y')。但不幸的是 SaleOrderDetail 无法删除 (SET IsDeleted = 'Y')。因此,如果您有其他解决方案,请给我建议。
      • 是的@Bogdan 我已经按照你所说的运行了 UPDATE dbo.SalesOrderDetail SET IsDeleted = 'Y' WHERE SalesOrderID = 1;但我收到消息“UPDATE 语句与 REFERENCE 约束“FK_SalesOrderDetail_SalesOrder_SalesOrderID_IsDeleted”冲突。冲突发生在数据库“DBTest_MT”、表“dbo.SalesOrderDetail”中。“
      • 如果您将 FK 定义从 ON UPDATE CASCADE 更改为 ON UPDATE NO ACTION,那么您将收到该错误。这个新规范 (ON UPDATE NO ACTION) 与参照完整性概念本身存在冲突/矛盾。例如,你不能有两个 SalesOrderDetail(SalesOrderID=101; IsDedeleted='Y'; Qty=10 & SalesOrderID=101; IsDedeleted='Y'; Qty=20)SalesOrder(SalesOrderID=101; IsDeleted='N')
      • 如果你想“删除”/更新自己在两个表中的记录(没有CASCADE)你必须改变FK定义:从FOREIGN KEY (SalesOrderID, IsDeleted) REFERENCES dbo.SalesOrder(SalesOrderID, IsDeleted)FOREIGN KEY (SalesOrderID) REFERENCES dbo.SalesOrder(SalesOrderID)。在最后一种情况下,您可以使用 IsDeleted='N' 的 SalesOrderID 和 IsDeleted='Y' 的对应 SalesOrderDetailID 行。 如果您想得到准确的答案,也许会更好地为您的问题添加新的细节。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多