这可以在SQL而不是循环中完成吗?
我有几个表,希望从另一个表中更新一个表:这可以在SQL而不是循环中完成吗?,sql,oracle,plsql,common-table-expression,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_tab
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语句来避免循环?不需要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))
它实际上可以通过条件聚合完成,并避免从第一个表中选择1项:
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)
不需要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))
它实际上可以通过条件聚合完成,并避免从第一个表中选择1项:
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)
您可以直接在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可能会提高性能。您可以直接在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可能会提高性能。一般来说,不希望存储可以计算的数据。第二个表可以很容易地实现为一个视图:选择bookstore\u id,SUMCASE WHEN event='ADD',然后按bookstore\u id从第一个表组中选择1 ELSE-1作为num\u books结束。如果性能有问题,可能有办法让您的DBMS自动持久化此视图中的数据Oracle:Materialized Views,SQL Server:索引视图。这是一个更复杂问题的简化示例,但逻辑类似。我的问题是我是否可以避免使用循环和plsql来做这种事情。你可以避免循环,这并不难做到。但是,你绝对应该听从建议,不要存储那些可以从现有数据中计算出来的数据。一般来说,最好不要存储那些可以计算出来的数据。第二个表可以很容易地实现为一个视图:选择bookstore\u id,SUMCASE WHEN event='ADD',然后按bookstore\u id从第一个表组中选择1 ELSE-1作为num\u books结束。如果性能有问题,可能有办法让您的DBMS自动持久化此视图中的数据Oracle:Materialized Views,SQL Server:索引视图。这是一个更复杂问题的简化示例,但逻辑类似。我的问题是我是否可以避免使用循环和plsql来做这种事情。你可以避免循环,这并不难做到。但是,您绝对应该听取建议,而不是存储可以从现有数据中轻松计算出来的数据。