为什么我不能使用SELECT ... FOR UPDATE with aggregate functions?

Bim*_*rM3 13 sql oracle plsql

我有一个应用程序,我在其中找到一组记录的数据库列的sum(),然后在单独的查询中使用该总和,类似于以下(组成表,但想法是相同的):

SELECT Sum(cost)
INTO v_cost_total
FROM materials
WHERE material_id >=0
AND material_id <= 10; 

[a little bit of interim work]

SELECT material_id, cost/v_cost_total
INTO v_material_id_collection, v_pct_collection
FROM materials
WHERE material_id >=0
AND material_id <= 10
FOR UPDATE; 
Run Code Online (Sandbox Code Playgroud)

但是,理论上有人可以在两个查询之间更新材料表上的成本列,在这种情况下,计算的百分比将关闭.

理想情况下,我只会在第一个查询中使用FOR UPDATE子句,但是当我尝试这个时,我收到一个错误:

ORA-01786: FOR UPDATE of this query expression is not allowed
Run Code Online (Sandbox Code Playgroud)

现在,解决方法不是问题 - 只需在查找Sum()之前执行额外的查询来锁定行,但该查询除了锁定表之外没有任何其他用途.虽然这个特定的例子并不耗时,但额外的查询可能会在某些情况下导致性能下降,并且它不是那么干净,所以我想避免这样做.

有谁知道为什么不允许这样做的特殊原因?在我的脑海中,FOR UPDATE子句应该只锁定与WHERE子句匹配的行 - 我不明白为什么我们正在对这些行做什么很重要.

编辑:看起来像SELECT ... FOR UPDATE可以与分析函数一起使用,如David Aldridge在下面所建议的那样.这是我用来证明这个工作的测试脚本.

SET serveroutput ON;

CREATE TABLE materials (
    material_id NUMBER(10,0),
    cost        NUMBER(10,2)
);
ALTER TABLE materials ADD PRIMARY KEY (material_id);
INSERT INTO materials VALUES (1,10);
INSERT INTO materials VALUES (2,30);
INSERT INTO materials VALUES (3,90);

<<LOCAL>>
DECLARE
    l_material_id materials.material_id%TYPE;
    l_cost        materials.cost%TYPE;
    l_total_cost  materials.cost%TYPE;

    CURSOR test IS
        SELECT material_id,
            cost,
            Sum(cost) OVER () total_cost
        FROM   materials
        WHERE  material_id BETWEEN 1 AND 3
        FOR UPDATE OF cost;
BEGIN
    OPEN test;
    FETCH test INTO l_material_id, l_cost, l_total_cost;
    Dbms_Output.put_line(l_material_id||' '||l_cost||' '||l_total_cost);
    FETCH test INTO l_material_id, l_cost, l_total_cost;
    Dbms_Output.put_line(l_material_id||' '||l_cost||' '||l_total_cost);
    FETCH test INTO l_material_id, l_cost, l_total_cost;
    Dbms_Output.put_line(l_material_id||' '||l_cost||' '||l_total_cost);
END LOCAL;
/
Run Code Online (Sandbox Code Playgroud)

这给出了输出:

1 10 130
2 30 130
3 90 130
Run Code Online (Sandbox Code Playgroud)

Gor*_*off 19

语法select . . . for update将记录锁定在表中以准备更新.进行聚合时,结果集不再引用原始行.

换句话说,数据库中没有要更新的记录.只有一个临时结果集.

  • @ BimmerM3...不,这比你想象的要困难得多.如何指定锁定`max()`的记录.是否只有`max()`值?它是整个表格,因为可以添加或更新大于最大值的新值吗? (9认同)