Postgresql के साथ "डुप्लिकेट कुंजी अद्यतन" ("डुप्लिकेट कुंजी अद्यतन" पर "इंसर्ट इंसर्ट") का अनुकरण कैसे करें?


140

कुछ SQL सर्वरों में एक सुविधा होती है, जहां INSERTइसे छोड़ दिया जाए तो यह प्राथमिक / अद्वितीय कुंजी बाधा का उल्लंघन करेगा। उदाहरण के लिए, MySQL है INSERT IGNORE

अनुकरण करने के लिए सबसे अच्छा तरीका क्या है INSERT IGNOREऔर ON DUPLICATE KEY UPDATEPostgreSQL के साथ?




6
9.5 में से, यह मूल रूप से संभव है: stackoverflow.com/a/34639631/4418
वॉरेन

MySQL का अनुकरण करना: ON DUPLICATE KEY UPDATEPgSQL 9.5 पर अभी भी कुछ हद तक असंभव है, क्योंकि PgSQL ON CLAUSEसमकक्ष को आपको बाधा नाम प्रदान करने की आवश्यकता होती है, जबकि MySQL किसी भी बाधा को परिभाषित करने की आवश्यकता के बिना कब्जा कर सकता है। यह मुझे पुनर्लेखन प्रश्नों के बिना इस सुविधा को "अनुकरण" करने से रोकता है।
NeverEndingQueue

जवाबों:


35

एक अद्यतन करने के लिए प्रयास करें। यदि यह किसी भी पंक्ति को संशोधित नहीं करता है जिसका अर्थ है कि यह मौजूद नहीं था, इसलिए एक प्रविष्टि करें। जाहिर है, आप ऐसा लेनदेन के अंदर करते हैं।

यदि आप क्लाइंट कोड पर अतिरिक्त कोड नहीं डालना चाहते हैं, तो आप निश्चित रूप से इसे एक फ़ंक्शन में लपेट सकते हैं। आपको उस सोच में बहुत दुर्लभ दौड़ की स्थिति के लिए एक लूप की भी आवश्यकता है।

प्रलेखन में इसका एक उदाहरण है: http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html , उदाहरण के लिए 40-2 दाईं ओर नीचे।

यह आमतौर पर सबसे आसान तरीका है। आप नियमों के साथ कुछ जादू कर सकते हैं, लेकिन यह बहुत अधिक गड़बड़ होने की संभावना है। मैं किसी भी दिन रैप-इन-फ़ंक्शन दृष्टिकोण की सिफारिश करूंगा।

यह एकल पंक्ति, या कुछ पंक्ति, मानों के लिए काम करता है। यदि आप किसी उपश्रेणी से उदाहरण के लिए बड़ी मात्रा में पंक्तियों के साथ काम कर रहे हैं, तो आप इसे दो प्रश्नों में विभाजित कर रहे हैं, एक INSERT के लिए और एक UPDATE के लिए (उपयुक्त जॉइन / सबसेलेक्ट के रूप में) - अपने मुख्य लिखने की कोई आवश्यकता नहीं है फ़िल्टर दो बार)


4
"यदि आप बड़ी मात्रा में पंक्तियों के साथ काम कर रहे हैं" तो बिल्कुल यही मेरा मामला है। मैं पंक्तियों को अपडेट / सम्मिलित करना चाहता हूं और mysql के साथ मैं बिना किसी लूपिंग के केवल एक क्वेरी के साथ ऐसा कर सकता हूं। अब मुझे आश्चर्य है कि क्या यह पोस्टग्रैसक्ल के साथ भी संभव है: बल्क अपडेट या इंसर्ट में सिर्फ एक क्वेरी का उपयोग करने के लिए। आप कहते हैं: "आप इसे दो प्रश्नों में विभाजित कर रहे हैं, एक INSERT के लिए और एक UPDATE के लिए" लेकिन मैं एक प्रविष्टि कैसे कर सकता हूं जो डुप्लिकेट कुंजियों पर त्रुटियों को नहीं फेंकती है? (यानी। "INSERT
IGNORE

4
मैग्नस का मतलब था कि आप इस तरह से एक क्वेरी का उपयोग करते हैं: "लेनदेन शुरू करें; टेस्ट के अनुसार अस्थायी टेबल अस्थायी_टेम का चयन करें * परीक्षण से जहां गलत है; 'data_file.csv' से अस्थायी_ कॉपी; लॉक टेबल टेस्ट; अपडेट टेस्ट सेट डेटा = अस्थायी_बिलिटी से अस्थायी_बिलिटी; test.id = Tem_table.id; टेस्ट में डालें * Tem_table में से सेलेक्ट करें जहाँ id नहीं है (टेस्ट से id चुनें) "
Tometzky

25
अपडेट: PostgreSQL 9.5 के साथ यह अब उतना ही सरल है INSERT ... ON CONFLICT DO NOTHING;। भी जवाब देखने stackoverflow.com/a/34639631/2091700
13

महत्वपूर्ण, एसक्यूएल मानक MERGEहै नहीं एक संगामिति सुरक्षित Upsert, जब तक आप एक लेने के LOCK TABLEपहले। लोग इसका उपयोग इस तरह से करते हैं, लेकिन यह गलत है।
क्रेग रिंगर

1
V9.5 के साथ यह अब एक 'मूल' सुविधा है, इसलिए कृपया @Alphaaa टिप्पणी (उत्तर का विज्ञापन करने वाली टिप्पणी का विज्ञापन करें) की जाँच करें
Camilo Delvasto

178

PostgreSQL 9.5 के साथ, यह अब मूल कार्यक्षमता है (जैसे MySQL कई वर्षों से पड़ा है ):

INSERT ... ON CONFLICT DO NOTHING / UPDATE ("UPSERT")

9.5 "यूपीएसईआरटी" संचालन के लिए समर्थन लाता है। INSERT को ON CONFLICT DO UPDATE / IGNORE क्लॉज को स्वीकार करने के लिए बढ़ाया गया है। यह खंड एक डुप्लिकेट उल्लंघन की स्थिति में एक वैकल्पिक कार्रवाई निर्दिष्ट करता है।

...

नए सिंटैक्स का और उदाहरण:

INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1) 
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;

100

संपादित करें: यदि आपने वॉरेन के उत्तर को याद किया है , तो PG9.5 में यह मूल रूप से है; उन्नयन का समय!


बिल करविन के उत्तर पर, यह समझने के लिए कि एक नियम आधारित दृष्टिकोण कैसा दिखेगा (उसी डीबी में एक और स्कीमा से स्थानांतरण, और एक बहु-स्तंभ प्राथमिक कुंजी के साथ):

CREATE RULE "my_table_on_duplicate_ignore" AS ON INSERT TO "my_table"
  WHERE EXISTS(SELECT 1 FROM my_table 
                WHERE (pk_col_1, pk_col_2)=(NEW.pk_col_1, NEW.pk_col_2))
  DO INSTEAD NOTHING;
INSERT INTO my_table SELECT * FROM another_schema.my_table WHERE some_cond;
DROP RULE "my_table_on_duplicate_ignore" ON "my_table";

नोट: नियम सभी INSERTऑपरेशनों पर लागू होता है जब तक कि नियम को गिरा नहीं दिया जाता है, इसलिए काफी तदर्थ नहीं।


@sema का मतलब है कि अगर another_schema.my_tableबाधाओं के अनुसार डुप्लिकेट शामिल हैं my_table?
ईओघनम

2
@ EoghanM I ने नियम को postgresql 9.3 में परीक्षण किया और अभी भी कई पंक्ति सम्मिलित विवरण जैसे कि INSERT INTO "my_table" (a, b), (a, b) के साथ डुप्लिकेट सम्मिलित कर सकता है; (उस पंक्ति (ए, बी) मान लिया जाये कि अभी तक "My_table" में मौजूद नहीं था।)
सेमा

@sema, gotcha - इसका मतलब यह होना चाहिए कि नियम को सभी डेटा डालने के प्रारंभ में निष्पादित किया जाता है, और प्रत्येक पंक्ति को सम्मिलित करने के बाद पुन: उपयोग नहीं किया जाता है। एक दृष्टिकोण आपके डेटा को किसी अन्य अस्थायी तालिका में सम्मिलित करने का होगा, जिसमें कोई बाधा न हो और फिर कर रहे होंINSERT INTO "my_table" SELECT DISTINCT ON (pk_col_1, pk_col_2) * FROM the_tmp_table;
EoghanM

@ EoghanM डुप्लिकेट बाधाओं को अस्थायी रूप से और सम्मिलित करने पर डुप्लिकेट को स्वीकार करने के लिए एक और तरीका है, लेकिन डुप्लिकेट को बाद में हटा देंDELETE FROM my_table WHERE ctid IN (SELECT ctid FROM (SELECT ctid,ROW_NUMBER() OVER (PARTITION BY pk_col_1,pk_col_2) AS rn FROM my_table) AS dups WHERE dups.rn > 1);
sema

मुझे @sema द्वारा वर्णित समस्या हो रही है। यदि मैं एक सम्मिलित करता हूं (ए, बी), (ए, बी), तो यह एक त्रुटि फेंकता है। क्या त्रुटियों को दबाने का एक तरीका है, इस मामले में भी?
डिओगो मेलो

35

उन लोगों के लिए, जिनके पास 9.5 या उससे अधिक पोस्टग्रैट्स हैं, नए CONFLICT DO NOTHING सिंटैक्स पर काम करना चाहिए:

INSERT INTO target_table (field_one, field_two, field_three ) 
SELECT field_one, field_two, field_three
FROM source_table
ON CONFLICT (field_one) DO NOTHING;

हममें से जिन लोगों के पास पहले वाला संस्करण है, उनके लिए यह सही जॉइन काम करेगा:

INSERT INTO target_table (field_one, field_two, field_three )
SELECT source_table.field_one, source_table.field_two, source_table.field_three
FROM source_table 
LEFT JOIN target_table ON source_table.field_one = target_table.field_one
WHERE target_table.field_one IS NULL;

समवर्ती वातावरण में एक बड़ी प्रविष्टि बनाते समय दूसरा दृष्टिकोण काम नहीं करता है। आप एक प्राप्त Unique violation: 7 ERROR: duplicate key value violates unique constraintजब target_tableएक और पंक्ति यह में डाला था , जबकि , इस क्वेरी निष्पादित किया जा रहा था, तो अपनी चाबी, वास्तव में, एक दूसरे के नकली। मुझे विश्वास है कि ताला लगाने target_tableसे मदद मिलेगी, लेकिन जाहिर तौर पर समस्या का सामना करना पड़ेगा।
जी। काश्तानोव

1
ON CONFLICT (field_one) DO NOTHINGउत्तर का सबसे अच्छा हिस्सा है।
एबेल कैलेजो

24

इंसर्ट इग्नोर लॉजिक पाने के लिए आप नीचे जैसा कुछ कर सकते हैं। मैंने पाया कि शाब्दिक मूल्यों के एक चुनिंदा बयान से बस सबसे अच्छा काम किया है, फिर आप डुप्लिकेट कुंजियों को नॉट एग्जॉस्ट क्लॉज के साथ हटा सकते हैं। डुप्लिकेट लॉजिक पर अपडेट प्राप्त करने के लिए मुझे संदेह है कि एक pl / pgsql लूप आवश्यक होगा।

INSERT INTO manager.vin_manufacturer
(SELECT * FROM( VALUES
  ('935',' Citroën Brazil','Citroën'),
  ('ABC', 'Toyota', 'Toyota'),
  ('ZOM',' OM','OM')
  ) as tmp (vin_manufacturer_id, manufacturer_desc, make_desc)
  WHERE NOT EXISTS (
    --ignore anything that has already been inserted
    SELECT 1 FROM manager.vin_manufacturer m where m.vin_manufacturer_id = tmp.vin_manufacturer_id)
)

क्या होगा अगर tmp में डुप्लिकेट पंक्ति है, जो हो सकती है?
हेनले चिउ

आप हमेशा अलग कीवर्ड के साथ चयन कर सकते हैं।
कीयो

5
एक FYI के रूप में, "व्हॉट्स नॉट एग्जिट" ट्रिक कई ट्रांजेक्शन पर काम नहीं करती है क्योंकि अलग-अलग ट्रांजैक्शन दूसरे ट्रांजैक्शन से नए जोड़े गए डेटा को नहीं देख सकते हैं।
डेव जोहानसन

21
INSERT INTO mytable(col1,col2) 
    SELECT 'val1','val2' 
    WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')

एक ही काम करने की कोशिश करने वाले कई लेनदेन का क्या प्रभाव पड़ता है? क्या यह संभव है कि जहां निष्पादन के बीच मौजूद नहीं है और डालने के लिए कुछ अन्य लेनदेन निष्पादित करते हुए एक पंक्ति सम्मिलित होती है? और अगर Postgres इसे रोक सकता है, तो क्या यह पोस्टग्रैजिनेशन उन सभी लेन-देन में सिंक्रोनाइज़ेशन का एक बिंदु पेश नहीं कर रहा है, जब वे इसे मारते हैं?
14αrΚhικ

यह कई लेनदेन के साथ काम नहीं करता है, क्योंकि नए जोड़े गए डेटा अन्य लेनदेन के लिए दिखाई नहीं देते हैं।
डेव जोहानसन

12

ऐसा लगता है कि PostgreSQL एक नियम नामक एक वस्तु का समर्थन करता है ।

http://www.postgresql.org/docs/current/static/rules-update.html

आप ON INSERTकिसी दिए गए तालिका के लिए एक नियम बना सकते हैं , यह कर NOTHINGयदि एक पंक्ति में दिए गए प्राथमिक कुंजी मान के साथ मौजूद है, या यदि कोई पंक्ति प्राथमिक प्राथमिक मान के साथ मौजूद है UPDATE, INSERTतो इसके बजाय यह कर सकती है ।

मैंने खुद इसकी कोशिश नहीं की है, इसलिए मैं अनुभव से नहीं बोल सकता या एक उदाहरण नहीं दे सकता।


1
अगर मुझे अच्छी तरह से समझ में आया कि ये नियम ट्रिगर हैं जो हर बार स्टेटमेंट को निष्पादित करने के लिए मिलते हैं। क्या होगा यदि मैं केवल एक प्रश्न के लिए नियम लागू करना चाहता हूं? मुझे नियम बनाना है तो तुरंत नष्ट कर दो? (दौड़ की स्थितियों के बारे में क्या?)
gpilotino

3
हां, मेरे पास भी यही सवाल होंगे। नियम तंत्र वह निकटतम चीज़ है जिसे मैं पोस्टग्रैडक्यूएल में MySQL के INSERT IGNORE या ON DUPLICATE KEY UPDATE में पा सकता था। यदि हम "डुप्लिकेट कुंजी अपडेट पर पोस्टग्रैक्स्ल" के लिए Google करते हैं, तो आप अन्य लोगों को नियम तंत्र की सिफारिश करते हुए पाते हैं, भले ही एक नियम के आधार पर, किसी नियम के लिए कोई नियम लागू होगा।
बिल कारविन

4
PostgreSQL Transactional DDL का समर्थन करता है, जिसका अर्थ है कि यदि आप एक नियम बनाते हैं और इसे एक लेनदेन के भीतर छोड़ देते हैं, तो नियम कभी भी बाहर दिखाई नहीं देगा (और इसलिए बाहर का कभी भी प्रभाव नहीं होगा)।
cdhowie

6

जैसा कि @hanmari ने अपनी टिप्पणी में उल्लेख किया है। जब पोस्टग्रेज टेबल में सम्मिलित किया जाता है, तो संघर्ष पर (..) कुछ भी नहीं करते हैं डुप्लिकेट डेटा सम्मिलित नहीं करने के लिए उपयोग करने के लिए सबसे अच्छा कोड है:

query = "INSERT INTO db_table_name(column_name)
         VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"

कोड के ONFLICT लाइन सम्मिलित विवरण को अभी भी डेटा की पंक्तियों को सम्मिलित करने की अनुमति देगा। क्वेरी और मान कोड एक्सेल से एक पोस्टग्रेज डीबी टेबल में सम्मिलित तिथि का एक उदाहरण है। मेरे पास यह सुनिश्चित करने के लिए कि पोस्ट आईडी का उपयोग अद्वितीय है, मैं एक पोस्टग्रेज टेबल में बाधाएं डाल रहा हूं। डेटा की पंक्तियों पर एक डिलीट को चलाने के बजाय, मैं एक sql कोड जोड़ता हूं, जो 1. पर शुरू होने वाले ID कॉलम को फिर से जोड़ता है:

q = 'ALTER id_column serial RESTART WITH 1'

यदि मेरे डेटा में एक आईडी फ़ील्ड है, तो मैं इसका उपयोग प्राथमिक आईडी / सीरियल आईडी के रूप में नहीं करता हूं, मैं एक आईडी कॉलम बनाता हूं और मैं इसे सीरियल में सेट करता हूं। मुझे उम्मीद है कि यह जानकारी सभी के लिए उपयोगी है। * मेरे पास सॉफ्टवेयर डेवलपमेंट / कोडिंग में कोई कॉलेज डिग्री नहीं है। कोडिंग में जो कुछ भी मुझे पता है, मैं अपने दम पर अध्ययन करता हूं।


यह समग्र अद्वितीय सूचकांकों पर काम नहीं करता है!
नुलिक

4

यह समाधान नियमों का उपयोग करने से बचता है:

BEGIN
   INSERT INTO tableA (unique_column,c2,c3) VALUES (1,2,3);
EXCEPTION 
   WHEN unique_violation THEN
     UPDATE tableA SET c2 = 2, c3 = 3 WHERE unique_column = 1;
END;

लेकिन इसमें एक प्रदर्शन दोष है ( PostgreSQL.org देखें ):

बिना किसी खंड के प्रवेश करने और बाहर निकलने के लिए एक अतिरिक्त खंड युक्त ब्लॉक काफी महंगा है। इसलिए, आवश्यकता के बिना निष्कासन का उपयोग न करें।


1

बल्क पर, आप हमेशा डालने से पहले पंक्ति को हटा सकते हैं। एक पंक्ति का विलोपन जो मौजूद नहीं है, एक त्रुटि का कारण नहीं है, इसलिए इसका सुरक्षित रूप से छोड़ दिया गया है।


2
यह दृष्टिकोण अजीब दौड़ की स्थिति के लिए काफी प्रवण होगा, मैं इसकी सिफारिश नहीं करूंगा ...
स्टीवन श्लैंस्कर

1
+1 यह आसान और सामान्य है। यदि देखभाल के साथ उपयोग किया जाता है तो यह वास्तव में एक सरल समाधान हो सकता है।
वाउचर वैन निफ्ट्रिक

1
यह तब भी काम नहीं करेगा जब मौजूदा डेटा को पोस्ट-इन्सर्ट (लेकिन डुप्लिकेट की पर नहीं) में बदल दिया गया हो और हम अपडेट रखना चाहते हैं। जब एसक्यूएल स्क्रिप्ट होती है तो यह परिदृश्य होता है जो कई अलग-अलग प्रणालियों के लिए लिखे जाते हैं, जैसे डीबी अपडेट जो उत्पादन, क्यूए, देव और परीक्षण प्रणालियों पर चलते हैं।
हन्नो फिएट

1
यदि आप उन्हें DEFERRABLE INITIALLY DEFERREDझंडे के साथ बनाते हैं, तो विदेशी कुंजी एक समस्या नहीं हो सकती है ।
टेम्पो

-1

डेटा इंपोर्ट स्क्रिप्ट के लिए, "IF NOT EXISTS" को बदलने के लिए, एक तरह से, थोड़ा अजीब फॉर्मूला है जो फिर भी है:

DO
$do$
BEGIN
PERFORM id
FROM whatever_table;

IF NOT FOUND THEN
-- INSERT stuff
END IF;
END
$do$;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.