查询调整以更新大量数据

时间:2017-05-31 05:49:45

标签: sql oracle performance plsql oracle12c

我有一个每天都在增长的庞大数据集。对每一行的数据都有一定的更新。

Table Structure :
Id    Level   Data     Output
--    -----   -------  --------- 
1     1         12.3      12.3
1     2         42.5     522.75
1     3        129     67434.75
2     1          3.12      3.12
2     2          0.12      0.3744
2     3         32.1      12.01824
2     4         39.1     469.913184
3     1          0.83      0.83
3     2          4.21      3.4943
4     1          3.49      3.49

对于一个id,输出是数据与其先前级别的数据相乘[data * 1 for level = 1]

现在,每天获得的ID数量没有限制,也没有限制每个ID的数量。

已编辑::帮助计算此数据集的输出(值或列)。

3 个答案:

答案 0 :(得分:0)

  1. 确保始终为更新运行相同的查询?
  2. 如果是,您应该针对有助于优化的查询创建物化视图,因为当执行任何查询时,它将在物化视图中搜索。
  3. 比较级别时,应该创建级别列的索引。

答案 1 :(得分:0)

我相信你提到的表使用复合键。

  1. 创建具有此2列的单个索引。
  2. 使用Oracle提示使用索引指向优化程序。

    Update  /*+ INDEX(tabName indexName) */ tabName
    set column = value
    where id = 1 and level = 1
    
  3. 非常感谢APC能够启发它想要实现的目标。 也许我应该举例说明一点

       Update  /*+ INDEX(tabName indexName) */ tabA t1
       set output = data * exp(sum(log(select data from tabA t2 where t2.level < t1.level)
       where id = 1 and level = 3
    

    由于我们尝试在更新时选择表格

    ,因此不确定是否会锁定表锁定

答案 2 :(得分:0)

记录中的数据不应该依赖于其他记录中的数据。如果output是每个ID data的运行产品,则不应存储,而是查询。 (否则,每当data发生更新时,您都必须重新计算所有值。)我们必须努力避免数据库中的冗余,以避免一致性问题。

说完这个,你必须写一个查询。一种选择是递归查询。另一种是窗口功能。至于后者,有SUM OVERAVG OVER等,但没有MULTIPLY OVER等。所以我们必须创建这个聚合函数:

CREATE OR REPLACE TYPE AggregateProduct 
AS OBJECT 
(
  product NUMBER,

  STATIC FUNCTION ODCIAggregateInitialize(actx IN OUT AggregateProduct) RETURN NUMBER,
  MEMBER FUNCTION ODCIAggregateIterate(self  IN OUT AggregateProduct, val IN NUMBER) RETURN NUMBER,
  MEMBER FUNCTION ODCIAggregateTerminate(self IN AggregateProduct, returnValue OUT NUMBER, flags IN NUMBER) RETURN NUMBER,
  MEMBER FUNCTION ODCIAggregateMerge(self IN OUT AggregateProduct, ctx2 IN AggregateProduct) RETURN NUMBER
);

CREATE OR REPLACE TYPE BODY AggregateProduct AS
  STATIC FUNCTION ODCIAggregateInitialize(actx IN OUT AggregateProduct) RETURN NUMBER IS 
  BEGIN
    IF actx IS NULL THEN
      actx := AggregateProduct(null);
    ELSE
      actx.product := null;
    END IF;
    RETURN ODCIConst.Success;
  END;

  MEMBER FUNCTION ODCIAggregateIterate(self IN OUT AggregateProduct, val IN NUMBER) RETURN NUMBER IS
  BEGIN
    IF val IS NOT NULL THEN
      self.product := NVL(self.product, 1) * val;
    END IF;
    RETURN ODCIConst.Success;
  END;

  MEMBER FUNCTION ODCIAggregateTerminate(self IN AggregateProduct, ReturnValue OUT NUMBER, flags IN NUMBER) RETURN NUMBER IS
  BEGIN
    returnValue := self.product;
    RETURN ODCIConst.Success;
  END;

  MEMBER FUNCTION ODCIAggregateMerge(self IN OUT AggregateProduct, ctx2 IN AggregateProduct) RETURN NUMBER IS
  BEGIN
    self.product := self.product * ctx2.product;
    RETURN ODCIConst.Success;
  END;
END;

CREATE OR REPLACE FUNCTION agg_multiply(x NUMBER) RETURN NUMBER PARALLEL_ENABLE
  AGGREGATE USING AggregateProduct;

然后查询将是:

select 
  id, "level", data,
  agg_multiply(data) over(partition by id order by "level") as output
from mytable
order by id, "level";

(顺便说一下,你应该避免使用level作为列名,因为这是Oracle中的保留字。)

如上所述,另一个选项是递归查询,但在哪里会有趣呢? ; - )

multiplied(id, "level", data, output) as
(
  select id, "level", data, data as output from mytable where "level" = 1
  union all
  select mytable.id, mytable."level", mytable.data, mytable.data * multiplied.output
  from multiplied
  join mytable on mytable.id = multiplied.id and mytable."level" = multiplied."level" + 1
)
select * 
from multiplied 
order by id, "level";