【问题标题】:Delete data from table with foreign keys as primary从以外键为主的表中删除数据
【发布时间】:2015-05-16 05:59:26
【问题描述】:

我正在用元素和它们之间的连接编写绘图表面。

我的两个表面表如下所示:

CREATE TABLE element
(
  elid character varying(60) NOT NULL,
  data json,
  surface_id character varying(60) NOT NULL,
  type character varying,
  CONSTRAINT element_primary_key PRIMARY KEY (elid),
)
CREATE TABLE connection
(
  from_elid character varying(60) NOT NULL,
  to_elid character varying(60) NOT NULL,
  CONSTRAINT from_to_element_id PRIMARY KEY (from_elid, to_elid),
  CONSTRAINT from_element_fk FOREIGN KEY (from_elid)
      REFERENCES element (elid) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE,
  CONSTRAINT to_element_fk FOREIGN KEY (to_elid)
      REFERENCES element (elid) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE
)

如果我删除表面上的一个元素,它也会删除相应的连接,它由connection 约束表示。 在这种情况下,我会检查所有具有相同 surface_id 的元素,而那些缺失的元素将被删除(以及它们的连接)。

但是在绘图表面上被删除时如何显式删除连接?由于from_elidto_elid 仍然存在于element 表中?

编辑:为了保存绘图表面,所有元素和它们之间的连接都由客户端发送到服务器。

数据库中的示例数据:

--------------------------------  ---------------------
|element                        | |connection         |
--------------------------------  ---------------------
|elid | data |surface_id |type  | |from_elid |to_elid |
--------------------------------- ---------------------
|e1   |{}    |s1         |xxx   | |e1        |e2      |
|e2   |{}    |s1         |xxx   | |e2        |e3      |
|e3   |{}    |s1         |xxx   | |e3        |e4      |
|e4   |{}    |s1         |xxx   | |..        |..      |
|e5   |{}    |s2         |xxx   | |e5        |e6      |
|e6   |{}    |s2         |xxx   | |e6        |e7      |
|..   |..    |..         |...   | |..        |..      |
--------------------------------- ---------------------

客户端发送的示例数据:

{
  surface_id: 's1', 
  elements:['e1','e2','e3','e4'], 
  connections:[
    {from:'e2', to:'e3'},
    {from:'e3', to:'e4'}
  ]
}

在示例数据中,e1e2 之间的连接缺失,这意味着它在绘图表面上被删除,surface_id = 's1' 并且必须从表 connection 中删除.如何使用给定的数据实现这一点,而不是删除带有surface_id = 's2' 的元素之间的连接?

【问题讨论】:

  • 对不起,我不明白:“但删除没有连接元素的单个连接需要另一种方法” - 只需从 connection 表中删除行。这不会自动删除元素。
  • 好的,如果你想删除一个连接,你的查询会是什么样子,如果你关于它被删除的唯一信息是它不是由客户端发送的(只发送当前元素和连接)
  • 抱歉,您需要比这更具体。这个“客户”到底是什么东西?它向数据库发送了什么?如果该客户端与数据库通信,那么它为什么不能简单地删除不再需要的连接(使用简单的delete from connection where ...
  • 我明白了,用示例数据再次改变了我的问题

标签: sql postgresql foreign-keys sql-delete


【解决方案1】:

但是在绘图表面上被删除时如何显式删除连接?由于 from_elid 和 to_elid 仍然存在于表格元素中

您误解了外键的工作原理。从connectionelement 的外键表明,如果您插入连接,则必须 存在一个元素。它确实要求每个元素都存在一个连接。

因此,您可以随时从connection 表中删除行。只要至少有一个使用该元素的连接,您就不能从element 表中删除行。


编辑

如果您的程序只获得“幸存”的连接,您可以使用提供的信息删除过时的连接

假设您有三个连接,但您不知道其中哪些是新的,哪些不是:

您首先需要删除不存在的:

delete from connection 
where (from_elid, to_elid) not in ( ('e2', 'e3'), ('e3','e4'), ('e4','e1') )
  and exists (select 1 
              from element e 
              where e.elid in (connection.from_elid, connection.to_elid)
                and e.surface_id = 's1');

这将删除表面 s1 上元素的所有连接,in 子句中的元素除外。

现在您需要从该列表中插入那些在connection 表中尚不存在的连接——基本上是一个条件插入。这可以使用insert .. select

insert into connection (from_elid, to_elid)
select *
from (
  values ('e2', 'e3'), ('e3','e4'), ('e4','e1')
) as t (from_elid, to_elid)
where not exists (select *
                  from connection c
                  where (t.from_elid, t.to_elid) = (c.from_elid, c.to_elid));

您确实需要在多用户环境中正确处理唯一密钥违规,因为其他事务可能会插入相同的信息。

SQLFiddle 示例:http://sqlfiddle.com/#!15/64e7d/1

【讨论】:

  • 感谢您的回答,我想我的问题还不够清楚。请参阅我的编辑。顺便说一句,约束from_elid用于删除连接
  • 这也是我的第一种方法,但是该查询也会删除来自其他绘图表面(由surface_id 确定)的所有连接。
【解决方案2】:

但是如何在被删除时显式删除连接 绘图表面?

您的意思是在删除元素表中引用的 elid 条目后,要删除连接表中的那些外键条目?

无论如何,如果 DDL 不允许你实现你想要的,你可以考虑使用像 BEFORE DELETE 或 AFTER DELETE 这样的触发器,这样你就可以先清理元素表,然后再清理连接表或连接表。元素表。

【讨论】:

  • 感谢您的回答,我试图通过编辑使我的问题更清楚
  • 我对您的要求的理解表明,像 BEFORE DELETE 或 AFTER DELETE 这样的触发器会有所帮助。
  • 嗯,我看不出触发器以何种方式有助于避免删除不在s1 中的元素之间的连接。你能举一个解决这个问题的触发器的例子吗?
  • 如果我正确理解了您问题的最后一段,那么在元素表中,您删除了 surface_id = 's1' 的“e1”,但连接表仍然保留了 from_elid = 'e1' 的条目。如果这是正确的,那么您可以在元素表上使用 BEFORE DELETE 触发器,在该触发器中,您可以删除 from_elid = 'e1' 的所有行,然后从元素表中删除“e1”。我无法理解或可能遗漏的是 - 在元素表中,您可以将 elid 设为 e1,将 surface_id 设为 s2 吗?如果这是正确的,那么您可能需要在连接表中再增加一列。
  • 连接表中的另一列表示surface_id。
猜你喜欢
  • 1970-01-01
  • 2011-10-23
  • 1970-01-01
  • 1970-01-01
  • 2014-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多