0

これは、大規模なデータ セットでは非常に長い時間がかかるようです。最初と最後の 3 つのクエリを 1 つに結合すると、高速になりますか? 何が高速化するかについて誰かが意見を持っていますか? それは有り難いです。

update "detail" set bal = (units * amount) where code_type = 'AB'; 
update "detail" set bal = (units * amount) where code_type = 'CD';
update "detail" set bal = (units * amount) where code_type = 'EF';
update "detail" set bal = (units * amount * -1) where code_type = 'GH';
update "detail" set bal = (units * amount * -1) where code_type = 'IK';
update "detail" set bal = (units * amount * -1) where code_type = 'LM';
update "detail" set bal = 0 where code_type = 'NO';

さらに -

update bill set pay = 
  (select round(sum(bd1.bal),2) from "detail" bd1 where 
  bd1.inv = bill.inv and 
  (bd1.code_type = 'AB' or bd1.code_type = 'CD')); 
update bill set pay = 0 where pay is null;
update bill set cost = 
  (select round(sum(bd2.bal),2) from "detail" bd2 where 
  bd2.inv = bill.inv and 
  (not (bd2.code_type = 'AB' or bd2.code_type = 'CD'))); 
update bill set cost = 0 where cost is null;
update bill set balance = round(cost + pay,2);

ありがとう

4

4 に答える 4

3

テーブル全体を更新していて、それを 12 回更新しているため、パフォーマンスはおそらく悪臭を放っています。テーブルが非常に大きい場合は、時間がかかります。また、これらの 2 つの埋め込みサブクエリは、それぞれ行ごとに 1 回実行されます。ああ。

次の frankenquery は、すべてを 1 つのステートメントにまとめます。それでもテーブル全体にヒットする必要がありますが、少なくとも 1 回しかヒットしません。構文をチェックしたり、データに対してテストしたりすることはできませんが、これまたはそれに非常によく似たものが機能するはずです。

EDITED、これを 2 つの更新に分割します (したがって、2 つのテーブル スキャンが必要です)

UPDATE Detail
 set
   bal = case
           when code_type in ('AB','CD','EF') then  bi.units * bi.amount
           when code_type in ('gh','ik','lm') then -bi.units * bi.amount
           when code_type = 'NO' then 0
           else bal  --  If none of the above, change nothing
         end

UPDATE Bill
 set
   payments = isnull(bd1.amount, payments)  --  This changes nothing if nothing was calculated
  ,pay = case
           when pay is null then 0
           else pay
         end
   --  Ok, problem with cost: what if calculated amount is 0 and current value is non-zero?
   --  I've insufficient data here to correctly resolve all the possible permutations
  ,cost = case
            when bd2.amount is not null then cost
            when cost is null then 0
            else cost
          end
  ,balance = round(charges + isnull(bd1.amount, bi.payments), 2)
 from Bill bi
  --  These subqueries could be combined into one using similar CASE logic,
  --  and it would probably perform better (one table scan vs. two).  I'm
  --  leaving it this way for demonstration, and to keep the overall query
  --  a bit simpler.
  left outer join (select
                      inv
                     ,round(sum(bd1.bal), 2) amount
                    from detail
                    where code_type = 'AB'
                     or code_type = 'CD'
                    group by inv) bd1
   on bd1.inv = bi.inv  --  ADDED in second edit
  left outer join (select 
                      inv  --  RENAMED in second edit
                     ,round(sum(bd2.bal), 2) amount
                    from detail
                    where code_type <> 'AB'
                     and code_type <> 'CD'
                    group by inv) bd2  --  RENAMED in second edit
   on bd2.invoive = bi.inv  --  ADDED in second edit

道徳:CASEステートメントは、SQL 開発者の親友になることができます。

于 2012-04-26T17:21:41.547 に答える
1

最初のクエリは次のように記述できます。

UPDATE "detail"
SET    bal =
       CASE
              WHEN code_type = 'NO'
              THEN 0
              ELSE
                     CASE
                            WHEN code_type IN ('AB',
                                               'CD',
                                               'EF')THEN 1
                            ELSE -1
                     END
       END        * (units * amount)
WHERE  code_type IN ('AB','CD','EF','GH','IK','KL','NO');

code_typeにインデックスを付けると、更新する必要のある適格な行がすぐに除外されます。テーブル構造の残りの部分によっては、速度が異なる場合がありますが、これが最速のバージョンになると思います。ここで重要なのはコードタイプに基づく0、1、または-1だけなので、それをチェックしてから、units*amountを掛けます。

更新:更新の2番目のバッチも1つのバッチで書き込むことができます。

UPDATE b
SET    payments = COALESCE(x.bal,0),
       cost     = COALESCE(y.bal,0),
       balance  = ROUND(charges + COALESCE(x.bal,0),2)
FROM   bill b
       LEFT OUTER JOIN
              ( SELECT  ROUND(SUM(bd1.bal),2) AS bal,
                       inv
              FROM     "detail" bd1
              WHERE    bd1.code_type IN ('AB',
                                         'CD')
              GROUP BY bd1.inv
              )
              x
       ON     x.inv = b.inv
       LEFT OUTER JOIN
              ( SELECT  ROUND(SUM(bd2.bal),2) AS bal,
                       invoice
              FROM     "detail" bd2
              WHERE    bd2.code_type NOT IN ('AB',
                                             'CD')
              GROUP BY bd2.invoice
              )
              y
       ON     y.invoice = b.invoice

速度を改善するためのヒント:

  • 詳細テーブルのインデックス、code_typeおよびinv列、インデックスにbalを含める
  • 詳細テーブル、code_type、およびinvoice列のインデックスには、インデックスにbalが含まれています。
  • 請求書テーブル、請求書、請求書のインデックス。
于 2012-04-26T18:28:42.357 に答える
0

最初の3つのステートメントは、次のような単一のステートメントで実行できると思います:

update detail set bal = (units * amount) where code_type in( 'AB','CD' )

次の 3 つのステートメントについても同じことができます。

于 2012-04-26T17:07:31.770 に答える
0

現在、クエリごとに再計算units * amountしています。units * amount * -1一度計算units * amountして最初のいくつかのクエリを組み合わせると、パフォーマンスが向上するはずですが、どの程度かはわかりません。

declare @total int
set @total = units * amount
update "detail" set bal = @total where code_type in ('AB', 'CD', 'EF');  
update "detail" set bal = (@total * -1) where code_type in ('GH', 'IK','LM');  
update "detail" set bal = 0 where code_type = 'NO'; 
于 2012-04-26T17:09:26.777 に答える