0

我正在处理一个 Oracle 查询,我非常需要让它更快。我将不胜感激任何建议。

  • 数据库是 Oracle,在 ExaData 集群上运行。
  • Oracle 版本:Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

我有两张桌子。

1) 交易:在商店购买 - TransactionID

2) TransactionItems:每次购买有 1..many items - TransactionID, ItemID

在每个表中,有两个标志:

  • 标志A:是/否
  • 标志B:是/否

查询需要:

  1. 为 TransactionItem 中的每条记录设置 FlagA 和 FlagB 的值。
  2. 根据 TransactionItem 中 Flags 的值,为 Transaction 中的每一行设置 FlagA 和 FlagB 的值

我已将查询分为 4 个步骤。

  1. 为 TransactionItem 设置标志 A 的值
  2. 为 TransactionItem 设置标志 B 的值
  3. 为事务设置标志 A 的值
  4. 为事务设置标志 B 的值

查询运行顺利。然而,这就是问题所在。有数十亿条Transaction记录,每个Transaction大约有7个Transaction Item。

这是它现在的速度:

  • 总时间:616 秒 / 10.27 分钟
  • 每秒处理 1,218 个事务/每分钟 73,000 个事务

我跟踪了每个步骤的处理时间:

  1. 为 TransactionItem 设置标志 A 的值

    • 4分52秒
  2. 为 TransactionItem 设置标志 B 的值

    • 3分26秒
  3. 为事务设置标志 A 的值

    • 1分6秒
  4. 为事务设置标志 B 的值

    • 0 分 51 秒

以下是我的完整查询。这是使用的其他表

产品

  • 每个 TransactionItem 都有一个 ProductId 每个产品都有一个 ProductCode。
  • 一个产品代码有多个产品

标记AproductCodes

  1. 包含分类为 FlagA 的 ProductCode 列表的单列

标记B产品代码

  1. 包含分类为 FlagB 的 ProductCode 列表的单列

交易支付

  1. 这是一个包含每笔交易的付款详情的事实表

Payment_Dim

  1. PaymentID 上的 TransactionPayment 链接
  2. 这是必需的,因为 FlagB 是基于 Payment_Dim.PaymentName 设置的

我有这些索引:

交易 1. TransactionID

TransactionItems 1. TransactionID 2. ProductID

产品 1. 产品 ID 2. 产品代码

FlagAproductCodes 1. ProductCode

FlagBproductCodes 1. ProductCode

付款 1. PaymentID 2. PaymentCode 3. Payment_Name

非常感谢您的帮助,谢谢

-- 1.   Set value of FlagA for TransactionItem

Update
          TransactionItems  Item

Set FlagA = 
      (
          Select 
                    Case 


                    When  
                              Item.FlagA_Qty = 0 Then 'N' -- this is the quantity of items purchased that fall into the FlagA category

                    When 
                              FlagA.ProductCode Is Null Then 'N'  

                    Else
                               'Y'

                    End 

          From  
                    Product Prod 
                    Left Join 
                              FlagAproductCodes FlagA 
                                        On Product.ProductCode = FlagA.ProductCode   


          Where  
Product.Prod_Id = Item.Prod_Id                    

        )
;



-- 2.  Set value of FlagB for TransactionItem

Update TransactionItems

Set  FlagB = 'Y'

Where ItemID In 
          (
          Select 
                    Trans_Items.ItemID
          From 
                    TransactionItems Trans_Items
                    Inner Join Product Prod 
                              On Trans_Items.Prod_Id = Product.Prod_Id 
                    Inner Join FlagBproductCodes FlagB 

                              On Product.ProductCode = FlagB.ProductCode
          Where 
                    (
                              Trans_Items.Gov_FlagA_Qty < Trans_Items.Item_Qty
                    ) 
                    AND 
                    (
                              Exists 
                              (Select Transaction_Payment_Fid 
                              From TransactionPayment Trans_Pay

                                        Inner Join Warehouse.Payment_Dim Pay_Dim 
                                                  On Trans_Pay.Payment_Id = Pay_Dim.Payment_Id
                              Where 
                                        Transaction_Fid = Trans_Items.Transaction_Fid 
                                        And Upper(Pay_Dim.Payment_Name) Like '%ABC%'
                              )
                    )
          )          
; 
Update TransactionItems
Set FlagB = 'N'
Where FlagB Is Null;






-- 3: Set FlagA for Transactions


Update
          Transactions  

Set 
          Gov_FlagA_Flag =
                    Case When Exists 
                              (Select ItemID 
                              From TransactionItems Item 
                              Where Item.Transaction_Fid = Transactions.Transaction_Fid 
                              and gov_FlagA_flag = 'Y') 
                              Then 'Y'
                    Else 'N'
                    End
;


-- 4: Set FlagB for Transactions


Update
          Transactions  

Set 
          FlagB =
                    Case When Exists 
                              (Select ItemID 
                              From TransactionItems Item 
                              Where Item.Transaction_Fid = Transactions.Transaction_Fid 
                              And FlagB = 'Y') 
                              Then 'Y'
                    Else 'N'
                    End
;
4

3 回答 3

0

我非常感谢您的指导——这真的很有帮助!

启用并行产生了巨大的变化!

ALTER SESSION 启用并行 DML;

再次感谢所有帮助

于 2014-09-02T15:27:05.607 回答
0

您需要研究并行执行,这可能是一个过于广泛的主题,无法在此处充分探讨。(而且我没有资格对此多说)。

同时,您可能会通过只更新每个表一次并减少偶然查找的次数来获得一些好处。这未经测试,但我认为涵盖与您针对 TransactionItems 的三个更新相同的逻辑,例如:

merge into TransactionItems TI
using (
  select P.Prod_ID,
    case when FAPC.ProductCode is null then 'N' else 'Y' end as FlagA,
    case when FBPC.ProductCode is null then 'N' else 'Y' end as FlagB
  from Product P
  left join FlagAproductCodes FAPC on FAPC.ProductCode = P.ProductCode
  left join FlagAproductCodes FBPC on FBPC.ProductCode = P.ProductCode
) temp
on (temp.Prod_id = TI.Prod_ID)
when matched then
update set TI.FlagA = case when temp.FlagA = 'Y' and TI.FlagA_Qty != 0
    then 'Y' else 'N' end,
  TI.FlagB = case when TI.FlagA_Qty < TI.Item_Qty
    and exists (
      select Transaction_Payment_Fid 
      from TransactionPayment TP
      join Payment_Dim PD on TP.Payment_Id = PD.Payment_Id
      where TP.Transaction_Fid = TI.Transaction_Fid 
      and upper(PD.Payment_Name) Like '%ABC%'
    ) then 'Y' else 'N' end
/

您可能更喜欢创建可更新的视图。但是在这么大的数据量上,它仍然需要很长时间。

这也可能有用

于 2014-08-22T16:29:35.357 回答
0

有趣的挑战。我的直接反应是分而治之——编写 PLSQL 以在扇区/ID 范围内进行操作,并经常提交。然后启动并行作业以在不同范围内运行,然后调整以找到最佳设置。如果运气好的话,表是分区的,那就更好了。

此外,尽管我来自一个一切都基于集合完成的时代,但在 PLSQL 甚至还没有被梦想过之前,您可能想要考虑在事务的基础上重新设计它,而不是您当前的基于集合的方法,您可以从中获取将主行更新为批量收集,然后使用批量收集来驱动明细表更新。我发现这可以更快,而且它确实让您在批量处理此操作时有更多的控制权。这也将为您提供在应该失败时重新启动的选项,例如快照太旧、归档日志已满等。如果发生这种情况,您不会想要重新开始。

于 2014-08-23T20:58:13.740 回答