2015-05-14 84 views
3

上下文: 我有两个表:markettypewagerlimitgroups(mtwlg)和stakedistributionindicators(sdi)。当创建一个mtwlg时,在sdi表中创建了两行,这两行是链接到mtwlg的 - 每行都有相同的值bar 2,id和另一个字段(我们称之为列X),其中一行必须包含0另一个是1。 我们的代码库中存在一个错误,它可以自动防止这种情况的发生,所以在出现错误期间创建的任何mtwlg都没有相关的sdi,导致NPE在各个地方出现。Oracle SQL - 如何编写有条件和循环的插入语句?

为了解决这个问题,需要写一个补丁来循环遍历mtwlg表和每个ID,在sdi表中搜索2个相关的行。如果行存在,则不做任何事情;如果只有1行,请检查F是否为0或1,并插入另一个值;如果两行都不存在,则插入它们。这需要为每个mtwlg完成,并且还需要插入一个唯一的ID。

伪代码:

For each market type wager limit group ID 
    Check if there are 2 rows with that id in the stake distributions table, 1 where column X = 0 and one where column X = 1 
    if none 
     create 2 rows in the stake distributions table with unique id's; 1 for each X value 
    if one 
     create the missing row in the stake distributions table with a unique id 
    if 2 
     do nothing 

如果有帮助的话 - 该修补程序将使用liquibase应用。

任何人有任何意见或想法,以及如何以及如何在SQL/liquibase补丁中写入?

在此先感谢您,让我知道您需要的任何其他信息。

编辑:

我实际上只是被告知要做到这一点使用PL/SQL,你有关于这个有什么想法/建议? 再次感谢。

回答

4

Oooooh,MERGE的出色工作。

这是你的伪代码再次:​​

For each market type wager limit group ID 
    Check if there are 2 rows with that id in the stake distributions table, 
     1 where column X = 0 and one where column X = 1 
    if none 
     create 2 rows in the stake distributions table with unique id's; 
     1 for each X value 
    if one 
     create the missing row in the stake distributions table with a unique id 
    if 2 
     do nothing 

这里的MERGE变种(还是伪code'ish,因为我不知道你的数据看起来真的):

MERGE INTO stake_distributions d 
USING (
    SELECT limit_group_id, 0 AS x 
    FROM market_type_wagers 
    UNION ALL 
    SELECT limit_group_id, 1 AS x 
    FROM market_type_wagers 
) t 
ON (
    d.limit_group_id = t.limit_group_id AND d.x = t.x 
) 
WHEN NOT MATCHED THEN INSERT (d.limit_group_id, d.x) 
VALUES (t.limit_group_id, t.x); 

没有循环,没有PL/SQL,没有条件语句,只是简单漂亮的SQL。

在评论由Boneist建议不错的选择使用CROSS JOIN而非UNION ALLUSING条款,这是在可能有更好的表现(未经证实):

MERGE INTO stake_distributions d 
USING (
    SELECT w.limit_group_id, x.x 
    FROM market_type_wagers w 
    CROSS JOIN (
    SELECT 0 AS x FROM DUAL 
    UNION ALL 
    SELECT 1 AS x FROM DUAL 
) x 
) t 
ON (
    d.limit_group_id = t.limit_group_id AND d.x = t.x 
) 
WHEN NOT MATCHED THEN INSERT (d.limit_group_id, d.x) 
VALUES (t.limit_group_id, t.x); 
+1

*“我可以简单地使用MAX(d.id)+1”* NO!千万不要这么做:-)有很多原因会导致错误(主要是竞赛条件)。理想情况下,您将使用一个序列,例如正如[user boneist here]所建议的(http://stackoverflow.com/a/30233998/521799)。显式地或[通过触发器](http://stackoverflow.com/questions/25660307/creating-oracle-sequence-trigger) –

+1

为了加速性能,你可以用一个虚拟2-行表/子查询 – Boneist

+0

@Boneist:绝对!感谢提示 –

0

如果处理逻辑过于粗糙的在一个SQL语句进行封装,你可能需要求助于游标循环和行类型 - 基本上可以让你不喜欢的东西如下:

DECLARE 

    r_mtwlg markettypewagerlimitgroups%ROWTYPE; 

BEGIN 

    FOR r_mtwlg IN (
     SELECT mtwlg.* 
     FROM markettypewagerlimitgroups mtwlg 
    ) 
    LOOP 
     -- do stuff here 
     -- refer to elements of the current row like this 
     DBMS_OUTPUT.PUT_LINE(r_mtwlg.id); 
    END LOOP; 
END; 
/

你显然可以在这个表格中嵌入另一个循环,但是我会将它作为练习留给你。您也可以在第一个游标中多次加入stakedistributionindicators,以便您只返回尚未具有x = 1和x = 0的行,您可能也可以为自己工作。

+0

如何正常使用的id目前生成?如果您的系统正在使用一个序列,您可以使用相同的序列,那么大多数Oracle数据库在版本12之前都会使用此序列。 – ninesided

+0

另外,假设'stakedistributionindicators'表有一个基于'id'的主键,那么不必担心你的问题的条件部分,你总是可以尝试插入两个'x'的值,但是捕获主关键违规异常并且什么都不做。这样你就可以保证总是得到你的两行,逻辑会更简单。 – ninesided

+0

当您可以在单个SQL语句中轻松完成此操作时,为什么建议使用循环游标(又名逐行,又名缓慢)?如果你这样做,性能将会变慢。 – Boneist

2

答:你不知道。绝对不需要循环任何东西 - 你可以在一个插入中完成。所有你需要做的是找出缺少的行,然后你只需要添加他们

下面是一个例子:

drop table t1; 
drop table t2; 
drop sequence t2_seq; 

create table t1 (cola number, 
       colb number, 
       colc number); 

create table t2 (id number, 
       cola number, 
       colb number, 
       colc number, 
       colx number); 

create sequence t2_seq 
    START WITH 1 
    INCREMENT BY 1 
    MAXVALUE 99999999 
    MINVALUE 1 
    NOCYCLE 
    CACHE 20 
    NOORDER; 

insert into t1 values (1, 10, 100); 
insert into t2 values (t2_seq.nextval, 1, 10, 100, 0); 
insert into t2 values (t2_seq.nextval, 1, 10, 100, 1); 

insert into t1 values (2, 20, 200); 
insert into t2 values (t2_seq.nextval, 2, 20, 200, 0); 

insert into t1 values (3, 30, 300); 
insert into t2 values (t2_seq.nextval, 3, 30, 300, 1); 

insert into t1 values (4, 40, 400); 

commit; 

insert into t2 (id, cola, colb, colc, colx) 
with dummy as (select 1 id from dual union all 
       select 0 id from dual) 
select t2_seq.nextval, 
     t1.cola, 
     t1.colb, 
     t1.colc, 
     d.id 
from t1 
     cross join dummy d 
     left outer join t2 on (t2.cola = t1.cola and d.id = t2.colx) 
where t2.id is null; 

commit; 

select * from t2 
order by t2.cola; 

     ID  COLA  COLB  COLC  COLX 
---------- ---------- ---------- ---------- ---------- 
     1   1   10  100   0 
     2   1   10  100   1 
     3   2   20  200   0 
     5   2   20  200   1 
     7   3   30  300   0 
     4   3   30  300   1 
     6   4   40  400   0 
     8   4   40  400   1 
0

如果你宁愿写你的逻辑在Java中与PL/SQL,Liquibase允许您创建custom changes。自定义更改指向您编写的Java类,可以执行任何您需要的逻辑。一个简单的例子可以发现here