2013-08-27 22 views
10

レコードセットのデータベース列のsum()を見つけて、後でそのような合計を別のクエリで使用するアプリケーションがあります。これは次のようになります(テーブルは作成されましたが、アイデアは同じです)。集計関数でSELECT ... FOR UPDATEを使用できないのはなぜですか?

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; 

しかし理論的に誰かが2つのクエリ間でマテリアルテーブルのコスト列を更新することができます。その場合、計算されたパーセントはオフになります。

理想的には、私は最初のクエリにFOR UPDATE句を使用だろうが、私がいることをしようとすると、私はエラーを取得:今

ORA-01786: FOR UPDATE of this query expression is not allowed 

を、回避策は問題ではありません - ちょうど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; 
/

1 10 130 
2 30 130 
3 90 130 
+1

'select ... for update'で解析関数(sumまたはratio_to_report)を使用することはできますか?私は現時点でこれをテストするためのデータベースを用意していませんので、わかりません... –

答えて

15

構文select . . . for updateロックは、更新の準備のために表に記録します。集計を実行すると、結果セットはもはや元の行を参照しません。

つまり、更新するデータベースにはレコードがありません。一時的な結果セットがあります。

+0

結果セット自体は一時的ですが、結果セットの整合性は基になるデータに依存しているようですこれは便利な機能です私はオラクルが同意しないと思う。しかたがない。 – BimmerM3

+6

@ BimmerM3。 。 。いいえ、あなたが考えるよりもはるかに難しい問題です。 'max()'のためにロックするレコードをどのように指定するのでしょうか?それは 'max()'の値だけのものでしょうか?新しい値がmaxより大きく追加または更新される可能性があるため、テーブル全体になりますか? –

+0

ああ、良い点。私はmax()やmin()などの関数については考えなかった。応答していただきありがとうございます。 – BimmerM3

3

あなたが何か試してみてください:

<<LOCAL>> 
declare 
    material_id materials.material_id%Type; 
    cost  materials.cost%Type; 
    total_cost materials.cost%Type; 
begin 
    select material_id, 
     cost, 
     sum(cost) over() total_cost 
    into local.material_id, 
     local.cost, 
     local.total_cost 
    from materials 
    where material_id between 1 and 3 
    for update of cost; 

    ... 

end local; 

を最初の行は、あなたの総コストを与えるが、それはすべての行を選択して、理論的には、彼らがロックすることができました。

これが許可されているかどうかわかりませんが、気になるかどうか聞いてみてください。

+0

興味深い - これは動作します!私はオリジナルの投稿に私のテストスクリプトを入れます。 – BimmerM3

+0

@ BimmerM3。 。 。これは、問題に対する洞察に富んだアプローチです(そして、アップボーディングする価値があります)。しかし、それは "面白い"ではありません。結果セットは元の行セットです。集計の問題は、結果セットが元の行ではなく*であることです。 –

0

あなたの問題ですか?しかし、理論上、誰かが2つのクエリ間でマテリアルテーブルのコスト列を更新する可能性があります。その場合、計算されたパーセントはオフになります。あなたがテーブルをロックしたいのはなぜ

SELECT material_id, cost/(SELECT Sum(cost) 
    FROM materials 
    WHERE material_id >=0 
    AND material_id <= 10) 
INTO v_material_id_collection, v_pct_collection 
FROM materials 
WHERE material_id >=0 
AND material_id <= 10; 

:その場合は

は、おそらくあなたは、単に内部クエリを使用することができますか?他のアプリケーションがその時間にそのテーブルを更新しようとすると失敗する可能性がありますか?

+1

他のアプリケーションは、ロックを解除するまで待つ必要がありますが、正しくプログラムされていると失敗しません。 – BimmerM3

+0

これは、他のアプリケーションのプログラミング方法によって異なります。 – Arnab

+0

私がここで話している実際のアプリケーションでは、私は戻って行を更新するので、どちらかの方法で更新後にロックされます。 FOR ... UPDATE句を使用してロックすると、その間に他の誰もデータを変更しないことが保証されます。この場合、実際の競合の可能性は非常に小さく、データの整合性は、別のセッションが失敗するリスクがあるほど重要です。 – BimmerM3

関連する問題