4

既存のデータベースにデータを挿入または更新するために、迅速な 1 回限りの SQL ファイルをよく使用します。SQL は通常、開発者によって記述され、開発システムでテストされてから、psql -U dbuser dbname < file.sql.

(些細な)例は次のようになります。

INSERT INTO employees (
    company_id,
    name,
    position,
    created_by,
    last_modified_by
) VALUES
(
    (SELECT id FROM companies WHERE name = 'Acme Fellowship'),
    'Frodo Baggins',
    'Ring bearer',
    (SELECT id FROM users WHERE login = 'admin'),
    (SELECT id FROM users WHERE login = 'admin')
),
(
    (SELECT id FROM companies WHERE name = 'Acme Fellowship'),
    'Samwise Gamgee',
    'Rope bearer',
    (SELECT id FROM users WHERE login = 'admin'),
    (SELECT id FROM users WHERE login = 'admin')
),
(
    (SELECT id FROM companies WHERE name = 'Acme Fellowship'),
    'Peregrin Took',
    'Ent rider',
    (SELECT id FROM users WHERE login = 'admin'),
    (SELECT id FROM users WHERE login = 'admin')
);

これは機能しますが、サブクエリには多くの反復コードがあります。companies.id関連する値をおよびusers.id一時変数に格納すると便利です (より効率的でエラーが発生しにくくなります) 。この解釈された例では、パフォーマンスの違いはおそらく最小限ですが、実際にはより複雑なクエリと更新があり、更新/挿入されたレコードが 3 つ以上あることがよくあります。

MySQL 用に記述された同じ例は、次のようになります。

SELECT @company_id := id FROM companies WHERE name = 'Acme Fellowship';
SELECT @admin_id := id FROM users WHERE login = 'admin';
INSERT INTO employees (
    company_id,
    name,
    position,
    created_by,
    last_modified_by
) VALUES
(@company_id, 'Frodo Baggins',  'Ring bearer', @admin_id, @admin_id),
(@company_id, 'Samwise Gamgee', 'Rope bearer', @admin_id, @admin_id),
(@company_id, 'Peregrin Took',  'Ent rider',   @admin_id, @admin_id);

PostgreSQL で同様のことを達成する方法はありますか?

私が見たもの:

  • psql のセッション変数 (with \set): クエリ結果の保存には使用できません
  • plpgsql: プロシージャでのみ使用できます (まだ 8.4 を実行しています)
  • 一時テーブル: 見苦しく複雑なステートメントを作成せずにそれらを使用する方法がわかりません

Postgres に直接相当するものがない場合、この種の更新ファイルを作成する最も不器用な方法は何だと思いますか?

4

3 に答える 3

4

SELECT で VALUES() を使用すると、次のように動作するはずです。

INSERT INTO employees (
    company_id,
    name,
    position,
    created_by,
    last_modified_by
)
SELECT
    (SELECT id FROM companies WHERE name = 'Acme Fellowship'),
    name,
    position,
    (SELECT id FROM users WHERE login = 'admin'),
    (SELECT id FROM users WHERE login = 'admin')
FROM
    (VALUES -- all your new content here
        ('Frodo Baggins',  'Ring bearer'), 
        ('Samwise Gamgee', 'Rope bearer'), 
        ('Peregrin Took',  'Ent rider')
    ) content(name, position); -- use some aliases to make it readable
于 2012-01-07T10:21:22.257 に答える
2

これは古い質問ですが、WITH ステートメントを使用すると、作業が楽になることがわかりました :)

WITH c AS (
    SELECT company_id,
    FROM companies
    WHERE name = 'Acme Fellowship'
), u AS (
    SELECT *
    FROM users
    WHERE login = 'admin'
), n AS (
    SELECT *
    FROM
        (VALUES -- all your new content here
            ('Frodo Baggins',  'Ring bearer'), 
            ('Samwise Gamgee', 'Rope bearer'), 
            ('Peregrin Took',  'Ent rider')
        ) content(name, position)
)
INSERT INTO employees (
    company_id,
    name,
    position,
    created_by,
    last_modified_by
)
SELECT c.company_id, n.name, n.position, u.id, u.id
FROM c, u, n
于 2014-04-15T15:12:10.400 に答える
2

CTEまたはサブクエリを使用して値を一度クエリし、それらを何度も挿入することを検討してください。このようにして、MySQL スタイルの変数を標準の SQL
に置き換えることができます。

INSERT INTO employees
      (company_id, name, position, created_by, last_modified_by)
SELECT c.id      , name, position, u.id      , u.id
FROM  (SELECT id FROM companies WHERE name = 'Acme Fellowship') c
     ,(SELECT id FROM users WHERE login = 'admin') u
     ,(VALUES
         ('Frodo Baggins',  'Ring bearer') 
        ,('Samwise Gamgee', 'Rope bearer')
        ,('Peregrin Took',  'Ent rider')
      ) v(name, position)

とが実際には一意であるcompanies.nameと仮定します。users.login複数のヒットは、挿入される行を増やします。マニュアルの INSERT コマンド
について読んでください。


誰かが簡単に見たい場合に備えて、一時テーブルを使用した私のテスト設定を次に示します。

CREATE TEMP TABLE companies (id int, name text);
INSERT INTO companies VALUES (17, 'Acme Fellowship');

CREATE TEMP TABLE users (id int, login text);
INSERT INTO users VALUES (9, 'admin');

CREATE TEMP TABLE employees (
 company_id int
,name text
,position text
,created_by int
,last_modified_by int);
于 2012-01-08T04:06:49.283 に答える