【问题标题】:Oracle Loop Through Join StatementOracle 循环通过连接语句
【发布时间】:2015-04-30 13:20:53
【问题描述】:

我需要在 PL-SQL 中执行一个语句,该语句选择一个 ID,对这些 ID 的子集执行连接...在下面的示例中,我有大约 700000 个客户,并且一个 比此示例中的简单 while 循环中显示的更复杂的查询...我的性能很差,我很好奇将我当前的 PL-SQL 分割成“块”是否会提高性能?

目前:

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;
  1. 将 CustomerId 列表选择到“临时”存储容器中 - 不确定选项是什么?
  2. 通过将这些 CustomerId 加入到另一个查询中,分批处理这些 CustomerId... 1000
  3. 将所有结果插入物理表中

所以,在 T-SQL 中可能是..

-- 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

谢谢

【问题讨论】:

    标签: oracle plsql oracle11g


    【解决方案1】:

    那么,这个呢

    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
    

    这避免了在一个语句中执行所有 700000 次插入,这可能会削弱您的查询……而光标使事情变得更糟。 PS:这是一个伪代码和实际代码的大杂烩,所以你必须弄清楚真正的语法。

    【讨论】:

    • 是的,看起来它可能是獾.. tbh 查询的微调不是这里的主要问题,这是我如何将我的 MSSQL 大脑转换为 PLSQL 的真实代码的语法是主要问题;)
    【解决方案2】:

    游标和临时表在这里都是糟糕的解决方案。但是游标是两者中最差的。 CTE(公用表表达式)将是一个更好的解决方案,但即使这样也不是必需的。为什么不从一开始就直接使用 insert into... select 语句

    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;
    

    如果您确实需要将其分解,请不要使用游标或临时表。我希望 CTE 会是更好的解决方案。

    【讨论】:

    • 最初我从 cte 开始,但我遇到的问题是表模式非常 erm.. 次优意味着如果我尝试为近 100 万客户选择结果,系统停止运行...通过试验/错误我知道一次介于 1000 到 10000 个 CustomerIds 之间的某个位置是一个最佳点,所以我也想在任何给定时间“分块”选择/插入过程我不知道CustomerIds 是什么,所以 > 1000 并不是一个真正可行的解决方案,我的 CustomerIds 范围可能从 154785 到 258789 .. 我只想随意抓取其中的 1000 个,然后继续...
    • “系统停止运行……” 你这是什么意思?而且,您是在选择(例如,通过 Toad 或 SQL*Plus 等),还是尝试了insert as ... select...?由于上下文切换较少,我希望所有行的插入总体上比将插入分解成单独的块更快。
    • 您看到我的其他答案了吗,使用 cte 并按块分解 ID?
    • 是的,谢谢,我在英国,所以我想你是在我工作日结束时发布的;)
    猜你喜欢
    • 2013-12-29
    • 1970-01-01
    • 1970-01-01
    • 2019-01-07
    • 2021-02-15
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    相关资源
    最近更新 更多