2

私はOracleの初心者です。いくつかの機能を持つパッケージを作成しようとしています。これは私がやりたいことの擬似コードです

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)を作成し、開始日と終了日に基づいてすべてのレコードを一時的に保存します。そのため、各レコードのTransactionDateとAmountを取得しようとすると、 TempTables(TableAおよびTableBよりも小さい)。

TableAとTableBにUserIDが見つからない場合も考慮に入れたいと思います。したがって、基本的に、TableAとTableBにレコードが見つからない場合は、出力にもエントリが必要ですが、ユーザーが非アクティブであることが示されます。

よろしくお願いします。

4

2 に答える 2

3

SQL はセットベースの言語です。それぞれが単一の行を返す多くのステートメントを実行するよりも、必要なすべての行を返す 1 つのステートメントを実行する方がはるかに効率的です。

すべての行を一度に取得する 1 つの方法を次に示します。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 では、パフォーマンスに関する質問に対する答えが「グローバル一時テーブルを使用する」であることが判明することは非常にまれです。より良いクエリが進むべき道であり、特に、Joy of Sets を採用することです!

于 2012-09-26T07:47:19.247 に答える
2

おそらく、1 つのクエリで実行する方がよいでしょう。たとえば、次のようになります。

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 に答える