web-dev-qa-db-ja.com

PostgreSQLは匿名コードブロックからデータを返す方法は?

テーブルmytable(id、 "someLongURI"、status、 "userId")と有効なクエリがあります。

UPDATE          mytable
SET             status = 'IN_WORK'
WHERE           "someLongURI" IN (
    SELECT      "someLongURI"
    FROM        mytable
    WHERE       status = 'UNUSED'
    AND         pg_try_advisory_xact_lock(id)
    ORDER BY    id ASC 
    LIMIT       1
    FOR UPDATE 
)
RETURNING       id, "someLongURI";

しかし、ここで「userId」パラメーターを確認し、これに基づいて既存の行を選択するか、更新する(そして更新された行を受け取る)必要があります。そのような何か(MySQLではこれはうまくいくでしょう):

IF NOT EXISTS (
    SELECT          1
    FROM            mytable tbl
    WHERE           tbl."userId" = 123
)
THEN
    UPDATE          mytable tbl
    SET             tbl.status = 'IN_WORK',
                    tbl."userId" = 123
    WHERE           tbl."someLongURI" IN (
        SELECT      "someLongURI"
        FROM        tbl.mytable
        WHERE       tbl.status = 'UNUSED'
            AND     pg_try_advisory_xact_lock(id)
        ORDER BY    id ASC 
        LIMIT       1
        FOR UPDATE 
    )
    RETURNING   tbl.id, tbl."someLongURI";
ELSE
    SELECT          tbl.id, tbl."someLongURI"
    FROM            mytable tbl
    WHERE           tbl."userId" = 123;
END IF;

DO $$ BEGIN {...} END $$;で囲みましたが、うまくいきませんでした。私が最後に来たこと:

DO
    RETURNS TABLE (id INT4, "someLongURI" TEXT) AS
$$
DECLARE
    "_userId" INT4;
BEGIN
    "_userId" = 123;

    IF NOT EXISTS (
        SELECT          1
        FROM            mytable tbl
        WHERE           tbl."userId" = "_userId"
    )
    THEN
        UPDATE          mytable tbl
        SET             tbl.status = 'IN_WORK'
                        tbl."userId" = "_userId"
        WHERE           tbl."someLongURI" IN (
            SELECT      "someLongURI"
            FROM        tbl.mytable
            WHERE       tbl.status = 'UNUSED'
                AND     pg_try_advisory_xact_lock(id)
            ORDER BY    id ASC 
            LIMIT       1
            FOR UPDATE 
        )
        RETURNING       tbl.id, tbl."someLongURI";
    ELSE
        SELECT          tbl.id, tbl."someLongURI"
        FROM            mytable tbl
        WHERE           tbl."userId" = "_userId";
    END IF;
END $$;

クエリの結果を受け取るために、この匿名コードブロック内に「RETURNS」またはその他のステートメントを追加する必要がある場所を理解できません。

2
Naryck

Postgresでは、IFステートメントを必要とせずに、単一の CTEを変更するデータ でそれを行うことができます。

WITH data AS (
    SELECT          tbl.id, tbl."someLongURI"
    FROM            mytable tbl
    WHERE           tbl."userId" = 123;
), changed AS (
    UPDATE          mytable tbl
    SET             tbl.status = 'IN_WORK',
                    tbl."userId" = 123
    WHERE           tbl."someLongURI" IN (
        SELECT      "someLongURI"
        FROM        tbl.mytable
        WHERE       tbl.status = 'UNUSED'
            AND     pg_try_advisory_xact_lock(id)
        ORDER BY    id ASC 
        LIMIT       1
        FOR UPDATE 
    )
    AND NOT EXISTS (SELECT * FROM data)
    RETURNING   tbl.id, tbl."someLongURI"
)
SELECT *
FROM changed
UNION ALL
SELECT *
FROM data
WHERE NOT EXISTS (SELECT * FROM changed);