質問

私はPostgreSQLを初めて使用しますが、すでに最初の問題があります。

手動で手順を追って、トランザクションがどのように機能するかを理解するためのコードをいくつか書きました。

短くするために、ユーザーとムーブメントの2つのテーブルを作成しました。最初のテーブルには名前、メール、クレジットの列があり、2番目にはインポート元、インポート先、インポート先の列があります。

だから、私はこの方法を試していました:

BEGIN;
INSERT INTO movements (from, to, import) VALUES ('mary', 'steve', 600);
UPDATE users SET credit = credit - 600 WHERE name = 'mary';
UPDATE users SET credit = credit + 600 WHERE name = 'steve';
--here comes the problem!
IF (SELECT credit FROM users WHERE name = 'mary') < 0 THEN
 ROLLBACK;
END IF
COMMIT;

常にエラーが発生します:

  

エラー:&quot; IF&quot;またはその付近の構文エラー

どこで間違っているのですか?

P.S .:サンプル機能に焦点を合わせないでください。トランザクションを理解するための試用に過ぎません。そして今、IF句...

役に立ちましたか?

解決

Johannesがすでに言っているように、通常のSQLとストアドプロシージャ言語であるPL / pgSQLを混在させています。ヨハネスが提供するリンクは、ストアドプロシージャの概念を説明するものです。

これをスクリプトとしてやっていると思う?次々にステートメントを実行しますか?ストアドプロシージャまたは関数の内部で実行したいことだけを実行できるのではないかと思います。これは、この方法でステートメントを実行すると、すべてのステートメントが独立して他のステートメントに関する関係や情報を持たないためです。

さらに、IF ... THEN ... ELSE ... END IF;の使用方法の詳細については、次のリンクを参照してください。 plpgsql内の条件:リンク


編集:

その時点でROLLBACKが許可されているかどうかはわかりませんが(各ストアドプロシージャは既に独自のトランザクションにあるため)、詳細なドキュメント@ http://www.postgresql.org 。コードを含むサンプル関数を次に示します。他の構文も示します。

CREATE OR REPLACE FUNCTION public.test()
RETURNS integer AS
$
DECLARE
tempvar integer;

BEGIN    
     tempvar := 1;

     INSERT INTO movements (from, to, import) VALUES ('mary', 'steve', 600);
     UPDATE users SET credit = credit - 600 WHERE name = 'mary';
     UPDATE users SET credit = credit + 600 WHERE name = 'steve';

     --here comes the problem!
     IF (SELECT credit FROM users WHERE name = 'mary') < 0 THEN
        ROLLBACK;
     END IF;

     RETURN tempvar;
END
$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;

ただし、本当にこの道を行くのであれば、GUI DBマネージャーを使用することをお勧めします。これをすべて学ぶ方が簡単です。

他のヒント

プレーンな SQL を使用しているようですが、 IF ステートメントは PL / pgSQL PostgreSQLの一部である手続き型言語。

IF部分の変更を試みることができます:

IF (SELECT credit FROM users WHERE name = 'mary') < 0 THEN
 ROLLBACK;
END IF

to

SELECT SUM(credit) INTO v_credit FROM users WHERE name = 'mary';
IF (v_credit) < 0 THEN
 ROLLBACK;
END IF

v_creditが以前に定義した変数であると仮定します。 私見、Postgreは、あなたがそれが一意であることを非常に確信していても、SELECTクエリが複数の結果を返すと仮定します。そのため、事前に変数に値を割り当ててみてください。

コードを次のように書き直すことができるかどうかを避けたい場合:

BEGIN;

    INSERT INTO movements (from, to, import)    
    SELECT 'mary', 'steve', CASE credit < 600 WHEN TRUE THEN 0 ELSE 600 END;

    UPDATE users SET credit = credit - CASE credit < 600 WHEN TRUE THEN 0 ELSE 600 END    
    WHERE name = 'mary';

    UPDATE users u SET u.credit = u.credit + CASE v.credit < 600 WHEN TRUE THEN 0 ELSE 600 END    
    FROM users v    
    WHERE u.name = 'steve' and v.name = 'mary'

COMMIT;

はい、これは愚かです:)。

MicrosoftのSQLおよびT / SQLと同様に、正規のSQLとPL / pgSQLが正しい順序で混在している場合、それらを混在させることができます。 SQL / PLが混在するストアドプロシージャでシーケンスが重要な例を次に示します。

カーソル内に条件ステートメントをラップすることはできません。カーソルを条件ステートメント内に配置する必要があります。逆の順序でシーケンスを実行すると、「ERROR:syntax error at at or near&quot; IF&quot;」:

と同じエラーが表示されます。
CREATE OR REPLACE FUNCTION getSubsystemFaultListCount(_bunoid integer, _subsystem text, _starttime timestamp without time zone, _stoptime timestamp without time zone)
      RETURNS refcursor AS
    $BODY$
    DECLARE mycurs refcursor;
    BEGIN 
        IF _subsystem = 'ALL' THEN
            OPEN mycurs FOR
            SELECT  count(*), fs_fault.faultcode, fs_fault.downloadtime
            FROM    fs_fault
            WHERE   fs_fault.bunoid = _bunoid
                AND fs_fault.statusid IN(2, 4)
                AND fs_fault.downloadtime BETWEEN _starttime AND _stoptime
            GROUP BY fs_fault.faultcode, fs_fault.downloadtime;
            RETURN mycurs;
        ELSE
            OPEN mycurs FOR
            SELECT  count(*), fs_fault.faultcode, fs_fault.downloadtime
            FROM    fs_fault
            WHERE   fs_fault.bunoid = _bunoid
                AND fs_fault.subsystemid 
                    IN(SELECT id FROM fs_subsystem WHERE type = _subsystem)
                AND fs_fault.statusid IN(2, 4)
                AND fs_fault.downloadtime BETWEEN _starttime AND _stoptime
            GROUP BY fs_fault.faultcode, fs_fault.downloadtime;
            RETURN mycurs;
        END IF;

    END;
    $BODY$

私はPostGresSQLの初心者です。この関数は単なる例です。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top