这可以在SQL而不是循环中完成吗?

时间:2016-03-17 10:22:33

标签: sql oracle plsql common-table-expression

我有几张表,想要更新另一张表:

create table first_table
(bookstore_id number not null,
event   varchar2(10),
timestamp   date);

create table second_table
(bookstore_id number,
numbooks    number);

insert into second_table values (1,0);
insert into second_table values (2,0);
insert into second_table values (3,0);

insert into first_table values (1, 'ADD', sysdate);
insert into first_table values (1, 'ADD', sysdate);
insert into first_table values (1, 'ADD', sysdate);
insert into first_table values (1, 'REMOVE', sysdate);
insert into first_table values (1, 'ADD', sysdate);
insert into first_table values (1, 'REMOVE', sysdate);
insert into first_table values (2, 'ADD', sysdate);
insert into first_table values (2, 'ADD', sysdate);
insert into first_table values (2, 'REMOVE', sysdate);
insert into first_table values (2, 'ADD', sysdate);
insert into first_table values (2, 'ADD', sysdate);
insert into first_table values (2, 'ADD', sysdate);
insert into first_table values (3, 'ADD', sysdate);
insert into first_table values (3, 'ADD', sysdate);
insert into first_table values (3, 'REMOVE', sysdate);
insert into first_table values (3, 'ADD', sysdate);
insert into first_table values (3, 'REMOVE', sysdate);
insert into first_table values (3, 'ADD', sysdate);
insert into first_table values (3, 'REMOVE', sysdate);

以下逻辑可以满足我的需求:

begin
    for storeid in (select bookstore_id from second_table)
    loop
        update second_table
        set numbooks =
            (select count(*) from first_table
            where event = 'ADD'
            and bookstore_id = storeid.bookstore_id)
            -   /* actual minus sign is needed here */
            (select count(*) from first_table
            where event = 'REMOVE'
            and bookstore_id = storeid.bookstore_id)
        where bookstore_id = storeid.bookstore_id;
    end loop;
end;
/

我的问题是,这可以使用单个SQL语句来完成,避免循环吗?

2 个答案:

答案 0 :(得分:2)

不需要CTE或其他任何东西,你可以这样做:

update second_table
set numbooks =
              ((select count(*) from first_table
               where event = 'ADD'
               and bookstore_id = second_table.bookstore_id)
               -   /* actual minus sign is needed here */
               (select count(*) from first_table
                where event = 'REMOVE'
                and bookstore_id = second_table.bookstore_id))

实际上可以使用条件聚合来完成,并避免第一个表中的一个选择:

update second_table
set numbooks =
              (select (count(CASE WHEN event = 'ADD' then 1 end)
                       -
                       count(CASE WHEN event = 'REMOVE' then 1 end))
               from first_table
               WHERE bookstore_id = second_table.bookstore_id)
 WHERE EXISTS(select 1 from first_table s where second_table.bookstore_id = s.bookstore_id)

答案 1 :(得分:0)

您可以直接在SQL中执行此操作。以下是Oracle中的一种方法(其他数据库具有更简单的机制):

update second_table st
     set numbooks = (select sum(case when event = 'ADD' then 1
                                     when event = 'REMOVE' then -1
                                     else 0
                                end)
                     from first_table ft
                     where st.bookstore_id = ft.bookstore_id
                    )
     where exists (select 1 from first_table where st.bookstore_id = ft.bookstore_id);

请注意,where子句仅更新第二个表中第一个表中的行。如果您知道所有行都在那里 - 或者您希望更新缺失的行(使用NULL,可以轻松更改为0) - 那么请不要包含它。

如果其他类型的事件有很多行,则子查询中的where event in ('ADD', 'REMOVE')可能会使性能受益。

相关问题