使用从同一个表计算的值更新表的最佳方法

时间:2011-10-26 10:26:24

标签: sql performance oracle sql-update

我在sql中完全是noob。我认为这是最好的方法;我有theese字段的收据表:

Receipt
-------
ReceiptID, AssociatedReceiptID, Value, Total

我想更新具有相同AssociatedReceiptID的所有行的Total字段及其Value字段的总和。所以我尝试了下一个sql语句:

UPDATE Receipt r1
SET Total = (SELECT sum(Value)
             FROM Receipt r2
             WHERE r2.AssociatedReceiptID = r1.AssociatedReceiptID
             GROUP BY r2.AssociatedReceiptID)

此表中有超过100000条记录,持续时间超过17小时。因为我正在更新我正在查询的同一个表,所以我决定将它拆分为两个更新语句,将sum结果存储在临时表(有)中,然后用这些值更新Receipt表。

UPDATE TemporaryTable t1
SET Total = (SELECT sum(Value)
             FROM Receipt r2
             WHERE r2.AssociatedReceiptID = t1.AssociatedReceiptID
             GROUP BY r2.AssociatedReceiptID)

UPDATE Receipt r1
SET Total = (SELECT Total
             FROM TemporaryTable t1
             WHERE t1.ReceiptID = r1.ReceiptID)

使用这些语句,更新过程需要6-7个小时。但我确信应该有更好的方法来做到这一点。所以,简而言之,这些是我的问题:

  • 你如何以更好的方式做到这一点?
  • 更新语句中的子查询每行更新一次,不是吗?因此,如果有10行具有相同的AssociatedReceiptID,则计算总和10次。如何在update语句中每个AssociatedReceiptID只计算一次总和?

提前致谢。

5 个答案:

答案 0 :(得分:2)

尝试在内存中创建临时表:

DECLARE @temp_receipts TABLE (
AssociatedReceiptID int,
sum_value int)

然后:

insert into @temp_receipts
SELECT AssociatedReceiptID, sum(Value)
FROM Receipt
GROUP BY AssociatedReceiptID

然后更新主表总数:

UPDATE Receipt r
SET Total = (SELECT sum_value
             FROM @temp_receipts tt
             WHERE r.AssociatedReceiptID = tt.AssociatedReceiptID)

但是,我会创建一个名为receipt_totals的表,然后使用它。在每个相关行中包含每个关联收据的总数是没有意义的。如果你是为了方便查询而考虑在收据和receipt_totals之间创建一个视图

答案 1 :(得分:1)

最初,你可能会按照你在问题中的建议去做。

对于一行的每次更改,我认为使用数据库触发器会更好。他们将更新每一行的值。

您可以阅读MySQL here中的触发器。

您可能需要将InnoDB用作存储引擎。

如果您不使用MySQL,请检查与您的DBMS相对应的参考。

答案 2 :(得分:0)

MERGE INTO Receipt r
USING (
        SELECT sum(Value) s, AssociatedReceiptID
        FROM Receipt           
        GROUP BY AssociatedReceiptID
      ) r_sum 
ON( r.AssociatedReceiptID = r_sum.AssociatedReceiptID)
WHEN MATCHED THEN UPDATE
set r.Total = r_sum.s
;

答案 3 :(得分:0)

我知道这是一个老问题,但我认为更好的方法就是这个。

UPDATE r1
    SET r1.Total = r2.sumValue

FROM Receipt r1
    INNER JOIN 
        (SELECT sum(Value) sumValue,AssociatedReceiptID
        FROM Receipt rSum
        GROUP BY rSum.AssociatedReceiptID) r2 ON r2.AssociatedReceiptID = r1.AssociatedReceiptID

这里只有一个查询,只计算一个。

希望它有用。

答案 4 :(得分:0)

对于大型表,将表复制到新表(并同时进行更改)比更新表要快得多(至少在Oracle DB上)。

例如:

update table1 set some_num = some_num +1 where year = 2010;

慢得多:

create table table1b as
  select (case when year = 2010 then some_num+1 else some_num) as some_num,
      other, columns, of, the, table
  from table1;
drop table1;
rename table1b to table1; -- also fix or recreate constraints

(对于从表中删除行也是如此:复制应保留在新表中的所有行,然后重命名它,而不是原始表上的常规DELETE)

所以在你的情况下,它将是:

create table ReceiptNew as
    select ReceiptID, AssociatedReceiptID, Value,
        sum(value) over (partition by AssociatedReceiptID) 
        as Total
    from Receipt;

drop table Receipt;
rename ReceiptNew to Receipt;

同样,你必须在表上重新设置约束(" NOT NULL"除外,它们会自动转移)。