PostgreSQL में UPSERT (MERGE, INSERT… ON DUPLICATE UPDATE) कैसे करें?


267

एक बहुत ही अक्सर पूछे जाने वाले प्रश्न यहां बताया गया है कि एक अपग्रेड कैसे करना है, जो कि MySQL कॉल है INSERT ... ON DUPLICATE UPDATEऔर MERGEऑपरेशन के हिस्से के रूप में मानक समर्थन करता है ।

यह देखते हुए कि PostgreSQL सीधे इसका समर्थन नहीं करता है (पृष्ठ 9.5 से पहले), आप यह कैसे करते हैं? निम्नलिखित को धयान मे रखते हुए:

CREATE TABLE testtable (
    id integer PRIMARY KEY,
    somedata text NOT NULL
);

INSERT INTO testtable (id, somedata) VALUES
(1, 'fred'),
(2, 'bob');

अब कल्पना है कि आप "Upsert" tuples करना चाहते हैं (2, 'Joe'), (3, 'Alan')है, तो नई तालिका सामग्री होगा:

(1, 'fred'),
(2, 'Joe'),    -- Changed value of existing tuple
(3, 'Alan')    -- Added new tuple

जब लोग चर्चा करते हैं तो यही बात होती है upsert। महत्वपूर्ण रूप से, किसी भी दृष्टिकोण को एक ही टेबल पर काम कर रहे कई लेनदेन की उपस्थिति में सुरक्षित होना चाहिए - या तो स्पष्ट लॉकिंग का उपयोग करके, या परिणामस्वरूप रेस की स्थिति के खिलाफ बचाव करना।

PostgreSQL में डुप्लिकेट अपडेट पर इस विषय को सम्मिलित रूप से बड़े पैमाने पर चर्चा की गई है ? , लेकिन यह MySQL सिंटैक्स के विकल्पों के बारे में है, और यह समय के साथ असंबंधित विवरण का एक अच्छा सा हो गया है। मैं निश्चित उत्तरों पर काम कर रहा हूं।

ये तकनीक “इन्सर्ट न होने पर, अन्यथा कुछ नहीं” के लिए भी उपयोगी हैं, अर्थात “डुप्लीकेट की इग्निशन पर” डालें…।



8
@Michael Hampton यहां लक्ष्य एक निश्चित संस्करण बनाना था जो कई पुराने उत्तरों से भ्रमित नहीं है - और लॉक किया गया है, इसलिए कोई भी इसके बारे में कुछ भी नहीं कर सकता है। मैं क्लोजवोट से असहमत हूं।
क्रेग रिंगर

क्यों, फिर यह जल्द ही पुराना हो जाएगा - और लॉक हो जाएगा, इसलिए कोई भी इसके बारे में कुछ भी नहीं कर सकता है।
माइकल हैम्पटन

2
@Michael Hampton यदि आप चिंतित हैं, तो शायद आप अपने द्वारा लिंक किए गए एक ध्वज को फ्लैग कर सकते हैं और इसे अनलॉक करने के लिए कह सकते हैं ताकि इसे साफ किया जा सके, फिर हम इसे इसमें मिला सकते हैं। मैं केवल स्पष्ट क्लोज़ होने के बारे में बीमार हूं। इस तरह के एक भ्रामक और गलत गड़बड़ होने के लिए मुखर के रूप में।
क्रेग रिंगर

1
कि क्यू एंड ए बंद नहीं है!
माइकल हैम्पटन

जवाबों:


396

9.5 और नया:

PostgreSQL 9.5 और नया समर्थन INSERT ... ON CONFLICT UPDATE(और ON CONFLICT DO NOTHING), अर्थात।

के साथ तुलनाON DUPLICATE KEY UPDATE

त्वरित स्पष्टीकरण

उपयोग के लिए मैनुअल को देखें - विशेष रूप से सिंटैक्स आरेख और व्याख्यात्मक पाठ में परस्पर विरोधी खंड ।

9.4 और पुराने के समाधान के विपरीत, जो नीचे दिए गए हैं, यह सुविधा कई परस्पर विरोधी पंक्तियों के साथ काम करती है और इसके लिए अनन्य लॉकिंग या रिट्री लूप की आवश्यकता नहीं होती है।

फीचर को जोड़ने के लिए प्रतिबद्ध है और इसके विकास के चारों ओर चर्चा यहाँ है


यदि आप 9.5 पर हैं और आपको पिछड़े-संगत होने की आवश्यकता नहीं है तो आप अभी पढ़ना बंद कर सकते हैं


9.4 और पुराने:

PostgreSQL में कोई अंतर्निहित UPSERT(या MERGE) सुविधा नहीं है, और इसे समवर्ती उपयोग के चेहरे में कुशलतापूर्वक करना बहुत मुश्किल है।

यह लेख उपयोगी विस्तार से समस्या पर चर्चा करता है

सामान्य तौर पर आपको दो विकल्पों के बीच चयन करना होगा:

  • रिट्रीट लूप में अलग-अलग इंसर्ट / अपडेट ऑपरेशन; या
  • टेबल लॉक करना और बैच मर्ज करना

व्यक्तिगत पंक्ति पुनः लूप

यदि आप कई कनेक्शन सम्मिलित रूप से आवेषण करने की कोशिश कर रहे हैं, तो एक रिट्री लूप में अलग-अलग पंक्ति अपस्कर्ट का उपयोग करना उचित विकल्प है।

PostgreSQL प्रलेखन में एक उपयोगी प्रक्रिया है जो आपको डेटाबेस के अंदर लूप में ऐसा करने देगी । यह ज्यादातर भोले समाधानों के विपरीत, खोए हुए अपडेट और रस्सियों को सम्मिलित करता है। यह केवल में काम करेगाREAD COMMITTED मोड और केवल सुरक्षित है यदि यह एकमात्र ऐसा कार्य है जो आप लेनदेन में करते हैं, हालांकि। यदि ट्रिगर या द्वितीयक अनन्य कुंजियाँ अनूठे उल्लंघन का कारण बनती हैं तो फ़ंक्शन सही ढंग से काम नहीं करेगा।

यह रणनीति बहुत अक्षम है। जब भी व्यावहारिक हो आपको काम को कतारबद्ध करना चाहिए और इसके बजाय नीचे बताए गए अनुसार एक थोक अपटाउन करना चाहिए।

इस समस्या के कई हल किए गए समाधान रोलबैक पर विचार करने में विफल होते हैं, इसलिए उनके परिणामस्वरूप अपूर्ण अपडेट होते हैं। एक दूसरे के साथ दो लेनदेन दौड़; उनमें से एक सफलतापूर्वक INSERT; दूसरे को एक डुप्लिकेट कुंजी त्रुटि मिलती है और UPDATEइसके बजाय करता है । UPDATEब्लॉक के लिए इंतज़ार कर INSERTरोलबैक या प्रतिबद्ध करने के लिए। जब यह वापस आ जाता है, तो UPDATEहालत फिर से जाँच शून्य पंक्तियों से मेल खाती है, इसलिए भले ही UPDATEयह वास्तव में आपके द्वारा अपेक्षित अपेक्षा को पूरा नहीं करता है। आपको परिणाम पंक्ति की गणना की जांच करनी होगी और जहां आवश्यक हो वहां पुन: प्रयास करना होगा।

कुछ प्रयास किए गए समाधान भी SELECT दौड़ पर विचार करने में विफल होते हैं। यदि आप स्पष्ट और सरल कोशिश करते हैं:

-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE.

BEGIN;

UPDATE testtable
SET somedata = 'blah'
WHERE id = 2;

-- Remember, this is WRONG. Do NOT COPY IT.

INSERT INTO testtable (id, somedata)
SELECT 2, 'blah'
WHERE NOT EXISTS (SELECT 1 FROM testtable WHERE testtable.id = 2);

COMMIT;

तब जब दो बार एक साथ कई विफलता मोड होते हैं। अपडेट री-चेक के साथ पहले से ही चर्चा की गई समस्या है। एक और जहां दोनों UPDATEएक ही समय में, शून्य पंक्तियों से मेल खाते हैं और जारी रखते हैं। फिर वे दोनों EXISTSपरीक्षण करते हैं, जो पहले होता है INSERT। दोनों को शून्य पंक्तियाँ मिलती हैं, इसलिए दोनों करते हैंINSERT । एक डुप्लिकेट कुंजी त्रुटि के साथ विफल रहता है।

यही कारण है कि आपको फिर से कोशिश करने वाले लूप की आवश्यकता है। आप सोच सकते हैं कि आप स्मार्ट SQL के साथ डुप्लिकेट कुंजी त्रुटियों या खो अद्यतन को रोक सकते हैं, लेकिन आप नहीं कर सकते। आपको पंक्ति गणना की जांच करने या डुप्लिकेट कुंजी त्रुटियों (चुने हुए दृष्टिकोण के आधार पर) और फिर से प्रयास करने की आवश्यकता है।

कृपया इसके लिए अपना समाधान न रोल करें। संदेश की कतार के साथ की तरह, यह शायद गलत है।

ताला के साथ उखाड़ फेंकना

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

इस स्थिति में, आप आमतौर पर निम्नलिखित प्रक्रिया का पालन करते हैं:

  • CREATEएक TEMPORARYमेज

  • COPY या अस्थायी तालिका में नया डेटा सम्मिलित करें

  • LOCKलक्ष्य तालिका IN EXCLUSIVE MODE। यह अन्य लेन-देन की अनुमति देता है SELECT, लेकिन तालिका में कोई बदलाव नहीं करता है।

  • एक है UPDATE ... FROMअस्थायी तालिका में मानों का उपयोग मौजूदा रिकॉर्ड की;

  • एक है INSERTपंक्तियों को पहले से ही लक्ष्य तालिका में मौजूद नहीं है की;

  • COMMIT, ताला जारी।

उदाहरण के लिए, प्रश्न में दिए गए उदाहरण के लिए, INSERTअस्थायी तालिका को आबाद करने के लिए बहु-मूल्य का उपयोग करना:

BEGIN;

CREATE TEMPORARY TABLE newvals(id integer, somedata text);

INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');

LOCK TABLE testtable IN EXCLUSIVE MODE;

UPDATE testtable
SET somedata = newvals.somedata
FROM newvals
WHERE newvals.id = testtable.id;

INSERT INTO testtable
SELECT newvals.id, newvals.somedata
FROM newvals
LEFT OUTER JOIN testtable ON (testtable.id = newvals.id)
WHERE testtable.id IS NULL;

COMMIT;

संबंधित पढ़ने

व्हाट अबाउट MERGE ?

एसक्यूएल मानक MERGE वास्तव में खराब संगोष्ठी को परिभाषित करता है और पहले एक तालिका को लॉक किए बिना उखाड़ने के लिए उपयुक्त नहीं है।

यह डेटा मर्जिंग के लिए एक बहुत ही उपयोगी OLAP स्टेटमेंट है, लेकिन यह वास्तव में संगामिति-सुरक्षित शोधकर्ता के लिए उपयोगी समाधान नहीं है। अन्य DBMSes का उपयोग करने वाले लोगों को उपयोग करने के लिए बहुत सारी सलाह हैMERGE , लेकिन यह वास्तव में गलत है।

अन्य DBs:


बल्क अपग्रेड में, क्या INSERT को फ़िल्टर करने के बजाय नए अंतराल से हटाने में संभव मूल्य है? उदाहरण के लिए अपडेशन AS (UPDATE ... नए नियम ।id) DELETE नए सिरे से प्राप्त कर रहे हैं। इसके साथ मेरा विचार: INSERT (JOIN / WHERE और अद्वितीय अवरोध के लिए) में दो बार फ़िल्टर करने के बजाय, UPDATE से अस्तित्व जाँच परिणामों का पुन: उपयोग करें, जो पहले से RAM में हैं, और बहुत छोटा हो सकता है। यह एक जीत हो सकती है अगर कुछ पंक्तियों का मिलान हुआ और / या नए अंतराल परीक्षण योग्य से बहुत छोटे हैं।
गुन्नलगुर ब्रीम

1
अभी भी अनसुलझे मुद्दे हैं और अन्य विक्रेताओं के लिए यह स्पष्ट नहीं है कि क्या काम करता है और क्या नहीं। 1. पोस्ट किया गया लूपिंग सॉल्यूशन जैसा कि नोट किया गया है, कई यूनीक कीज के मामले में काम नहीं करता है। 2. mysql के लिए डुप्लिकेट कुंजी पर भी कई विशिष्ट कुंजियों के लिए काम नहीं करता है। 3. क्या MySQL, SQL सर्वर और Oracle के लिए अन्य समाधान काम के ऊपर पोस्ट किए गए हैं? क्या उन मामलों में अपवाद संभव हैं और क्या हमें लूप करना है?
dan b

@danb यह केवल PostgreSQL के बारे में वास्तव में है। कोई क्रॉस-वेंडर समाधान नहीं है। PostgreSQL के लिए समाधान कई पंक्तियों के लिए काम नहीं करता है, आपको दुर्भाग्य से प्रति पंक्ति एक लेनदेन करना होगा। MERGESQL सर्वर और ओरेकल के लिए उपयोग करने वाले "समाधान" गलत हैं और ऊपर बताए अनुसार, दौड़ की स्थिति से ग्रस्त हैं। आपको प्रत्येक DBMS पर विशेष रूप से गौर करना होगा कि कैसे उन्हें संभालना है, मैं वास्तव में केवल PostgreSQL पर सलाह दे सकता हूं। PostgreSQL पर एक सुरक्षित मल्टी-रोवर अपर्चर करने का एकमात्र तरीका होगा यदि मूल सर्वर के लिए देशी अपग्रेड के लिए समर्थन जोड़ा जाए।
क्रेग रिंगर

यहां तक ​​कि PostGresQL के लिए समाधान उस मामले में काम नहीं करता है जहां एक तालिका में कई अद्वितीय कुंजी (केवल एक पंक्ति को अपडेट करना) है। उस स्थिति में आपको यह निर्दिष्ट करने की आवश्यकता है कि किस कुंजी को अपडेट किया जा रहा है। उदाहरण के लिए jdbc का उपयोग करके एक क्रॉस-वेंडर समाधान हो सकता है।
dan b

2
Postgres अब UPSERT का समर्थन करता है - git.postgresql.org/gitweb/…
क्रिस

32

मैं PostgreSQL के पूर्व 9.5 संस्करणों के साथ एकल सम्मिलन समस्या के लिए एक और समाधान के साथ योगदान करने की कोशिश कर रहा हूं। विचार केवल प्रविष्टि डालने का प्रयास करने का है, और यदि रिकॉर्ड पहले से मौजूद है, तो इसे अद्यतन करने के लिए:

do $$
begin 
  insert into testtable(id, somedata) values(2,'Joe');
exception when unique_violation then
  update testtable set somedata = 'Joe' where id = 2;
end $$;

ध्यान दें कि यह समाधान केवल तभी लागू किया जा सकता है जब तालिका की पंक्तियों के कोई विलोपन न हों

मैं इस समाधान की दक्षता के बारे में नहीं जानता, लेकिन यह मुझे उचित लगता है।


3
धन्यवाद, ठीक वही है जिसकी मुझे तलाश थी। समझ में नहीं आता कि इसे ढूंढना इतना कठिन क्यों था।
isapir

3
हां। यह सरलीकरण काम करता है अगर और केवल अगर कोई हटाए नहीं गए हैं।
क्रेग रिंगर

@CraigRinger क्या आप बता सकते हैं कि अगर हटा दिया गया तो वास्तव में क्या होगा?
पगड़ी बांध 14

@turbanoff प्रविष्टि विफल हो सकती है क्योंकि रिकॉर्ड पहले से ही है, फिर इसे समवर्ती रूप से हटा दिया गया है, और फिर अद्यतन शून्य पंक्तियों को प्रभावित करता है क्योंकि पंक्ति हटा दी गई थी।
क्रेग रिंगर

@ क्रैगरिंगर तो हटाना समवर्ती होता है । अगर यह ठीक काम करता है तो क्या संभव हो सकता है? यदि डिलीट करना समवर्ती रूप से काम कर रहा है - तो इसे हमारे ब्लॉक के ठीक बाद निष्पादित किया जा सकता है। मैं क्या कहने की कोशिश कर रहा हूं - अगर हमारे पास समवर्ती हटाने हैं - तो यह कोड उचित तरीके से उसी तरह से insert on update
जागता है

28

यहाँ कुछ उदाहरण हैं insert ... on conflict ...( पृष्ठ 9.5+ ):

  • सम्मिलित हों, संघर्ष पर - कुछ भी न करें
    insert into dummy(id, name, size) values(1, 'new_name', 3)
    on conflict do nothing;`  
  • सम्मिलित करें, संघर्ष पर - अपडेट करें , कॉलम के माध्यम से संघर्ष लक्ष्य निर्दिष्ट करें
    insert into dummy(id, name, size) values(1, 'new_name', 3)
    on conflict(id)
    do update set name = 'new_name', size = 3;  
  • सम्मिलित करें, संघर्ष पर - अपडेट करें , बाधा नाम के माध्यम से संघर्ष लक्ष्य निर्दिष्ट करें
    insert into dummy(id, name, size) values(1, 'new_name', 3)
    on conflict on constraint dummy_pkey
    do update set name = 'new_name', size = 4;

महान जवाब - सवाल: क्यों या किस स्थिति में किसी को कॉलम या बाधा नाम से लक्ष्य विनिर्देश का उपयोग करना चाहिए? क्या विभिन्न उपयोग-मामलों के लिए कोई लाभ / हानि है?
नाथन बेंटन

1
@NathanBenton मुझे लगता है कि कम से कम 2 अंतर हैं: (1) स्तंभ का नाम प्रोग्रामर द्वारा निर्दिष्ट किया गया है, जबकि बाधा नाम या तो प्रोग्रामर द्वारा निर्दिष्ट किया जा सकता है, या तालिका / स्तंभ नामों के अनुसार डेटाबेस द्वारा उत्पन्न किया जा सकता है। (2) प्रत्येक स्तंभ में कई अवरोध हो सकते हैं। उस ने कहा, यह आपके मामले पर निर्भर करता है कि किसको चुनना है।
एरिक वांग

8

पोस्टग्रेज> = 9.5 के लिए SQLAlchemy अपग्रेड

चूंकि ऊपर दी गई बड़ी पोस्ट पोस्टग्रेज संस्करणों के लिए कई अलग-अलग एसक्यूएल तरीकों को शामिल करती है (न केवल गैर-9.5 सवाल के रूप में), मैं SQLAlchemy में इसे कैसे करना है, अगर आप पोस्टग्रेज 9.5 का उपयोग कर रहे हैं, तो जोड़ना चाहूंगा। अपने स्वयं के मुखर को लागू करने के बजाय, आप SQLAlchemy के फ़ंक्शंस का उपयोग कर सकते हैं (जो SQLAlchemy 1.1 में जोड़े गए थे)। यदि संभव हो तो व्यक्तिगत रूप से, मैं इनका उपयोग करने की सलाह दूंगा। न केवल सुविधा के कारण, बल्कि इसलिए भी कि यह PostgreSQL को किसी भी दौड़ की स्थिति को संभालने की सुविधा देता है।

एक और उत्तर से क्रॉस-पोस्टिंग जो मैंने कल दिया ( https://stackoverflow.com/a/44395983/2156909 )

SQLAlchemy ON CONFLICTअब दो तरीकों से समर्थन करता है on_conflict_do_update()और on_conflict_do_nothing():

प्रलेखन से नकल:

from sqlalchemy.dialects.postgresql import insert

stmt = insert(my_table).values(user_email='a@b.com', data='inserted data')
stmt = stmt.on_conflict_do_update(
    index_elements=[my_table.c.user_email],
    index_where=my_table.c.user_email.like('%@gmail.com'),
    set_=dict(data=stmt.excluded.data)
    )
conn.execute(stmt)

http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html?highlight=conflict#insert-on-conflict-upsert


4
प्रश्न में अजगर और SQLAlchemy का उल्लेख नहीं किया गया है।
अलेक्जेंडर एमेलियनोव

मैं अक्सर मेरे द्वारा लिखे गए समाधानों में पायथन का उपयोग करता हूं। लेकिन मैंने SQLAlchemy (या इसके बारे में पता था) में नहीं देखा है। यह एक सुरुचिपूर्ण विकल्प लगता है। धन्यवाद। यदि यह जांच करता है, तो मैं इसे अपने संगठन को प्रस्तुत करूंगा।
रॉबर्ट

3
WITH UPD AS (UPDATE TEST_TABLE SET SOME_DATA = 'Joe' WHERE ID = 2 
RETURNING ID),
INS AS (SELECT '2', 'Joe' WHERE NOT EXISTS (SELECT * FROM UPD))
INSERT INTO TEST_TABLE(ID, SOME_DATA) SELECT * FROM INS

Postgresql 9.3 पर परीक्षण किया गया


@ क्रेगिंगर: क्या आप इस बारे में विस्तार से बता सकते हैं? परमाणु परमाणु नहीं है?
पैरिसनी

2
अगर यह लिखता है तो @parisni No. प्रत्येक CTE शब्द का अपना स्नैपशॉट है। इसके अलावा, उन पंक्तियों पर किए गए विधेय लॉकिंग का कोई प्रकार नहीं है जो नहीं पाए गए ताकि वे अभी भी दूसरे सत्र द्वारा समवर्ती रूप से बनाए जा सकें। यदि आपने SERIALIZABLEअलगाव का उपयोग किया है तो आपको क्रमिक विफलता के साथ गर्भपात हो जाएगा, अन्यथा आपको संभवतः एक अद्वितीय उल्लंघन मिलेगा। पुनर्स्थापना को मजबूत न करें, पुनर्निवेश गलत होगा। का उपयोग करें INSERT ... ON CONFLICT ...। यदि आपका PostgreSQL बहुत पुराना है, तो इसे अपडेट करें।
क्रेग रिंगर

@ क्रैगरिंगर INSERT ... ON CLONFLICT ...बल्क लोडिंग के लिए अभिप्रेत नहीं है। आपके पोस्ट से, LOCK TABLE testtable IN EXCLUSIVE MODE;CTE के भीतर परमाणु चीजें प्राप्त करने के लिए एक समाधान है। नहीं ?
पैरिसनी

@parisni यह थोक लोडिंग के लिए नहीं है? कौन कहता है? postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT । यकीन है, यह बड़े-बड़े व्यवहार के बिना थोक लोडिंग की तुलना में बहुत धीमा है, लेकिन यह स्पष्ट है और मामला यह होगा कि आप क्या करते हैं। यह सबट्रेन के उपयोग की तुलना में अधिक तेज़ है, यह सुनिश्चित है। सबसे तेज़ तरीका टारगेट टेबल को लॉक करना है, फिर एक insert ... where not exists ...या समान, निश्चित रूप से करना है।
क्रेग रिंगर

1

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

पहले आयात

import itertools as it

from functools import partial
from operator import itemgetter

from sqlalchemy.exc import IntegrityError
from app import session
from models import Posts

अब एक युगल सहायक कार्य करता है

def chunk(content, chunksize=None):
    """Groups data into chunks each with (at most) `chunksize` items.
    https://stackoverflow.com/a/22919323/408556
    """
    if chunksize:
        i = iter(content)
        generator = (list(it.islice(i, chunksize)) for _ in it.count())
    else:
        generator = iter([content])

    return it.takewhile(bool, generator)


def gen_resources(records):
    """Yields a dictionary if the record's id already exists, a row object 
    otherwise.
    """
    ids = {item[0] for item in session.query(Posts.id)}

    for record in records:
        is_row = hasattr(record, 'to_dict')

        if is_row and record.id in ids:
            # It's a row but the id already exists, so we need to convert it 
            # to a dict that updates the existing record. Since it is duplicate,
            # also yield True
            yield record.to_dict(), True
        elif is_row:
            # It's a row and the id doesn't exist, so no conversion needed. 
            # Since it's not a duplicate, also yield False
            yield record, False
        elif record['id'] in ids:
            # It's a dict and the id already exists, so no conversion needed. 
            # Since it is duplicate, also yield True
            yield record, True
        else:
            # It's a dict and the id doesn't exist, so we need to convert it. 
            # Since it's not a duplicate, also yield False
            yield Posts(**record), False

और अंत में उखाड़ने का कार्य

def upsert(data, chunksize=None):
    for records in chunk(data, chunksize):
        resources = gen_resources(records)
        sorted_resources = sorted(resources, key=itemgetter(1))

        for dupe, group in it.groupby(sorted_resources, itemgetter(1)):
            items = [g[0] for g in group]

            if dupe:
                _upsert = partial(session.bulk_update_mappings, Posts)
            else:
                _upsert = session.add_all

            try:
                _upsert(items)
                session.commit()
            except IntegrityError:
                # A record was added or deleted after we checked, so retry
                # 
                # modify accordingly by adding additional exceptions, e.g.,
                # except (IntegrityError, ValidationError, ValueError)
                db.session.rollback()
                upsert(items)
            except Exception as e:
                # Some other error occurred so reduce chunksize to isolate the 
                # offending row(s)
                db.session.rollback()
                num_items = len(items)

                if num_items > 1:
                    upsert(items, num_items // 2)
                else:
                    print('Error adding record {}'.format(items[0]))

यहां बताया गया है कि आप इसका उपयोग कैसे करते हैं

>>> data = [
...     {'id': 1, 'text': 'updated post1'}, 
...     {'id': 5, 'text': 'updated post5'}, 
...     {'id': 1000, 'text': 'new post1000'}]
... 
>>> upsert(data)

इसका लाभ यह bulk_save_objectsहै कि यह सम्मिलित ( थोक संचालन के विपरीत ) रिश्तों, त्रुटि जाँच आदि को संभाल सकता है ।


यह भी मुझे गलत लगता है। यदि आपकी आईडी की सूची एकत्र करने के बाद एक समवर्ती सत्र एक पंक्ति सम्मिलित करता है तो क्या होगा? या एक को हटा देता है?
क्रेग रिंगर

good point @CraigRinger मैं ऐसा ही कुछ करता हूं, लेकिन केवल 1 सत्र में ही काम करना होता है। फिर कई सत्रों को संभालने का सबसे अच्छा तरीका क्या है? एक लेनदेन शायद?
रबानो

लेन-देन सभी समसामयिक मुद्दों का जादू समाधान नहीं हैं। आप SERIALIZABLE लेनदेन का उपयोग कर सकते हैं और क्रमिक विफलताओं को संभाल सकते हैं लेकिन यह धीमा है। आपको त्रुटि से निपटने और एक पुनर्प्रयास लूप की आवश्यकता है। इसमें मेरा उत्तर और "संबंधित पठन" अनुभाग देखें।
क्रेग रिंगर

@ क्रैगरिंगर गेटचा। मैंने वास्तव में अन्य सत्यापन विफलताओं के कारण अपने मामले में एक रिट्री लूप लागू किया। मैं इस उत्तर को तदनुसार अपडेट करूंगा।
पुनर्मिलन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.