在SQL中,如何使MERGE从一组返回的结果中用一行更新相关行
我使用MERGE(Oracle)对符合on子句中指定的条件的记录进行更新,on子句连接子查询创建的虚拟表。声明的格式如下:在SQL中,如何使MERGE从一组返回的结果中用一行更新相关行,sql,oracle,merge,max,correlated-subquery,Sql,Oracle,Merge,Max,Correlated Subquery,我使用MERGE(Oracle)对符合on子句中指定的条件的记录进行更新,on子句连接子查询创建的虚拟表。声明的格式如下: MERGE INTO table1 t1 USING SELECT (t2.f21, MAX(t2.f22), t3.f31, t3.f32 from table2 t2, table3 t3 where {... various join/filter criteria ...} group by t2.f21, t3.f31, t3.f32) MATCHDATA ON
MERGE INTO table1 t1 USING SELECT (t2.f21, MAX(t2.f22), t3.f31, t3.f32
from
table2 t2, table3 t3
where
{... various join/filter criteria ...}
group by t2.f21, t3.f31, t3.f32) MATCHDATA
ON (t1.f11 = MATCHDATA.f21)
where t1.f12 = 'something';
现在rub:MATCHDATA将返回多行,因为“…条件…”本质上将返回多组匹配记录。所以“groupby”和“MAX()”的使用并没有给我买任何担保;相反,如果我加上:
where rownum = 1
在将MATCHDATA结果包装在另一个SELECT语句中(该语句只是重复返回的字段名)之后,我将限制自己只能更新一组需要更新的记录中的一条记录,该记录具有由MAX()确定的最高值。相反,我需要更新表1中与其记录组的每个MAX()记录中的join字段相匹配的记录。我从周五开始。我是新来的,所以没有什么进展。但这看起来很有希望,也许明天会产生更好的结果。事实上,当我尝试使用它时,例如不使用“rownum=1”将MATCHDATA中返回的记录集限制为一条记录,我发现了一个熟悉的“无法返回稳定的记录集”错误,合并支持者(如我自己)在其同事向他们寻求建议时必须害羞地微笑“比相关子查询更好”-福音化的新SQL命令,因为它们面临同样的错误
如您所见,我将MERGE视为相关子查询的更成功的兄弟。但是,在这种情况下,我是否应该回顾两个象鼻虫中较小的一个(即,使用相关子查询)来完成工作?或者是通过分区或对上面的其他修改来找到修复
感谢所有花时间提供建议的人,我对此表示感谢
我得到了熟悉的“无法返回一组稳定的记录”错误
因为您在ON子句中使用的连接键不足以使行唯一,无法执行WHEN MATCHED THEN UPDATE语句。
必须在ON子句中包含更多的键,直到匹配的行是唯一的,从而返回一组稳定的记录
让我们看一个测试用例:
设置
SQL> CREATE TABLE source_table (
2 col1 NUMBER,
3 col2 VARCHAR2(10),
4 col3 VARCHAR2(10)
5 );
Table created.
SQL>
SQL> INSERT INTO source_table (col1, col2, col3) VALUES (1, 'a', 'p');
1 row created.
SQL> INSERT INTO source_table (col1, col2, col3) VALUES (1, 'b', 'q');
1 row created.
SQL> INSERT INTO source_table (col1, col2, col3) VALUES (2, 'c', 'r');
1 row created.
SQL> INSERT INTO source_table (col1, col2, col3) VALUES (3, 'c', 's');
1 row created.
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> CREATE TABLE target_table (
2 col1 NUMBER,
3 col2 VARCHAR2(10),
4 col3 VARCHAR2(10)
5 );
Table created.
SQL>
SQL> INSERT INTO target_table (col1, col2, col3) VALUES (1, 'b', 'p');
1 row created.
SQL> INSERT INTO target_table (col1, col2, col3) VALUES (3, 'd', 'q');
1 row created.
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> SELECT * FROM source_table;
COL1 COL2 COL3
---------- ---------- ----------
1 a p
1 b q
2 c r
3 c s
SQL> SELECT * FROM target_table;
COL1 COL2 COL3
---------- ---------- ----------
1 b p
3 d q
SQL>
错误再现
SQL> MERGE INTO target_table trg
2 USING source_table src
3 ON (trg.col1 = src.col1) -- Not Unique
4 WHEN MATCHED THEN UPDATE SET
5 trg.col2 = src.col2,
6 trg.col3 = src.col3
7 WHEN NOT MATCHED THEN INSERT
8 (
9 col1,
10 col2,
11 col3
12 )
13 VALUES
14 (
15 src.col1,
16 src.col2,
17 src.col3
18 );
USING source_table src
*
ERROR at line 2:
ORA-30926: unable to get a stable set of rows in the source tables
SQL>
因此,正如预期的那样,我们得到了错误ORA-30926:无法在源表中获得一组稳定的行
让我们将上的子句设置为唯一的
SQL> MERGE INTO target_table trg
2 USING source_table src
3 ON (trg.col1 = src.col1
4 AND
5 trg.col2 = src.col2) -- Unique
6 WHEN MATCHED THEN UPDATE SET
7 trg.col3 = src.col3
8 WHEN NOT MATCHED THEN INSERT
9 (
10 col1,
11 col2,
12 col3
13 )
14 VALUES
15 (
16 src.col1,
17 src.col2,
18 src.col3
19 );
4 rows merged.
SQL> SELECT * FROM target_table;
COL1 COL2 COL3
---------- ---------- ----------
1 b q
3 d q
2 c r
3 c s
1 a p
SQL>
问题解决了
请记住,您无法更新ON子句中引用的列。假设我们有这个表T2:
C1 C2 AMOUNT UF
-- -- ---------- ----------
A X 12 101
A Y 3 102
A Y 12 103
B X 7 104
B Y 9 105
我需要将表1中的记录与中的join字段匹配
他们的记录组的每个MAX()记录都已更新。我从
Fri.沿着路径沿着分区往下走,我对那个分区还不熟悉,所以没有做任何修改
进展很大
这是一条很好的路径,您可以使用函数:
但如果merge
的加入字段仅为“C1”,则这组记录不稳定,因为对于C1='A'
我们有两排,甲骨文看起来很不好意思,它不知道你对哪一排感兴趣。
要解决此问题,您可以使用
而不是rank()-如果它都一样。但是如果这很重要,您需要在order
子句中添加更多内容,例如:
select * from (
select t2.*, rank() over (partition by c1 order by amount desc, c2) rn from t2 )
where rn = 1
C1 C2 AMOUNT UF RN
-- -- ---------- ---------- --
A X 12 101 1
B Y 9 105 1
这组行是稳定的,因为对于C1,没有重复的行,您可以在合并中使用它
merge into t1
using (
select * from (
select t2.*, rank() over (partition by c1 order by amount desc, c2) rn from t2 )
where rn=1) md
on (md.c1 = t1.c1)
when matched then update set t1.uf = md.uf
when not matched then insert (t1.c1, t1.uf)
values (md.c1, md.uf)
谢谢,Lalit,我进去后第一件事就是试试(大约6小时后)并用结果更新此页面。当然,请将其标记为回答,这将帮助其他人。谢谢Lalit。我得出结论,对于我需要做的事情,这是错误的方法。因此,我屏住呼吸,想出了一个应对挑战的新策略,并将我需要做的更新减少到2个普通DML a操作:插入相关子查询,然后进行基于自连接的更新。就我的问题的上下文而言,这样做了。但衷心感谢您花时间回复,我相信这不会浪费时间。感谢您的反馈。您好,Pounder,请查看我对Lalit的回复,感谢您的帮助!
merge into t1
using (
select * from (
select t2.*, rank() over (partition by c1 order by amount desc, c2) rn from t2 )
where rn=1) md
on (md.c1 = t1.c1)
when matched then update set t1.uf = md.uf
when not matched then insert (t1.c1, t1.uf)
values (md.c1, md.uf)