4

我们经常使用快速一次性 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在这个解释的示例中,性能差异可能很小,但实际上我们确实有更复杂的查询和更新,并且通常有超过三个更新/插入的记录。

为 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 的会话变量(带\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

考虑使用CTE或子查询来查询一次值并多次插入它们。
这样,您可以用标准 SQL替换 MySQL 样式变量。

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.nameusers.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 回答
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 回答