我有两张桌子:
CREATE TABLE huge ( id INT PRIMARY KEY, name VARCHAR2(100), day INT, errno INT, error_message VARCHAR2(100) ); CREATE TABLE smallish ( id INT PRIMARY KEY, name VARCHAR2(100), day int ); -- Note the lack of a foreign key between huge and smallish on name -- This is intentional
我想做三件事:
huge
中的所有行,将day
设置为smallish
中huge
中的行name
中只有smallish
的{{1}} }}。huge
中的所有行,使errno
为1
,error_message
为:name not in smallish
huge
中的行在smallish
。huge
中的所有行,使errno
2
和error_message
:name has multiple rows in smallish
huge
中的行name
smallish
我使用下面的DML似乎可以正常工作,但其中两个DML在输出中提供了全表扫描,否则看起来不直观。
此外,在一个陈述中完成所有这一切将非常酷,而不是三个。
更新
以下似乎有效,看起来有些直观,但解释计划显示了巨大的全表扫描:
UPDATE huge h_out SET (day, errno, error_message) = ( select CASE WHEN DAY IS NOT NULL AND count = 1 THEN day ELSE NULL END as bill_day, CASE WHEN day IS NULL THEN 1 WHEN count > 1 THEN 2 ELSE NULL END AS errno, CASE WHEN day IS NULL THEN name || ' not in smallish' WHEN count > 1 THEN name || ' has multiple rows in smallish' ELSE NULL END as error_message FROM ( select dhuge.name, max(smallish.day) as day, count(dhuge.name) as count from (select distinct huge.name from huge) dhuge left join smallish on dhuge.name = smallish.name group by dhuge.name ) h_in WHERE h_out.name = h_in.name );
原件:
-- Problem #1 UPDATE huge h SET (day) = ( SELECT MIN(day) FROM smallish s WHERE h.name = s.name GROUP BY s.name HAVING count(1) = 1 ) WHERE EXISTS ( SELECT null FROM smallish s WHERE s.name = h.name ); -- Problem #2 Explain plan shows a full table scan on huge UPDATE huge h_out SET (errno, error_message) = ( select 1, h_out.name || ' not in smallish' AS error_message FROM DUAL ) WHERE NOT EXISTS ( SELECT NULL FROM smallish s WHERE s.name = h_out.name ); -- Problem #3 Explain plan shows a full table scan on huge UPDATE huge h SET (errno, error_message) = ( SELECT 2, h.name || ' has multiple rows' FROM dual ) WHERE EXISTS ( SELECT s.name FROM smallish s WHERE h.name = s.name GROUP BY s.name HAVING count(1) > 1 );
要复制:
DROP TABLE huge; DROP TABLE smallish; CREATE TABLE huge ( id INT PRIMARY KEY, name VARCHAR2(100), day INT, errno INT, error_message VARCHAR2(100) ); CREATE TABLE smallish ( id INT PRIMARY KEY, name VARCHAR2(100), day int ); create index huge_name_indx ON huge (name); create index smallish_name_indx ON smallish (name); insert into huge values (1, 'good1', null, 0, null); insert into huge values (2, 'good1', null, 0, null); insert into huge values (3, 'good1', null, 0, null); insert into huge values (4, 'good1', null, 0, null); insert into huge values (5, 'good2', null, 0, null); insert into huge values (6, 'good2', null, 0, null); insert into huge values (7, 'good2', null, 0, null); insert into huge values (8, 'good2', null, 0, null); insert into huge values (9, 'double1', null, 0, null); insert into huge values (10, 'double1', null, 0, null); insert into huge values (11, 'double1', null, 0, null); insert into huge values (12, 'double1', null, 0, null); insert into huge values (13, 'double2', null, 0, null); insert into huge values (14, 'double2', null, 0, null); insert into huge values (15, 'double2', null, 0, null); insert into huge values (16, 'double2', null, 0, null); insert into huge values(17, 'notin1', null, 0, null); insert into huge values(18, 'notin1', null, 0, null); insert into huge values(19, 'notin1', null, 0, null); insert into huge values(20, 'notin1', null, 0, null); insert into huge values(21, 'notin2', null, 0, null); insert into huge values(22, 'notin2', null, 0, null); insert into huge values(23, 'notin2', null, 0, null); insert into huge values(24, 'notin2', null, 0, null); insert into smallish values (1, 'good1', 1); insert into smallish values (2, 'good2', 2); insert into smallish values (3, 'double1', 3); insert into smallish values (4, 'double1', 4); insert into smallish values (5, 'double2', 5); insert into smallish values (6, 'double2', 6); commit;
答案 0 :(得分:2)
要在一个语句中执行此操作,您可以使用merge
:
merge into huge h
using (select name, count(*) as cnt, max(day) as day
from smallish
group by name
) s
on h.name = s.name
when matched then update
set day = (case when s.cnt = 1 then s.day else h.day end),
errno = (case when s.cnt > 1 then 2 else h.errno end),
error_message = (case when s.cnt > 1 then s.name || ' has multiple rows in smallish' else error_message end)
when not matched then update
set errno = 1,
error_message = h.name || ' not in smallish';
答案 1 :(得分:0)
上面的合并看起来很有希望,但是您不能将其用于名称不匹配(语法限制)-为此需要单独的UPDATE,并且可能无法避免全表扫描。 我喜欢使用“ IN(子查询)”,因为它通常会提供很好的解释计划。 试试这些:
UPDATE huge SET ...
WHERE name IN (SELECT name FROM smallish GROUP BY name HAVING count(*) = 1);
UPDATE huge SET ...
WHERE name IN (SELECT name FROM smallish GROUP BY name HAVING count(*) > 1);
UPDATE huge SET ...
WHERE name NOT IN (SELECT name FROM smallish);
为huge_name_indx设置BITMAP索引也可能有所帮助。
顺便说一句,汤姆·凯特(Tom Kyte)建议用更新的数据创建一个新表,而不是更新现有的大表(https://asktom.oracle.com/pls/asktom/asktom.search?tag=how-to-update-millions-or-records-in-a-table-200211)。