我可以在一个语句中删除两个表中的条目吗?

jam*_*art 3 sql sql-server sql-server-2012

我必须从两个表中的每一个中删除一行,它们由一个 ID 链接但没有正确的 PK - FK 关系(这个数据库没有外键!)

这些表具有假定的 1-1 关系。我不知道为什么它们不只是放在同一张桌子上,但我不能随意更改它。

人们

PersonId | Name | OwnsMonkey
----------------------------
    1       Jim       true
    2       Jim       false
    3       Gaz       true
Run Code Online (Sandbox Code Playgroud)

信息

PersonId |    FurtherInfo
-----------------------------
    1       Hates his monkey
    2        Wants a monkey
    3       Loves his monkey
Run Code Online (Sandbox Code Playgroud)

要决定删除什么,我必须找到一个用户名以及他们是否拥有一只猴子:

Select PersonId from People where Name = 'Jim' and OwnsMonkey = 'false'
Run Code Online (Sandbox Code Playgroud)

所以我使用这个想法做两个单独的陈述,Info首先删除,然后从People

delete from Info where PersonId = (Select PersonId from People where Name = 'Jim' and OwnsMonkey = 'false');

delete from People where PersonId = (Select PersonId from People where Name = 'Jim' and OwnsMonkey = 'false');
Run Code Online (Sandbox Code Playgroud)

在 StackOverflow 上找到了一个有希望的答案

delete      a.*, b.*
from        People a
inner join  Info b
where       a.People = b.Info
            and a.PersonId = 
            (Select PersonId from People where Name = 'Jim' and OwnsMonkey = 'false')
Run Code Online (Sandbox Code Playgroud)

但是它在 Sql Server (2012) 中给出了语法错误,我也尝试了没有别名的情况,但似乎不可能同时删除两个表

Vla*_*nov 5

我可以在一个语句中删除两个表中的条目吗?

不能。一条语句只能从 MS SQL Server 中的一个表中删除行。

您所指的答案是关于 MySQL 的,而 MySQL 确实允许使用一条语句从多个表中删除,如MySQL 文档中所示。MS SQL Server 不支持这一点,可以在docs 中看到。DELETE在 SQL Server的语句中没有包含多个表的语法。如果您尝试从视图而不是表中删除,也有一个限制:

table_or_view_name 引用的视图必须是可更新的,并且在视图定义的 FROM 子句中引用了一个基表。


我希望避免在偶然情况下出现两个单独的语句,第二个语句由于任何原因都不起作用,中断了 - 真的是并发,我猜 TRY/CATCH 会很好地工作。

这就是交易的目的。您可以在一个事务中放置多个语句,它们要么全部成功,要么全部失败。要么全有,要么全无。

在您的情况下,您不仅可以,而且应该将两个DELETE语句放在一个事务中。

TRY/CATCH 有助于以更可控的方式处理可能的错误,但主要概念是“事务”。

BEGIN TRANSACTION

delete from Info where PersonId = (Select PersonId from People where Name = 'Jim' and OwnsMonkey = 'false');

delete from People where PersonId = (Select PersonId from People where Name = 'Jim' and OwnsMonkey = 'false');

COMMIT
Run Code Online (Sandbox Code Playgroud)

我强烈建议阅读Erland Sommarskog 撰写的一篇很棒的文章SQL Server 中错误和事务处理


如果你想变得棘手,像这样:

WITH
CTE
AS
(
    SELECT
        Info.PersonId AS ID1, People.PersonId AS ID2
    FROM
        Info
        INNER JOIN People ON Info.PersonId = People.PersonId
)
DELETE FROM CTE
WHERE ID1 = 1;
Run Code Online (Sandbox Code Playgroud)

你会得到一个错误:

视图或函数“CTE”不可更新,因为修改会影响多个基表。

或者像这样:

WITH
CTE
AS
(
    SELECT
    PersonId
    FROM Info

    UNION ALL

    SELECT
    PersonId
    FROM People
)
DELETE FROM CTE
WHERE PersonId = 1;
Run Code Online (Sandbox Code Playgroud)

你会得到另一个错误:

视图“CTE”不可更新,因为定义包含 UNION 运算符。

  • 当事务中有多个DML语句时,需要SET XACT_ABORT ON才能完全回滚 (2认同)
  • @jamheadart因为MySQL和SQL Server有不同的sql方言、不同的功能和不同的规则。两者支持的功能有很多重叠,但也存在重大差异。 (2认同)