2012-01-07 42 views
4

我們經常使用快速的一次性SQL文件來插入或更新現有數據庫中的數據。 SQL通常由開發人員編寫,在開發系統上進行測試,然後使用psql -U dbuser dbname < file.sql導入生產數據庫。替代PostgreSQL中的MySQL變量?

A(簡單)的例子可能是這樣的:

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') 
); 

雖然這個作品,有很多的子查詢重複的代碼。如果將臨時變量中的​​和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,你覺得什麼W¯¯應該是產生這種更新文件的最笨拙的方式?

回答

2

考慮使用CTEs或子查詢,查詢值一次和插入他們很多次。
這樣,您可以用標準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 command in the manual


這裏是我的測試設置臨時表的情況下,任何人都希望有一個快速瀏覽一下:

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); 
+0

非常優雅,謝謝。 – Zilk 2012-01-08 12:01:46

+2

CTE不能在8.4版本中工作,它不支持INSERT。你至少需要9.1版本。 – 2012-01-08 12:35:02

+0

@FrankHeikens:好點。這就是爲什麼我最終在這裏使用子查詢。 – 2012-01-08 14:20:14

4

使用價值()在SELECT,應該工作:

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 
1

這是一個老問題,但我發現,使用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