2

I am kind of new in Oracle. I am trying to create a package that has several functions. This is the pseudocode of what I want to do

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

Running this kind of code takes a long time because TableA and TableB is a very large table, and I am only getting 1 entry per record from the tables.

I would want to create a temporary table (TempTableA, TempTableB) within the package that will temporarily store all records based on the startdate and enddate, so that when I try to retrieve the TransactionDate and Amount for each rec, I will only refer to the TempTables (which is smaller than TableA and TableB).

I also want to take into consideration if the UserID is not found in TableA and TableB. So basically, when there are no records found in TableA and TableB, I also want the entry in the output, but it is indicated that the user is inactive.

Thank you for all your help.

4

2 回答 2

3

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 中不是这样。更好的查询是要走的路,尤其是拥抱集合的喜悦!

于 2012-09-26T07:47:19.247 回答
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
于 2012-09-26T07:04:24.447 回答