Oracle SQL语句根据特定条件更新列值

时间:2016-10-24 01:28:26

标签: sql oracle

我有一个表有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

任何帮助都将受到高度赞赏..

提前致谢。

4 个答案:

答案 0 :(得分:2)

这是不是答案,但是对目前为止提供的两种解决方案的测试 - 我将称之为" EXISTS" " AGGREGATE"解决方案或方法。

测试详情如下,但这里有两个总体结论:

  1. 两种方法都具有可比较的执行时间;平均而言,AGGREGATE方法的工作速度比EXISTS方法快一点,但是幅度非常小(小于一次试验到下一次试验的运行时间之间的差异)。如果没有任何列上的索引,则运行时间为:(第一个数字用于EXISTS方法,第二个数字用于AGGREGATE)。 试用版1 :8.19s 8.08s 试用版2 :8.98s 8.22s 试用版3 :9.46s 9.55s 注意 - 估算的优化器成本应仅用于比较同一语句的不同执行计划,而不是针对使用不同方法的不同解决方案。即便如此,有人会不可避免地要求;所以 - 对于EXISTS方法,Optimizer发现的最低成本是4766;对于AGGREGATE,2665。但是,这完全没有意义。

  2. 如果需要更新很多行,索引将伤害性能远远超过它们的帮助。实际上,当更新行时,索引也必须更新。如果只需要更新少量行,那么索引将有所帮助,因为大多数时间都花在查找必须更新的行上,而且只需要花费很少的时间在更新中。在我的例子中,几乎25%的行必须更新...所以AGGREGATE解决方案需要51.2秒,EXISTS解决方案需要59.3秒!建议:如果您希望可能需要更新大量行,并且您已经在表上有索引,那么最好还是删除它们并在更新后重新创建它们!或者,也许还有其他解决方案可以解决这个问题;我不是专家(牢记这一点!)

  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 byhavingdistinct更有效。要将其与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;