सादा INSERT
INSERT INTO bar (description, foo_id)
SELECT val.description, f.id
FROM (
VALUES
(text 'testing', text 'blue') -- explicit type declaration; see below
, ('another row', 'red' )
, ('new row1' , 'purple') -- purple does not exist in foo, yet
, ('new row2' , 'purple')
) val (description, type)
LEFT JOIN foo f USING (type);
इसके LEFT [OUTER] JOINबजाय [INNER] JOINइसका मतलब यह है कि जब कोई मैच val नहीं मिलता है तो पंक्तियों को गिराया नहीं जाता है foo। इसके बजाय, के NULLलिए दर्ज किया गया है foo_id।
VALUESसबक्वेरी में अभिव्यक्ति के रूप में ही करता है @ ypercube के CTE। सामान्य टेबल एक्सप्रेशंस अतिरिक्त सुविधाएँ प्रदान करते हैं और बड़े प्रश्नों में पढ़ने में आसान होते हैं, लेकिन वे अनुकूलन बाधाओं के रूप में भी सामने आते हैं। जब उपर्युक्त में से किसी की आवश्यकता नहीं होती है, तो उपश्रेणी आम तौर पर थोड़ी तेज़ होती है।
idस्तंभ नाम के रूप में एक व्यापक प्रसार विरोधी पैटर्न है। होना चाहिए foo_idऔर bar_idया कुछ भी वर्णनात्मक। तालिकाओं के एक समूह में शामिल होने पर, आप सभी नाम वाले कई स्तंभों के साथ समाप्त होते हैं id...
सादे textया varcharइसके बजाय पर विचार करें varchar(n)। यदि आपको वास्तव में लंबाई प्रतिबंध लगाने की आवश्यकता है, तो एक CHECKबाधा जोड़ें :
आपको स्पष्ट प्रकार की जातियों को जोड़ने की आवश्यकता हो सकती है। चूंकि VALUESअभिव्यक्ति सीधे एक तालिका (जैसे INSERT ... VALUES ...) से जुड़ी नहीं है , इसलिए प्रकार व्युत्पन्न नहीं किए जा सकते हैं और डिफ़ॉल्ट डेटा प्रकार का उपयोग स्पष्ट प्रकार की घोषणा के बिना किया जाता है, जो सभी मामलों में काम नहीं कर सकता है। यह पहली पंक्ति में करने के लिए पर्याप्त है, बाकी लाइन में गिर जाएंगे।
INSERT उसी समय FK पंक्तियों को याद कर रहा है
यदि आप fooकिसी एकल SQL कथन में मक्खी पर गैर-मौजूद प्रविष्टियाँ बनाना चाहते हैं, तो CTE साधन हैं:
WITH sel AS (
SELECT val.description, val.type, f.id AS foo_id
FROM (
VALUES
(text 'testing', text 'blue')
, ('another row', 'red' )
, ('new row1' , 'purple')
, ('new row2' , 'purple')
) val (description, type)
LEFT JOIN foo f USING (type)
)
, ins AS (
INSERT INTO foo (type)
SELECT DISTINCT type FROM sel WHERE foo_id IS NULL
RETURNING id AS foo_id, type
)
INSERT INTO bar (description, foo_id)
SELECT sel.description, COALESCE(sel.foo_id, ins.foo_id)
FROM sel
LEFT JOIN ins USING (type);
नोट करने के लिए दो नई डमी पंक्तियाँ डालें। दोनों बैंगनी हैं , जो fooअभी तक अस्तित्व में नहीं है। पहले कथन की आवश्यकता बताने के लिए दो पंक्तियाँ ।DISTINCTINSERT
चरण-दर-चरण स्पष्टीकरण
1 CTE selइनपुट डेटा की कई गुना पंक्तियाँ प्रदान करता है। सबक्वेरी valके साथ VALUESअभिव्यक्ति स्रोत के रूप में एक मेज या सबक्वेरी साथ प्रतिस्थापित किया जा सकता है। इसके तत्काल बाद LEFT JOINकरने के लिए fooसंलग्न करने के लिए foo_idके लिए पहले से मौजूद typeपंक्तियों। अन्य सभी पंक्तियों को foo_id IS NULLइस तरह से मिलता है ।
दूसरा CTE अलग-अलग नए प्रकारों ( ) को insसम्मिलित करता है , और नई जनरेट करता है - साथ में पंक्तियों को सम्मिलित करने के लिए वापस जुड़ता है।foo_id IS NULLfoofoo_idtype
अंतिम बाहरी INSERTअब प्रत्येक पंक्ति के लिए foo.id सम्मिलित कर सकता है: या तो पहले से मौजूद प्रकार, या इसे चरण 2 में डाला गया था।
कड़ाई से बोलते हुए, दोनों आवेषण "समानांतर में" होते हैं, लेकिन चूंकि यह एक एकल कथन है, डिफ़ॉल्ट FOREIGN KEYबाधाओं की शिकायत नहीं होगी। डिफ़ॉल्ट रूप से कथन के अंत में संदर्भात्मक अखंडता लागू की जाती है।
Postgres 9.3 के लिए SQL फिडेल । (9.1 में वही काम करता है।)
यदि आप इन प्रश्नों में से कई को समवर्ती रूप से चलाते हैं, तो एक छोटी दौड़ की स्थिति है। के तहत और अधिक संबंधित प्रश्न पढ़ें यहाँ और यहाँ और यहाँ । वास्तव में केवल भारी समवर्ती लोड के तहत होता है, यदि कभी भी। एक अन्य जवाब में विज्ञापित कैशिंग समाधानों की तुलना में, मौका सुपर-टिनी है ।
बार-बार उपयोग के लिए कार्य
बार-बार उपयोग के लिए मैं एक SQL फ़ंक्शन बनाऊंगा जो कि अभिव्यक्ति के unnest(param)स्थान पर पैरामीटर के रूप में रिकॉर्ड की एक सरणी लेता है और उपयोग करता VALUESहै।
या, यदि अभिलेखों के सरणियों के लिए सिंटैक्स आपके लिए बहुत गड़बड़ है, तो पैरामीटर के रूप में एक अल्पविराम से अलग स्ट्रिंग का उपयोग करें _param। उदाहरण के लिए:
'description1,type1;description2,type2;description3,type3'
फिर VALUESउपरोक्त कथन में अभिव्यक्ति को बदलने के लिए इसका उपयोग करें :
SELECT split_part(x, ',', 1) AS description
split_part(x, ',', 2) AS type
FROM unnest(string_to_array(_param, ';')) x;
पोस्टग्रेज 9.5 में यूपीएसईआरटी के साथ कार्य
पैरामीटर पासिंग के लिए एक कस्टम रो टाइप बनाएं। हम इसके बिना कर सकते थे, लेकिन यह सरल है:
CREATE TYPE foobar AS (description text, type text);
समारोह:
CREATE OR REPLACE FUNCTION f_insert_foobar(VARIADIC _val foobar[])
RETURNS void AS
$func$
WITH val AS (SELECT * FROM unnest(_val)) -- well-known row type
, ins AS (
INSERT INTO foo AS f (type)
SELECT DISTINCT v.type -- DISTINCT!
FROM val v
ON CONFLICT(type) DO UPDATE -- type already exists
SET type = excluded.type WHERE FALSE -- never executed, but lock rows
RETURNING f.type, f.id
)
INSERT INTO bar AS b (description, foo_id)
SELECT v.description, COALESCE(f.id, i.id) -- assuming most types pre-exist
FROM val v
LEFT JOIN foo f USING (type) -- already existed
LEFT JOIN ins i USING (type) -- newly inserted
ON CONFLICT (description) DO UPDATE -- description already exists
SET foo_id = excluded.foo_id -- real UPSERT this time
WHERE b.foo_id IS DISTINCT FROM excluded.foo_id -- only if actually changed
$func$ LANGUAGE sql;
कॉल करें:
SELECT f_insert_foobar(
'(testing,blue)'
, '(another row,red)'
, '(new row1,purple)'
, '(new row2,purple)'
, '("with,comma",green)' -- added to demonstrate row syntax
);
समवर्ती लेनदेन के साथ पर्यावरण के लिए तेज और रॉक-ठोस।
उपरोक्त प्रश्नों के अलावा, यह ...
... लागू होता है SELECTया INSERTपर foo: कोई भी type, जो FK तालिका में मौजूद नहीं है, अभी तक डाला गया है। पहले से मौजूद अधिकांश प्रकारों को मानते हुए। पूरी तरह से सुनिश्चित होने और दौड़ की शर्तों को पूरा करने के लिए, मौजूदा पंक्तियों की हमें ज़रूरत है (ताकि समवर्ती लेनदेन हस्तक्षेप न कर सकें)। यदि आपके मामले के लिए यह बहुत अधिक पागल है, तो आप इसे बदल सकते हैं:
ON CONFLICT(type) DO UPDATE -- type already exists
SET type = excluded.type WHERE FALSE -- never executed, but lock rows
साथ में
ON CONFLICT(type) DO NOTHING
... लागू होता है INSERTया UPDATE(सही "यूपीएसईआरटी") पर bar: यदि descriptionपहले से मौजूद है तो इसका typeअद्यतन किया जाता है:
ON CONFLICT (description) DO UPDATE -- description already exists
SET foo_id = excluded.foo_id -- real UPSERT this time
WHERE b.foo_id IS DISTINCT FROM excluded.foo_id -- only if actually changed
लेकिन केवल अगर typeवास्तव में बदलता है:
... मानों को एक VARIADICपैरामीटर के साथ ही ज्ञात पंक्ति प्रकारों से गुजरता है । डिफ़ॉल्ट अधिकतम 100 मापदंडों पर ध्यान दें! की तुलना करें:
कई पंक्तियों को पास करने के कई अन्य तरीके हैं ...
सम्बंधित: