2013-10-15 46 views
1

我有引用其他表的表:如何在postgresql中插入其他表的值?

CREATE TABLE scratch 
(
    id SERIAL PRIMARY KEY, 
    name TEXT NOT NULL, 
    rep_id INT NOT NULL REFERENCES reps, 
    term_id INT REFERENCES terms 
); 
CREATE TABLE reps (
    id SERIAL PRIMARY KEY, 
    rep TEXT NOT NULL UNIQUE 
); 
CREATE TABLE terms (
    id SERIAL PRIMARY KEY, 
    terms TEXT NOT NULL UNIQUE 
); 

我想補充一個新的記錄劃傷給出的,則代表方面值,即我既沒有相應的rep_id也不term_id

現在是我唯一的想法就是:

insert into scratch (name, rep_id, term_id) 
      values ('aaa', (select id from reps where rep='Dracula' limit 1), (select id from terms where terms='prepaid' limit 1)); 

我的問題是這樣的。我試圖使用參數化查詢API(從使用節點postgres的包節點),其中一個插入件的查詢看起來像這樣:

insert into scratch (name, rep_id, term_id) values ($1, $2, $3); 

,然後$ 1,$ 2和$ 3個值的陣列作爲傳遞一個單獨的論點。最後,當我對參數化查詢感到滿意時,想法是將它們提升爲準備好的語句,以便利用最有效和最安全的方式來查詢數據庫。

但是,我很困惑我怎麼能用我的例子來做到這一點,在這個例子中,不同的表格必須被子查詢。

請幫忙。

P.S.

我使用的是PostgreSQL 9.2,對於PostgreSQL特定的解決方案沒有問題。

編輯1

C:\Users\markk>psql -U postgres 
psql (9.2.4) 
WARNING: Console code page (437) differs from Windows code page (1252) 
     8-bit characters might not work correctly. See psql reference 
     page "Notes for Windows users" for details. 
Type "help" for help. 

postgres=# \c dummy 
WARNING: Console code page (437) differs from Windows code page (1252) 
     8-bit characters might not work correctly. See psql reference 
     page "Notes for Windows users" for details. 
You are now connected to database "dummy" as user "postgres". 
dummy=# DROP TABLE scratch; 
DROP TABLE 
dummy=# CREATE TABLE scratch 
dummy-# (
dummy(# id SERIAL NOT NULL PRIMARY KEY, 
dummy(# name text NOT NULL UNIQUE, 
dummy(# rep_id integer NOT NULL, 
dummy(# term_id integer 
dummy(#); 
NOTICE: CREATE TABLE will create implicit sequence "scratch_id_seq" for serial column "scratch.id" 
NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index "scratch_pkey" for table "scratch" 
NOTICE: CREATE TABLE/UNIQUE will create implicit index "scratch_name_key" for table "scratch" 
CREATE TABLE 
dummy=# DEALLOCATE insert_scratch; 
ERROR: prepared statement "insert_scratch" does not exist 
dummy=# PREPARE insert_scratch (text, text, text) AS 
dummy-# INSERT INTO scratch (name, rep_id, term_id) 
dummy-# SELECT $1, r.id, t.id 
dummy-# FROM reps r, terms t 
dummy-# WHERE r.rep = $2 AND t.terms = $3 
dummy-# RETURNING id, name, $2 rep, $3 terms; 
PREPARE 
dummy=# DEALLOCATE insert_scratch2; 
ERROR: prepared statement "insert_scratch2" does not exist 
dummy=# PREPARE insert_scratch2 (text, text, text) AS 
dummy-# INSERT INTO scratch (name, rep_id, term_id) 
dummy-#    VALUES ($1, (SELECT id FROM reps WHERE rep=$2 LIMIT 1), (SELECT id FROM terms WHERE terms=$3 LIMIT 1)) 
dummy-# RETURNING id, name, $2 rep, $3 terms; 
PREPARE 
dummy=# EXECUTE insert_scratch ('abc', 'Snowhite', ''); 
id | name | rep | terms 
----+------+-----+------- 
(0 rows) 


INSERT 0 0 
dummy=# EXECUTE insert_scratch2 ('abc', 'Snowhite', ''); 
id | name | rep | terms 
----+------+----------+------- 
    1 | abc | Snowhite | 
(1 row) 


INSERT 0 1 
dummy=# EXECUTE insert_scratch ('abcd', 'Snowhite', '30 days'); 
id | name | rep | terms 
----+------+----------+--------- 
    2 | abcd | Snowhite | 30 days 
(1 row) 


INSERT 0 1 
dummy=# EXECUTE insert_scratch2 ('abcd2', 'Snowhite', '30 days'); 
id | name | rep | terms 
----+-------+----------+--------- 
    3 | abcd2 | Snowhite | 30 days 
(1 row) 


INSERT 0 1 
dummy=# 

EDIT 2

我們可以利用該rep_id需要的事實,即使terms_id是可選的,使用INSERT-SELECT以下版本:

PREPARE insert_scratch (text, text, text) AS 
INSERT INTO scratch (name, rep_id, term_id) 
SELECT $1, r.id, t.id 
FROM reps r 
LEFT JOIN terms t ON t.terms = $3 
WHERE r.rep = $2 
RETURNING id, name, $2 rep, $3 terms; 

然而,這個版本有兩個問題:

  1. 沒有區分是否有缺失terms值(即, '')和一個無效的值(即完全從術語表中缺失的非空值)。兩者都被視爲失蹤條款。 (但插入有兩個子查詢會遇到同樣的問題)
  2. 該版本取決於需要rep的事實。但是如果rep_id也是可選的呢?

EDIT 3

找到的第2項所述溶液 - 被要求對代表消除依賴性。再加上使用WHERE語句有問題,如果代表無效 - 它只是插入0行,而我想在這種情況下顯式失敗,SQL不會失敗。我的解決方法就是使用虛擬一行CTE:

PREPARE insert_scratch (text, text, text) AS 
WITH stub(x) AS (VALUES (0)) 
INSERT INTO scratch (name, rep_id, term_id) 
SELECT $1, r.id, t.id 
FROM stub 
LEFT JOIN terms t ON t.terms = $3 
LEFT JOIN reps r ON r.rep = $2 
RETURNING id, name, rep_id, term_id; 

如果代表缺失或無效,此SQL將嘗試插入NULL到rep_id領域,因爲該領域是NOT NULL錯誤將提高 - 正是我需要。如果進一步我決定讓代表可選 - 沒問題,同樣的SQL也適用於此。

回答

2
INSERT into scratch (name, rep_id, term_id) 
SELECT 'aaa' 
     , r.id 
     , t.id 
FROM reps r , terms t -- essentially an outer join 
WHERE r.rep = 'Dracula' 
    AND t.terms = 'prepaid' 
     ; 

注:

  • 你不需要醜LIMIT S,因爲r.rep和t.terms是唯一的(候選鍵)
  • ,你可以通過一個FROM a FULL OUTER JOIN b更換FROM a, b
  • scratch表格可能需要UNIQUE約束on (rep_id, term_it)term_id的可空性是有問題的)

UPDATE:同爲準備查詢as found in the Documentation

PREPARE hoppa (text, text,text) AS 
     INSERT into scratch (name, rep_id, term_id) 
     SELECT $1 , r.id , t.id 
     FROM reps r , terms t -- essentially an outer join 
     WHERE r.rep = $2 
     AND t.terms = $3 
     ; 
EXECUTE hoppa ('bbb', 'Dracula' , 'prepaid'); 

SELECT * FROM scratch; 

UPDATE2:測試數據

DROP SCHEMA tmp CASCADE; 
CREATE SCHEMA tmp ; 
SET search_path=tmp; 

CREATE TABLE reps (id SERIAL PRIMARY KEY, rep TEXT NOT NULL UNIQUE); 
CREATE TABLE terms (id SERIAL PRIMARY KEY, terms TEXT NOT NULL UNIQUE); 
CREATE TABLE scratch (id SERIAL PRIMARY KEY, name TEXT NOT NULL, rep_id INT NOT NULL REFERENCES reps, term_id INT REFERENCES terms); 

INSERT INTO reps(rep) VALUES('Dracula'); 
INSERT INTO terms(terms) VALUES('prepaid'); 

結果:

NOTICE: drop cascades to 3 other objects 
DETAIL: drop cascades to table tmp.reps 
drop cascades to table tmp.terms 
drop cascades to table tmp.scratch 
DROP SCHEMA 
CREATE SCHEMA 
SET 
CREATE TABLE 
CREATE TABLE 
CREATE TABLE 
INSERT 0 1 
INSERT 0 1 
INSERT 0 1 
PREPARE 
INSERT 0 1 
id | name | rep_id | term_id 
----+------+--------+--------- 
    1 | aaa |  1 |  1 
    2 | bbb |  1 |  1 
(2 rows) 
+0

你能不能改變你的例子,利用'PREPARE'聲明?我想看看你的例子看起來像$ 1,$ 2,... – mark

+0

由於一些奇怪的原因,這與INSERT插入不插入任何東西給我。你有沒有在一個實際的PostgreSQL數據庫上檢查它? – mark

+0

@mark是的,我執行並檢查。查看更新。 – joop

相關問題