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 次。如果桌子很大,那将需要时间。此外,这两个嵌入式子查询将每行运行一次。哎哟。

以下 frankenquery 将所有内容汇总到一个语句中。它仍然必须击中整个桌子,但至少它只击中一次。我无法检查语法或根据数据对其进行测试,但是这个或非常类似的东西应该可以工作。

已编辑,将其拆分为两个更新(因此,需要进行两次表扫描)

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,这就是您检查的内容,然后乘以单位*数量。

更新:您的第二批更新也可以一批写入:

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 包含在索引中。
  • 账单表、inv 和发票上的索引。
于 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 * amountunits * 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 回答