2012-04-26 57 views
0

这似乎在大型数据集上花费了很长时间。将第一个和最后3个查询合并为1会更快吗?有没有人有什么可以加快速度的建议?我很感激。可以优化此SQL语句吗?

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); 

感谢

回答

3

性能可能太臭,因为你要更新整个表,你正在更新其十二倍。如果桌子很大,那就需要 时间。而且,这两个嵌入式子查询每行将每行运行一次。哎哟。

以下frankquery将所有内容组合成一个语句。它仍然必须击中整个桌子,但至少它只能做一次。我无法检查语法 或者对数据进行测试,但是这个或者非常类似于它的东西应该可以工作。

编辑,分成两个更新(这样,两个表扫描requried)

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开发人员的最好的朋友。

+0

OP的语句更新了两个不同的表“'detail”'和'bill'。但你只是更新'账单'。它必须是2个frankqueries,而不是1,是吗? – 2012-04-26 17:28:27

+0

呃,我错过了 - 没有意识到那里真的有两个问题。我会尽力将它分成两部分。 – 2012-04-26 17:47:16

+0

感谢您的帮助,第一部分效果很好!我很抱歉,因为在我的第二个陈述中,我错误地标记了一些字段。我不知道这会造成多大的差异。但如果你不介意再看一遍。以下是我所做的更改。 1. bd2.inv = bill.inv和2.更新账单设置成本= 0,其中成本为空; 3.更新账单设置余额= round(cost + pay,2); – Trevor 2012-04-27 18:06:07

0

我认为首先3条语句,你可以做到这一点在单个语句是这样的:

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

同样可以为未来3做声明也。

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'; 
+0

“set @ total”中定义的'units'和'amount'在哪里?这似乎并不奏效。 – cairnz 2012-04-26 18:20:37

+0

@ cairnz - 看起来他们是OP的查询中的变量,虽然他不清楚。如果它们与'bal'具有相同'detail'表中的字段,那么他的数据库没有被标准化,这也可能会导致他的问题。 – dj18 2012-04-26 20:18:33

+0

变量将以“@”作为前缀。很明显,它是表格中的列,在这种情况下,您的查询会写入各种错误的数据。 – cairnz 2012-04-27 07:17:29

1

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 *的数量。

更新: 你更新的第二批也可以在一个批次中写到:

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和发票列中,在索引中包含bal。
  • 账单表,inv和发票索引。