【问题标题】:Temporary tables in Packages - Oracle包中的临时表 - Oracle
【发布时间】:2012-09-17 17:57:30
【问题描述】:

我是甲骨文的新手。 我正在尝试创建一个具有多种功能的包。 这是我想做的伪代码

function FunctionA(UserID, startdate, enddate)
  /* Select TransactionDate, Amount
     from TableA
     where TransactionDate between startdate and enddate
     and TableA.UserID = UserID */
  Return TransactionDate, Amount
end FunctionA

function FunctionB(UserID, startdate, enddate)
  /* Select TransactionDate, Amount
     from TableB
     where TransactionDate between startdate and enddate
     and TableB.UserID = UserID */
  Return TransactionDate, Amount
end FunctionA

TYPE TRANSACTION_REC IS RECORD(
          TransactionDate    DATE,
          TransactionAmt     NUMBER);

function MainFunction(startdate, enddate)
  return TBL
  is
  vTrans TRANSACTION_REC;
begin
  FOR rec IN
    ( Select UserID, UserName, UserStatus
      from UserTable
      where EntryDate between startdate and enddate )
  LOOP
    vTrans := FunctionA(rec.UserID, startdate, enddate)

    if vTrans.TransactionDate is null then
       vTrans := FunctionB(rec.UserID, startdate, enddate)

       if vTrans.TransactionDate is null then
           rec.UserStatus := 'Inactive'
       endif;
    endif;
  END Loop;

  PIPE ROW(USER_OBJ_TYPE(rec.UserID,
                       rec.UserName,
                       rec.UserStatus,
                       vTrans.TransactionDate,
                       vTtans.TransactionAmt));
end MainFunction

运行这种代码需要很长时间,因为 TableA 和 TableB 是一个非常大的表,我只能从表中获得每条记录的 1 个条目。

我想在包中创建一个临时表(TempTableA,TempTableB),它将根据 startdate 和 enddate 临时存储所有记录,这样当我尝试检索每个记录的 TransactionDate 和 Amount 时,我只会参考 TempTables(比 TableA 和 TableB 小)。

如果在 TableA 和 TableB 中找不到 UserID,我也想考虑一下。所以基本上,当在TableA和TableB中都没有找到记录时,我也想要输出中的条目,但是表明用户处于非活动状态。

感谢您的所有帮助。

【问题讨论】:

  • 为什么不优化每张表使用的SQL呢?如果您在每个选择中仅获得一行,则意味着您的用户 ID 是唯一的。您在该列上有(唯一)索引吗?如果没有,您应该添加一个(甚至是主键)。基于唯一索引检索单行将非常快(并且几乎与表的大小无关)
  • 您的附加伪代码坦率地说是垃圾。这是无法编译的大杂烩。如果您希望有人为您编写代码,您应该发布您的实际完整需求,而不是让人们从一些笨拙的 PL/SQL 中猜测它们。当然,SO 上的受访者的角色不是为你做你的工作,但也许有人会觉得特别慷慨。

标签: performance oracle packages


【解决方案1】:

SQL 是一种基于集合的语言。执行一条语句返回所需的所有行比执行许多语句都返回一行要高效得多。

这是一次获取所有行的一种方法。它使用通用表表达式,因为您读取了整个 UserTable 并且您应该只执行一次。

with cte as 
  (select UserID
         , UserStatus
   from UserTable )
select cte.UserID
       , cte.UserStatus
       , TableA.TransactionDate 
       , TableA.Amount  
from cte join TableA   
     on (cte.UserID = TableA.UserID)
where cte.UserStatus = 'A'
and TableA.TransactionDate between startdate and enddate
union
select cte.UserID
       , cte.UserStatus
       , TableB.TransactionDate 
       , TableB.Amount  
from cte join TableB  
     on (cte.UserID = TableB.UserID)
where cte.UserStatus != 'A'
and TableB.TransactionDate between startdate and enddate

顺便说一下,要小心使用临时表。它们不像 T-SQL 中的临时表。它们是永久堆表,只是它们的数据是临时的。这意味着填充临时表是一个昂贵的过程,因为数据库将所有这些行写入磁盘。因此,我们需要确定从临时表中读取数据集所获得的性能提升值得所有这些写入的开销。

您的代码肯定不是这种情况。事实上,性能问题的答案是“使用全局临时表”的情况非常罕见,至少在 Oracle 中不是这样。更好的查询是要走的路,尤其是拥抱集合的喜悦!

【讨论】:

  • 谢谢@APC。感谢您的帮助:) 不过,我还有另一个案例要考虑。我忘记考虑在 TableA 和 TableB 中没有找到记录的情况(请参阅我的问题中更新的伪代码)抱歉造成混淆。
【解决方案2】:

最好在一个查询中完成,例如:

Select UserTable.UserID, UserTable.UserName, UserTable.UserStatus
      ,TableA.TransactionDate AS ATransactionDate
      ,TableA.Amount          AS AAmount
      ,TableB.TransactionDate AS BTransactionDate
      ,TableB.Amount          AS BAmount
from UserTable
left join TableA
  on (UserTable.UserID = TableA.UserID)
left join TableB
  on (UserTable.UserID = TableB.UserID)
where UserTable.EntryDate between startdate and enddate

【讨论】:

    猜你喜欢
    • 2010-12-24
    • 2015-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-10
    • 1970-01-01
    • 2015-04-23
    • 1970-01-01
    相关资源
    最近更新 更多