PostgreSQL“ IF”構文エラー
-
03-07-2019 - |
質問
私は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の初心者です。この関数は単なる例です。