【问题标题】:Delete data from child tables从子表中删除数据
【发布时间】:2013-08-01 16:11:33
【问题描述】:

我有 2 张桌子:

“客户”和“地址”。一个客户可以有多个地址,因此它们具有“n:m”关系。 出于这个原因,我还有表“customer-addr”。

这就是我的表格的样子:

                    +---------------+       
+-----------+       | customer_addr |       
| customers |       +---------------+       +-----------+
+-----------+       | id            |       | addresses |
| id        | <---> | cid           |       +-----------+
| name      |       | aid           | <---> | id        |
+-----------+       +---------------+       | address   |
                                            +-----------+

我需要更新所有客户数据,包括。所有地址。出于这个原因,我考虑先删除所有现有地址,然后更新客户表,然后创建新地址。

我的问题:如何有效地删除一位客户的所有现有地址? (我必须从 2 个表中删除行)。

我可以使用单一语句吗? (没有cascade-method,风险太大)

或者我可以用 2 个语句而不使用子选择吗?

最好的方法是什么?

请注意,我使用的是 postgresql

编辑:

我的整个数据库设计更复杂,地址表不仅来自“客户”,还来自“供应商”、“bulkbuyers”,..

每个地址只属于一位客户或一位供应商或一位大宗采购商。 (没有地址被多个家长使用/没有地址共享)

客户/供应商/.. 可以有多个地址。

因此,来自 zebediah49 的已编辑解决方案将不起作用,因为它还会删除每个供应商/bulkbuyer/...的所有地址。

【问题讨论】:

  • 如果都是数据,你不能把customer_addraddresses的全部内容都扔掉吗?编辑:好的,你想要一个客户。编辑解决了我的问题;谢谢。
  • 我只想删除一个特定客户的数据。而且我只知道客户的id
  • 地址可以属于2+客户吗?
  • 在您完成所有这些操作后,您希望您的数据有何不同?
  • @Dan Bracuk:每次操作后,可能会发生以下变化:(1)客户数据(例如其他客户名称)(2)地址数据(例如其他街道)( 3)新地址添加(4)旧地址删除(5)1-4的组合。因此,在此操作之后,与客户相关的所有内容都可能发生变化

标签: sql postgresql foreign-keys sql-delete


【解决方案1】:

我会在 PostgreSQL 9.1 或更高版本中使用writable CTE,也称为数据修改 CTE:

WITH del AS (
   DELETE FROM customer_addr
   WHERE  cid = $kill_this_cid
   RETURNING aid
   )
DELETE FROM addresses a
USING  (SELECT DISTINCT aid FROM del) d
WHERE  a.id = d.aid;

这应该是最快和最安全的。

如果(cid, aid)customer_addr 中定义为UNIQUE,则不需要DISTINCT 步骤:

...
DELETE FROM addresses a
USING  del d
WHERE  a.id = d.aid;

【讨论】:

  • 哇,我不知道它是如何工作的,但它正是我想要的。我会研究一下它是如何工作的,感谢您的快速回答
  • @maja:您的研究可以很简单,只需点击我包含的手册的链接即可。 ;)
  • @maja:关于您被拒绝的编辑:子查询需要有别名。但是我对列名id 应用了您的修复以适应问题,即使我鄙视这一点。 Why?
  • 你说得对,没想到。我对其进行了编辑,以达到每次编辑至少 6 个字符更改。但是“AS”之后的左括号仍然缺失
【解决方案2】:

编辑:

知道了;这更安全,因为无论如何都有两个客户共享地址的风险:

DELETE FROM customer_addr WHERE cid = $TARGET_CID;
DELETE FROM addresses WHERE id NOT IN (SELECT aid FROM customer_addr);

首先,删除所有引用,然后删除所有未引用的地址。 请注意,例如,您可以只执行第一步,稍后再运行“清理”第二步。


我建议两步交易:

DELETE FROM addresses WHERE id IN (SELECT ca.aid FROM customers c LEFT JOIN customer_addr ca ON ca.cid=c.id WHERE c.name='$NAME_TO_DELETE');
DELETE FROM customer_addr WHERE cid = (SELECT id FROM customers WHERE name='$NAME_TO_DELETE');

如果您已经有客户 ID(编辑:您有),您可以跳过大部分内容:

DELETE FROM addresses WHERE id IN (SELECT aid FROM customer_addr WHERE cid=$TARGET_CID);
DELETE FROM customer_addr WHERE cid = $TARGET_CID;

用适当的事务性BEGIN/END 包装它们,以确保您不会最终处于不一致的状态,并且应该设置好。

【讨论】:

  • 这行不通,因为我不能从地址中删除数据,只要 customer_addr 中的相应行存在。 customer_addr 条目仍然指向地址(外键约束)
  • 单笔交易也不行?有趣的;我会看看我是否能想出一个替代方案。
  • 不幸的是,这也不起作用,请参阅我编辑的问题:(
  • 您始终可以使用NOT IN (... UNION ... ),但这为错误留下了更多空间,如果您添加新的依赖表,稍后会进一步更新。鉴于更新的信息,另一个答案是更好的方法。或者(实际上不要这样做),您可以信任您的外键,只信任DELETE FROM addresses,每个引用的地址都会抛出错误而不是被删除——您还需要确保它继续运行而不是退出在第一次无效删除时。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-03
  • 1970-01-01
  • 2012-06-25
  • 1970-01-01
  • 2020-12-10
相关资源
最近更新 更多