我有一个表有3列-PID,LOCID,ISMGR。现在在现有场景中,对于某些人,基于位置ID,他被设置为ISMGR = true。 但是根据新的要求,我们必须让任何至少有一个ISMGR = true的人的所有ISMGR = true(意味着如果他是任何一个位置的管道,他应该是所有位置的经理)。 / p>
运行脚本之前的表数据:
PID|LOCID|ISMGR
1 1 1
1 2 0
1 3 0
2 1 0
2 2 1
运行脚本后的表数据:
PID|LOCID|ISMGR
1 1 1
1 2 1
1 3 1
2 1 1
2 2 1
任何帮助都将受到高度赞赏..
提前致谢。
答案 0 :(得分:2)
这是不是答案,但是对目前为止提供的两种解决方案的测试 - 我将称之为" EXISTS" " AGGREGATE"解决方案或方法。
测试详情如下,但这里有两个总体结论:
两种方法都具有可比较的执行时间;平均而言,AGGREGATE方法的工作速度比EXISTS方法快一点,但是幅度非常小(小于一次试验到下一次试验的运行时间之间的差异)。如果没有任何列上的索引,则运行时间为:(第一个数字用于EXISTS方法,第二个数字用于AGGREGATE)。 试用版1 :8.19s 8.08s 试用版2 :8.98s 8.22s 试用版3 :9.46s 9.55s 注意 - 估算的优化器成本应仅用于比较同一语句的不同执行计划,而不是针对使用不同方法的不同解决方案。即便如此,有人会不可避免地要求;所以 - 对于EXISTS方法,Optimizer发现的最低成本是4766;对于AGGREGATE,2665。但是,这完全没有意义。
如果需要更新很多行,索引将伤害性能远远超过它们的帮助。实际上,当更新行时,索引也必须更新。如果只需要更新少量行,那么索引将有所帮助,因为大多数时间都花在查找必须更新的行上,而且只需要花费很少的时间在更新中。在我的例子中,几乎25%的行必须更新...所以AGGREGATE解决方案需要51.2秒,EXISTS解决方案需要59.3秒!建议:如果您希望可能需要更新大量行,并且您已经在表上有索引,那么最好还是删除它们并在更新后重新创建它们!或者,也许还有其他解决方案可以解决这个问题;我不是专家(牢记这一点!)
为了正确测试,在我创建测试表并提交之后,我自己运行了每个解决方案,然后我回滚并以SYS(在不同的会话中)登录,我运行alter system flush buffer_cache
以确保缓存命中或未命中伤害不会随机提升性能。在所有情况下,一切都是从磁盘存储完成的。
我创建了一个id为1到120万的表,以及1到3之间的随机整数,概率分别为40%,40%和20%(参见下面dbms_random
的使用) 。然后根据这个prep
数据我创建了测试表:基于这个随机整数,每个pid
被包括一次,两次或三次;并且在每行中添加随机0或1作为ismgr
(概率为50-50)。我还添加了1到4之间的随机整数locid
来模拟实际数据;我并不担心重复locid
,因为该列在问题中没有任何作用。
在120万pid
中,大约480,000(40%)只出现在测试表中一次,另外约480,000次出现两次,约240,000次出现三次。总行数应约为2,160,000。这是基表的基数(实际上它最终是2,160,546)。然后:不需要更改具有唯一pid
的~480,000行;计数为2的480,000 pid
中的一半将具有相同的ismgr
(因此没有变化),另一半将被拆分,因此我们需要从这些中更改240,000行;并且一个简单的组合参数表明,必须更改在表中出现三次的pid
的720,000行中的3/8或270,000行。所以我们应该期望改变510,000行。事实上,更新语句导致更新了510,132行(两种解决方案都相同)。这些健全性检查表明测试可能已正确设置。下面我还展示了基表中的一个小样本,也作为一个完整性检查。
CREATE TABLE 声明:
create table tbl as
with prep ( pid, dup ) as (
select level,
round( dbms_random.value(0.5, 3) ) as dup
from dual
connect by level <= 1200000
)
select pid,
round( dbms_random.value(0.5, 4.5) ) as locid,
round( dbms_random.value(0, 1) ) as ismgr
from prep
connect by level <= dup
and prior pid = pid
and prior sys_guid() is not null
;
commit;
完整性检查:
select count(*) from tbl;
COUNT(*)
----------
2160546
select * from tbl where pid between 324720 and 324730;
PID LOCID ISMGR
---------- ---------- ----------
324720 4 1
324721 1 0
324721 4 1
324722 3 0
324723 1 0
324723 3 0
324723 3 1
324724 3 1
324724 2 0
324725 4 1
324725 2 0
324726 2 0
324726 1 0
324727 3 0
324728 4 1
324729 1 0
324730 3 1
324730 3 1
324730 2 0
19 rows selected
更新语句:
update tbl t
set ismgr = 1
where ismgr = 0 and
exists (select 1 from tbl t2 where t2.pid = t.pid and t2.ismgr = 1);
rollback;
update tbl
set ismgr = 1
where ismgr = 0
and pid in ( select pid
from tbl
group by pid
having max(ismgr) = 1);
rollback;
-- statements to create indexes, used in separate testing:
create index pid_ismgr_idx on tbl(pid, ismgr);
create index ismgr_ids on tbl(ismgr);
答案 1 :(得分:1)
为什么选择PL / SQL?您只需要一个简单的SQL语句。例如:
update your_table t -- enter your actual table name here
set ismgr = 1
where ismgr = 0
and pid in ( select pid
from your_table
group by pid
having max(ismgr) = 1)
;
答案 2 :(得分:1)
我倾向于使用exists
:
update t
set ismgr = 1
where ismgr = 0 and
exists (select 1 from t t2 where t2.pid = t.pid and t2.ismgr = 1);
exists
应该比使用聚合执行子查询更有效。
这适用于t(pid, ismgr)
和t(ismgr)
上的索引。
答案 3 :(得分:1)
现有的解决方案非常好,但我更喜欢在我从相关子查询中更新行时使用merge
。我发现它更具可读性,性能通常与exists
方法相当。
MERGE INTO t
USING (SELECT DISTINCT pid
FROM t
WHERE ismgr = 1) src
ON (t.pid = src.pid)
WHEN MATCHED THEN
UPDATE SET ismgr = 1
WHERE ismgr = 0;
正如@mathguy指出的那样,在这种情况下使用group by
和having
比distinct
更有效。要将其与merge
一起使用,只需更改子查询:
MERGE INTO t
USING (SELECT pid
FROM t
GROUP BY pid
HAVING MAX(ismgr) = 1) src
ON (t.pid = src.pid)
WHEN MATCHED THEN
UPDATE SET ismgr = 1
WHERE ismgr = 0;