Postgresql 和 Oracle:从公共子查询更新多个表

Posted

技术标签:

【中文标题】Postgresql 和 Oracle:从公共子查询更新多个表【英文标题】:Postgresql and Oracle: update multiple tables from common subquery 【发布时间】:2013-02-27 13:17:44 【问题描述】:

你好,我有 4 张桌子

第一个表是菜单 有专栏:

    身份证号PK parent_id 编号 FK 到 menu.Id 标题字符变化(250) softdel 布尔默认 false

第二个表格是页面

    ID 为 PK menu_id 作为 menu.id 的 FK page_id 作为 page.id 的 FK softdel 布尔默认 false

第三个表是文章

    id 作为 PK 和 FK 到 page.id softdel 布尔默认设置为 false

第四张表article_lang

    partial_id 作为 PK id 作为 article.id 的 FK 语言字符 softdel 布尔默认设置为 false

当我“删除”(我的意思是设置 softdel true)菜单时,我需要创建更新语句,例如 200 我还将 softdel = false 设置为 parent_id = 200 的所有菜单以及 menu_id = menus_id 的所有页面和page_id = pages.id 的文章等等....

我只需要 1 个更新语句就可以做到吗??

如果我能创建 JPA 查询或 EJB 查询那就太好了:)

在oracle中我写了语句:

update pub_menu pm set softdel = 0 where pm.id in (
with menu_tree(id, parent_id) as (
  select 
    t1.id , t1.parent_id
    from menu t1
    where t1.id = 454

    union all
    select 
    t2.id , t2.parent_id
      from menu_tree
      join menu t2 on menu_tree.id = t2.parent_id
)
select id from menu_tree

)

update menu_page pmp set softdel = 1 where pmp.menu_id in (
with menu_tree(id, parent_id) as (
  select 
    t1.id , t1.parent_id
    from menu t1
    where t1.id = 454

    union all
    select 
    t2.id , t2.parent_id
      from menu_tree
      join menu t2 on menu_tree.id = t2.parent_id
)
select id from menu_tree

)

它的工作,但我这样做是不正确的:/

【问题讨论】:

您可以通过在前三个更新中添加RETURNING some_id 并在下一次更新中使用该值来链接四个更新。没有ddl,我就不细说了。 使用触发器传播softdel 可能更容易。 是的,它在 postgresql 中工作,但我需要在 postgresql 和 oracle 数据库上运行的 sql 我认为我什至不会进行更新——检查父行是否将 softdel 设置为 true 可能是有效且高效的 【参考方案1】:

类似:

with recursive menu_tree (id, parent_id) as (
   select id, parent_id
   from menu
   where id = 200
   union all 
   select c.id, c.parent_id
   from menu c
     join menu_tree p on p.id = c.parent_id
)
, deleted_menus (menu_id) as (
   update menu
      set softdel = true
   where id in (select id from menu_tree)
   returning menu.id
),
deleted_pages (page_id) as (
   update page
      set softdel = true
   where menu_id in (select menu_id from deleted_menus)
   returning page.id
),
deleted_articles (article_id) as (
   update article
     set softdel = true
   where page_id in (select page_id from deleted_pages)
)
update article_lang
   set softdel = true
where id in (select article_id from deleted_articles);

【讨论】:

恕我直言,WHERE ... IN(...) 可以替换为 EXISTS,或者仅替换为普通的 UPDATE target_table SET xx=true FROM src_table WHERE same_condition @wildplasser:可能,但我认为这不会有什么不同(而且我更习惯于编写标准 SQL UPDATEs :) 而不是使用连接) 不,它不会产生影响(我想 IN(...) 字段不可为空),但我实际上讨厌 IN(...) 子句,并且更喜欢相关子查询。 .. 是的,它在 postgresql 中运行良好,但我还需要在 oracle 数据库上运行此更新查询:/ @ŁukaszWoźniczka:这在 Oracle 中是不可能的。您将需要运行多个更新来实现这一点(或使用触发器)【参考方案2】:

如果你只是删除它们并让外键级联会更容易。

但是,您可以编写一个简短的触发函数来为您执行额外的删除操作。前 3 个表中的每一个都需要一个,但它们基本相同。他们需要根据每个 ROW 的 AFTER 触发器中的 OLD.id 更新链中的下一个表。

手册中的触发函数示例。

【讨论】:

【参考方案3】:

您可以将多个更新与 CTE 结合起来:

WITH u1 AS (UPDATE menu SET softdel = TRUE WHERE menu.id = 200),
     u2 AS (UPDATE menu SET softdel = FALSE WHERE menu.parent_id = 200),
     u3 AS (...),
     u4 AS (...),
     -- and so on
SELECT 1

【讨论】:

至少对于 Oracle 来说,这不是正确的语法:ORA-00928: missing SELECT keyword【参考方案4】:

Oracle 不允许任何一条 sql 语句更新多个表。因此,在这种情况下,常规 CTE 无法避免代码重复。

但这可以通过使用PL/SQL FOR LOOP IMPLICIT CURSOR 来解决,如下所示:

begin
  for cur in (
    with menu_tree(id, parent_id) as (
        select t1.id, t1.parent_id
        from menu t1 where t1.id = 454
        union all
        select t2.id , t2.parent_id
        from menu_tree
        join menu t2 on menu_tree.id = t2.parent_id
    )
    select id from menu_tree
  ) loop
    update pub_menu pm set softdel = 0 where pm.id = cur.id;
    update menu_page pmp set softdel = 1 where pmp.menu_id = cur.id;
  end loop cur;
end;

【讨论】:

以上是关于Postgresql 和 Oracle:从公共子查询更新多个表的主要内容,如果未能解决你的问题,请参考以下文章

在 PostgreSQL 中以一行显示今年和去年的数据

配置Goldengate从Oracle到PostgreSQL的同步复制

将函数从 Oracle 转换为 PostgreSQL

使用Ora2Pg工具把数据从Oracle导入到PostgreSQL

配置ogg从Oracle到PostgreSQL的同步复制json数据

Oracle GoldenGate现在支持从 PostgreSQL 捕获数据