我有一个要使用SQL更新的数据库表。基本上,它为时间表小册子的各个部分提供了一组描述信息,但这并不重要。一些数据已经通过应用程序输入,但是这很耗时,并且其中的数据块是“样板”,需要每周进行更新。在某些情况下,我可能会错过通过应用程序输入数据的机会,因此,如果数据不存在,我还想自动创建。
这导致我使用MERGE查询,如下所示:
MERGE INTO TTP_LINE_DESCRIPTION o
USING
(SELECT DISTINCT lv.lv_nv_id TLDE_NV_ID,
lv.lv_id TLDE_LV_ID,
dir.dir_id TLDE_DIR_ID,
8 TLDE_MED_FLAG,
1 TLDE_TYPE,
0 TLDE_SORT_NO,
'Timetable valid from ' || to_char(lv.lv_valid_from,'DD/MM/YYYY') || ' until ' || nvl2(lv.lv_valid_until,to_char(lv.lv_valid_until,'DD/MM/YYYY'),'further notice') TLDE_TEXT,
0 TLDE_ALIGNMENT,
null TLDE_FONT_SIZE,
null TLDE_FONT_STYLE
FROM LINE_VERSION lv
JOIN line_point_sequence lps ON (lv.lv_id = lps.lps_lv_id)
JOIN direction dir ON (dir.dir_id = lps.lps_dir_id)
where lv.lv_nv_id=3799 and lv.lv_id=10455244) n
ON (o.TLDE_NV_ID=n.TLDE_NV_ID
and o.TLDE_LV_ID=n.TLDE_LV_ID
and o.TLDE_DIR_ID=n.TLDE_DIR_ID
and o.TLDE_TYPE=n.TLDE_TYPE
and o.TLDE_SORT_NO=n.TLDE_SORT_NO)
WHEN MATCHED THEN
UPDATE SET o.TLDE_TEXT=n.TLDE_TEXT,
o.TLDE_ALIGNMENT=n.TLDE_ALIGNMENT,
o.TLDE_FONT_SIZE=n.TLDE_FONT_SIZE,
o.TLDE_FONT_STYLE=n.TLDE_FONT_STYLE
WHEN NOT MATCHED THEN
INSERT (o.tlde_id, o.tlde_nv_id, o.tlde_lv_id, o.tlde_dir_id,
o.tlde_med_flag, o.tlde_type, o.tlde_sort_no, o.tlde_text,
o.tlde_alignment, o.tlde_font_size, o.tlde_font_style,
o.updated_by, o.updated_on, o.updated_prog)
VALUES ((select max(tld.tlde_id)+1 from TTP_LINE_DESCRIPTION tld),
n.tlde_nv_id, n.tlde_lv_id, n.tlde_dir_id, n.tlde_med_flag,
n.tlde_type, n.tlde_sort_no, n.tlde_text, n.tlde_alignment,
n.tlde_font_size, n.tlde_font_style, 'STUARTR',
SYSDATE, 'PL/SQL Developer');
这里要注意一件事,即SELECT DISTINCT中的WHERE子句。我知道lv.lv_id=10455244
是一个引用,它将强制select在该SELECT中仅返回一对行,以便可以限制测试。为此,10455244是TTP_LINE_DESCRIPTION表中当前不存在的有效值。
当我使用表中的值时,WHEN MATCHED代码将正确执行,并在0.016s内更新一对行。
使用上面显示的值单独运行SELECT语句将返回需要加0.109s的两行。
根据VALUES行末尾的第一项获取最大id并将其添加一个(这是主键)需要0s。
最后,如果我写一个INSERT INTO并显式地写我要为其中一行写的所有值,那么我可以在0.016s内完成一行的INSERT。
但是把它们放在一起……什么都没有。执行只是坐在那里执行,并且似乎没有结束。否则我会紧张等待它是否会结束。我给它留了一段合理的时间,似乎什么也没进。
那是怎么回事,为什么它不做我认为应该做的事?
答案 0 :(得分:0)
您正在这样做:
create table t (id, nv_id, val) as (select 1, 101, 'A' from dual);
merge into t o
using (
select 102 nv_id, 'P' val from dual union all
select 103 nv_id, 'Q' val from dual ) n
on (o.nv_id = n.nv_id)
when matched then update set val = n.val
when not matched then
insert (o.id, o.nv_id, o.val)
values ((select max(id) + 1 from t), n.nv_id, n.val);
在我的情况下,它可以工作,但是插入行的id
是相同的:2
。当我执行回滚并添加主键约束时:
alter table t add constraint t_pk primary key(id);
merge
导致错误-违反唯一约束。我怀疑这与您的tlde_id
有关,也许不是约束,而是其他。以这种方式产生价值是一个很大的禁忌。如果您的Oracle版本是12c或更高版本,则可以将此列更改为自动生成
generated by default on null as identity
或在较早的版本中使用序列和触发器(找到最大id
,加1并将其设置为序列的起始值)
create sequence seq_t_id start with 2;
create or replace trigger t_on_insert
before insert on t for each row
begin
select seq_t_id.nextval into :new.id from dual;
end;
然后通过从插入子句中删除merge
或仅插入id
来修改null
,触发器将设置适当的值:
merge into t o
using (
select 102 nv_id, 'P' val from dual union all
select 103 nv_id, 'Q' val from dual ) n
on (o.nv_id = n.nv_id)
when matched then update set val = n.val
when not matched then
insert (o.nv_id, o.val)
values (n.nv_id, n.val);
即使这不能解决您的问题,也不应以id
的形式生成max() + 1
,这也会导致会话并发,提交,多次插入等问题。