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 UPDATE
s :) 而不是使用连接)
不,它不会产生影响(我想 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:从公共子查询更新多个表的主要内容,如果未能解决你的问题,请参考以下文章
配置Goldengate从Oracle到PostgreSQL的同步复制
使用Ora2Pg工具把数据从Oracle导入到PostgreSQL