【问题标题】:Is it possible to delete from multiple tables in the same SQL statement?是否可以在同一个 SQL 语句中从多个表中删除?
【发布时间】:2013-07-06 12:34:07
【问题描述】:

可以使用连接语句来限定要删除的集合进行删除,例如:

DELETE J
FROM Users U
inner join LinkingTable J on U.id = J.U_id
inner join Groups G on J.G_id = G.id

WHERE G.Name = 'Whatever'
and U.Name not in ('Exclude list')

但是,我有兴趣删除连接条件的两边——LinkingTable 记录和它所依赖的用户记录。我无法打开级联,因为我的解决方案是首先使用实体​​框架代码,并且双向关系会产生多个级联路径。

理想情况下,我想要这样的东西:

DELETE J, U
FROM Users U
inner join LinkingTable J on U.id = J.U_id
...

从语法上讲,这行不通,但我很好奇这样的事情是否可能?

【问题讨论】:

  • 不行,一条 DML 语句只能影响一张表。您的选择是删除其中一个约束(并可能使用触发器强制执行它)。
  • 我认为您需要在问题中更清楚地说明您的外键指向两个方向。
  • @AaronBertrand:谢谢——这就是我一直在寻找的答案:DML 语句只能影响一个表,因此我想要的是不可能的。我们必须想办法解决这个问题。如果你写了一个答案,我会标记它。
  • @bwerks 由于Delete语句只影响一个表,您可以使用"deleted" pseudo table从多个表中删除值,通过加入来一一删除,请参阅下面的答案
  • @Aaron,并不是所有的 DML stmts 都是单表操作。例如在 select stmt 中使用 OUTPUT 子句,我们可以同时在 2 个表中执行 INSERT。对于删除是,除非启用 Cascde 删除是 SQL 服务器中的一个表 stmt。

标签: sql sql-server delete-row cascading-deletes


【解决方案1】:

你说的方式在MY SQL 中是可能的,但在SQL SERVER不是

您可以使用“已删除”伪表一次删除两个表中的值,例如,

 begin transaction;

 declare @deletedIds table ( samcol1 varchar(25) );

 delete #temp1
 output deleted.samcol1 into @deletedIds
 from #temp1 t1
 join #temp2 t2
 on t2.samcol1 = t1.samcol1

 delete #temp2
 from #temp2 t2
 join @deletedIds d
 on d.samcol1 = t2.samcol1;

 commit transaction;

简要说明可以看看这个Link

要了解已删​​除表的使用,您可以关注此Using the inserted and deleted Tables

【讨论】:

  • 您的意思是“您所说的方式在 MY SQL 中是可能的,但在 SQL SERVER 中不是”?
【解决方案2】:

如果您通过 T-SQL 创建外键,则必须将 ON DELETE CASCADE 选项附加到外键:

Code Snippet 

ALTER TABLE <tablename>
ADD CONSTRAINT <constraintname> FOREIGN KEY (<columnname(s)>)
REFERENCES <referencedtablename> (<columnname(s)>)

ON DELETE CASCADE;

【讨论】:

    【解决方案3】:

    在事务中使用 TRY CATCH

    BEGIN TRANSACTION
    BEGIN TRY
        DELETE from A WHERE id=1
    
        DELETE FROM b WHERE id=1
    
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
    ROLLBACK TRANSACTION;
    END CATCH
    

    或 您也可以使用 Store 过程 Using Stored Procedure With Transaction:

    【讨论】:

      【解决方案4】:

      我能想到的唯一方法是以过程的方式在逻辑上打破双向外键。

      如果您没有 visualization state 或 status 的一些标志,这种方法会对您的应用程序产生巨大影响

      有点像

      1. INSERT 虚拟行对用户不可见(类似 Id = -1 用于虚拟值)
      2. LinkingTable 中添加一个替代列以指向Users,我将其称为U_ComesFrom

        ALTER TABLE LinkingTagble ADD U_ComesFrom_U_id INT DEFAULT(-1)

      3. 添加FOREIGN KEYNOCHECK

        使用 NOCHECK 更改表 LinkingTable
        外键 (U_ComesFrom_U_id)
        参考用户 (Id) ;

      4. 添加到Users

        ALTER TABLE 用户添加 MarkedForDeletion BIT NOT NULL DEFAULT(0)

      那么你的 SQL 应该是这样的

      BEGIN TRANSACTION
          UPDATE J
          SET U_Comes_From_U_id = U_ID, U_id = -1 -- or some N/R value that you define in Users
          FROM Users U
          inner join LinkingTable J on U.id = J.U_id
          inner join Groups G on J.G_id = G.id
          WHERE G.Name = 'Whatever'
          and U.Name not in ('Exclude list')
      
          UPDATE U
          SET MarkedForDeletion = 1
          FROM Users
          inner join LinkingTable J on U.id = J.U_ComesFrom_U_id 
          WHERE U_id > 0
      
          DELETE FROM LinkingTable 
          WHERE U_ComesFrom_U_id > 0
      
          DELETE FROM Users
          WHERE MarkedForDeletion = 1
      
      COMMIT
      

      这种方法会影响性能,因为每个事务将是每个双向键至少 4 个 DML 操作。

      【讨论】:

        【解决方案5】:

        不,您需要运行多个语句。

        因为需要从两个表中删除,考虑创建一个匹配 id 的临时表:

        SELECT U.Id INTO #RecordsToDelete
        FROM Users U
           JOIN LinkingTable J ON U.Id = J.U_Id
        ...
        

        然后从每个表中删除:

        DELETE FROM Users 
        WHERE Id IN (SELECT Id FROM #RecordsToDelete)
        
        DELETE FROM LinkingTable
        WHERE Id IN (SELECT Id FROM #RecordsToDelete)
        

        【讨论】:

        • 我认为问题在于双向外键,在这种情况下,单独的语句也不起作用(鸡 + 蛋)。
        • 如果是这种情况,那么也许删除约束并在删除运行后将其添加回来也可以工作。
        • 我不认为 EF - Code First 允许您动态禁用/启用约束,而且它可能不是高并发环境中的最佳解决方案。如果您要一直关闭约束,那么打开它有什么意义?
        猜你喜欢
        • 1970-01-01
        • 2015-05-11
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        • 1970-01-01
        • 1970-01-01
        • 2018-11-26
        • 1970-01-01
        相关资源
        最近更新 更多