问题
I need to execute a statement in PL-SQL that taking a selection of Ids, executes a join against a sub-set of those ids... in the example below i have about 700000 customers, and a far more complex query than shown here in the simple while loop in this example...i'm seeding quite poor performance and am curious if chopping up my current PL-SQL into 'chunks' would yield a perf increase?
Currently:
declare
TYPE customerIdTabType IS TABLE OF customer.CustomerId%TYPE INDEX BY BINARY_INTEGER;
vars customerIdTabType;
-- maybe this should be in a table?
cursor c is
select
c.CustomerId
from customer c
join productcustomers pc on pc.customerid = c.customerid
join product p on p.productid = pc.productid
where
c.CustomerId > 1000;
begin
open c;
loop
fetch c bulk collect into vars limit 1000;
-- here is where instead of looping through each item in vars
-- i actually want to 'join' to the 1000 that i have.
forall i in 1..vars.count
insert into xxx (CustomerId)
values (vars(i));
commit;
exit when vars.count = 0;
end loop;
close c;
end;
- Select a list of CustomerIds into a "temporary" storage container - not sure what the options are?
- Process those CustomerIds in batches of say... 1000 by joining them to another query
- Insert all results into a physical table
so, in T-SQL might be..
-- create a temp table
create table #MyTempTable (
id int identity(1,1)
,customerid varchar(10)
)
-- populate that table
insert into #MyTempTable
select Customerid
from schema.Customers
-- create some vars for looping
declare i int, c int;
select i = 0;
select c = count(*) from #MyTempTable;
-- loop through the original set in 'chunks' of 1000
while i < c
begin
insert into SomeOtherTable
(CustomerId, CustomerAttribute)
select
o.CustomerId
,o.CustomerAttribute
from OtherTable o
join #MyTempTable t
on o.CustomerId = t.CustomerId
where
t.Id between i and i+1000 -- from 0 to 1000
set @i = i+1000 -- next loop will be from 1000 to 2000
end
Thanks
回答1:
Then, what about this
select startid = min(id) from customer;
select maxid = max(id) from customer;
i = startid
while i <= maxid
begin
with myCTE as (select ... from customer where id >= i and id < i + 1000)
insert into xxx (....)
select ....
from myCustomerChunk
join productcustomers pc on ....
join product p on ....
i = i+1000
end
That avoids doing all 700000 inserts in one statement which is what was probably grinding down your query... and the cursor made things just worse. PS: this is a hodge-podge of pseudo and actual code, so you will have to work out the real syntax.
回答2:
Both cursors and temp tables are poor solutions here. But cursors are the worse of the two. A CTE (Common Table Expression) would be a better solution, but even that is not necessary here. Why not just use a straight insert into... select statement from the get-go
Insert into xxx (CustomerId)
select
c.CustomerId
from customer c
join productcustomers pc on pc.customerid = c.customerid
join product p on p.productid = pc.productid
where
c.CustomerId > 1000;
If you do need to break it up, do not use a cursor or a temp table. I expect a CTE would be a better solution.
来源:https://stackoverflow.com/questions/29968879/oracle-loop-through-join-statement