用于更新大量数据的查询调优
Query tuning for update of large volumes of data
我有一个每天都在增长的庞大数据集。每一行的数据都有一定的更新运行。
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的等级数量也没有限制。
已编辑 :: 帮助计算此数据集的输出(值或作为列)。
- 确保总是运行相同的更新查询?
- 如果是,您应该针对有助于优化的查询创建实体化视图,因为当任何查询执行时,它将在实体化视图中搜索。
- 比较级别时-应该创建级别列的索引。
我相信 table 你提到的使用复合键。
- 创建具有这 2 列的单一索引。
使用 Oracle Hint 来指导优化器使用您的索引。
Update /*+ INDEX(tabName indexName) */ tabName
set column = value
where id = 1 and level = 1
非常感谢 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
不确定是否会命中 table 锁定,因为我们尝试 select table 而更新
记录中的数据不应依赖于其他记录中的数据。 output
是每个 ID data
的 运行 产品,这不应该被存储,而是被查询。 (否则,每当 data
发生更新时,您都必须重新计算所有值。)我们必须努力避免数据库中出现冗余,以避免出现一致性问题。
话虽如此,您必须编写一个查询。一种选择是递归查询。另一个是 window 函数。至于后者,有SUM OVER
、AVG 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";
我有一个每天都在增长的庞大数据集。每一行的数据都有一定的更新运行。
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的等级数量也没有限制。
已编辑 :: 帮助计算此数据集的输出(值或作为列)。
- 确保总是运行相同的更新查询?
- 如果是,您应该针对有助于优化的查询创建实体化视图,因为当任何查询执行时,它将在实体化视图中搜索。
- 比较级别时-应该创建级别列的索引。
我相信 table 你提到的使用复合键。
- 创建具有这 2 列的单一索引。
使用 Oracle Hint 来指导优化器使用您的索引。
Update /*+ INDEX(tabName indexName) */ tabName set column = value where id = 1 and level = 1
非常感谢 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
不确定是否会命中 table 锁定,因为我们尝试 select table 而更新
记录中的数据不应依赖于其他记录中的数据。 output
是每个 ID data
的 运行 产品,这不应该被存储,而是被查询。 (否则,每当 data
发生更新时,您都必须重新计算所有值。)我们必须努力避免数据库中出现冗余,以避免出现一致性问题。
话虽如此,您必须编写一个查询。一种选择是递归查询。另一个是 window 函数。至于后者,有SUM OVER
、AVG 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";