वर्तमान में स्वीकार जवाब एक भी संघर्ष लक्ष्य, कुछ संघर्ष, छोटे tuples और कोई भी ट्रिगर के लिए ठीक लगता है। यह जानवर बल के साथ समसामयिक मुद्दे 1 (नीचे देखें) से बचा जाता है । सरल समाधान की अपनी अपील है, दुष्प्रभाव कम महत्वपूर्ण हो सकते हैं।
अन्य सभी मामलों के लिए, हालांकि, आवश्यकता के बिना समान पंक्तियों को अपडेट न करें। यहां तक कि अगर आप सतह पर कोई अंतर नहीं देखते हैं, तो विभिन्न दुष्प्रभाव हैं :
यह ट्रिगर हो सकता है जिसे फायर नहीं किया जाना चाहिए।
यह "निर्दोष" पंक्तियों को लिखता है, संभवतः समवर्ती लेनदेन के लिए लागत को कम करता है।
यह पंक्ति नई लग सकती है, हालांकि यह पुरानी है (लेन-देन टाइमस्टैम्प)
सबसे महत्वपूर्ण बात , PostgreSQL के MVCC मॉडल के साथ हर पंक्ति के लिए एक नया पंक्ति संस्करण लिखा जाता है UPDATE
, भले ही पंक्ति डेटा बदल गया हो। यह यूपीएसईआरटी के लिए ही एक प्रदर्शन जुर्माना, टेबल ब्लोट, इंडेक्स ब्लोट, टेबल पर बाद के संचालन के लिए प्रदर्शन जुर्माना लगाता है VACUUM
। कुछ डुप्लिकेट के लिए एक मामूली प्रभाव, लेकिन ज्यादातर दुपट्टे के लिए बड़े पैमाने पर ।
साथ ही , कभी-कभी यह व्यावहारिक या उपयोग करने के लिए भी संभव नहीं है ON CONFLICT DO UPDATE
। नियम पुस्तिका:
के लिए ON CONFLICT DO UPDATE
, conflict_target
प्रदान किया जाना चाहिए।
एक एकल यदि एक से अधिक अनुक्रमित / कमी शामिल हैं "संघर्ष लक्ष्य" संभव नहीं है।
आप खाली अपडेट और साइड इफेक्ट्स के बिना ही (लगभग) समान प्राप्त कर सकते हैं। निम्नलिखित समाधानों में से कुछ भी ON CONFLICT DO NOTHING
(कोई "संघर्ष लक्ष्य") के साथ काम करते हैं , जो उत्पन्न होने वाले सभी संभावित संघर्षों को पकड़ने के लिए - जो वांछनीय हो सकता है या नहीं।
समवर्ती लेखन भार के बिना
WITH input_rows(usr, contact, name) AS (
VALUES
(text 'foo1', text 'bar1', text 'bob1') -- type casts in first row
, ('foo2', 'bar2', 'bob2')
-- more?
)
, ins AS (
INSERT INTO chats (usr, contact, name)
SELECT * FROM input_rows
ON CONFLICT (usr, contact) DO NOTHING
RETURNING id --, usr, contact -- return more columns?
)
SELECT 'i' AS source -- 'i' for 'inserted'
, id --, usr, contact -- return more columns?
FROM ins
UNION ALL
SELECT 's' AS source -- 's' for 'selected'
, c.id --, usr, contact -- return more columns?
FROM input_rows
JOIN chats c USING (usr, contact); -- columns of unique index
source
स्तंभ प्रदर्शित करने के लिए यह कैसे काम करता वैकल्पिक योग है। आपको वास्तव में दोनों मामलों के बीच अंतर बताने के लिए इसकी आवश्यकता हो सकती है (खाली लेखन पर एक और फायदा)।
अंतिम JOIN chats
काम करता है क्योंकि संलग्न डेटा-संशोधित सीटीई से नई सम्मिलित पंक्तियां अभी तक अंतर्निहित तालिका में दिखाई नहीं दे रही हैं। (एक ही एसक्यूएल स्टेटमेंट के सभी भागों में अंतर्निहित टेबल के समान स्नैपशॉट हैं।)
चूंकि VALUES
अभिव्यक्ति मुक्त-स्थायी है (सीधे सीधे संलग्न नहीं है INSERT
) पोस्टग्रेट्स डेटा प्रकारों को लक्ष्य स्तंभों से प्राप्त नहीं कर सकते हैं और आपको स्पष्ट प्रकार की जातियों को जोड़ना पड़ सकता है। नियम पुस्तिका:
जब VALUES
इसका उपयोग किया जाता है INSERT
, तो मान सभी स्वचालित रूप से संबंधित गंतव्य स्तंभ के डेटा प्रकार के लिए बाध्य होते हैं। जब इसका उपयोग अन्य संदर्भों में किया जाता है, तो सही डेटा प्रकार निर्दिष्ट करना आवश्यक हो सकता है। यदि प्रविष्टियाँ सभी उद्धृत शाब्दिक स्थिरांक हैं, तो पहले के लिए जोर देना सभी के लिए ग्रहण किए गए प्रकार को निर्धारित करने के लिए पर्याप्त है।
स्वयं क्वेरी (साइड इफेक्ट की गिनती नहीं) में थोड़ा और अधिक के लिए महंगा हो सकता है कुछ CTE के भूमि के ऊपर और अतिरिक्त के कारण ड्यूप्स, SELECT
एक अद्वितीय बाधा के साथ कार्यान्वित किया जाता है - (जो सही सूचकांक के बाद से सस्ता होना चाहिए वहाँ परिभाषा से है एक अनुक्रमणिका)।
कई डुप्लिकेट के लिए (बहुत) तेज हो सकता है । अतिरिक्त लिखने की प्रभावी लागत कई कारकों पर निर्भर करती है।
लेकिन किसी भी मामले में कम दुष्प्रभाव और छिपी हुई लागतें हैं । यह संभवतः सबसे सस्ता है।
संलग्न अनुक्रम अभी भी उन्नत हैं, क्योंकि संघर्ष के लिए परीक्षण करने से पहले डिफ़ॉल्ट मान भरे जाते हैं ।
सीटीई के बारे में:
समवर्ती लेखन भार के साथ
डिफ़ॉल्ट READ COMMITTED
लेनदेन अलगाव मान लिया गया है । सम्बंधित:
दौड़ की स्थिति से बचाव के लिए सबसे अच्छी रणनीति सटीक आवश्यकताओं, तालिका में पंक्तियों की संख्या और आकार और यूपीएसईआरटी, समवर्ती लेनदेन की संख्या, संघर्षों की संभावना, उपलब्ध संसाधनों और अन्य कारकों पर निर्भर करती है ...
समसामयिक समस्या 1
यदि एक समवर्ती लेन-देन ने एक पंक्ति में लिखा है जिसे आपका लेन-देन अब यूपीएसईआरटी की कोशिश करता है, तो आपके लेनदेन को समाप्त होने के लिए दूसरे के लिए इंतजार करना होगा।
यदि अन्य लेन-देन ROLLBACK
(या कोई त्रुटि, यानी स्वचालित ROLLBACK
) के साथ समाप्त होता है , तो आपका लेनदेन सामान्य रूप से आगे बढ़ सकता है। मामूली संभव दुष्प्रभाव: अनुक्रमिक संख्या में अंतराल। लेकिन कोई गायब पंक्तियों।
यदि अन्य लेन-देन सामान्य रूप से (अंतर्निहित या स्पष्ट COMMIT
) समाप्त होता है , तो आपका INSERT
विरोध संघर्ष ( UNIQUE
सूचकांक / बाधा निरपेक्ष है) का पता लगाएगा और DO NOTHING
इसलिए, पंक्ति को वापस नहीं करेगा। (यह भी पंक्ति को लॉक नहीं कर सकता जैसा कि समवर्ती समस्या 2 में दिखाया गया है , क्योंकि यह दिखाई नहीं देता है ।) SELECT
क्वेरी की शुरुआत से एक ही स्नैपशॉट देखता है और अभी तक अदृश्य पंक्ति को वापस नहीं कर सकता है।
परिणाम सेट से कोई भी ऐसी पंक्तियाँ गायब हैं (भले ही वे अंतर्निहित तालिका में मौजूद हों)!
यह जैसा है ठीक हो सकता है । खासकर यदि आप उदाहरण की तरह पंक्तियाँ नहीं लौटा रहे हैं और पंक्ति को जानकर संतुष्ट हैं। यदि यह पर्याप्त नहीं है, तो इसके आस-पास विभिन्न तरीके हैं।
आप आउटपुट की पंक्ति गणना की जांच कर सकते हैं और यदि यह इनपुट की पंक्ति गणना से मेल नहीं खाता है तो स्टेटमेंट दोहरा सकते हैं। दुर्लभ मामले के लिए काफी अच्छा हो सकता है। बिंदु एक नई क्वेरी शुरू करने के लिए है (उसी लेनदेन में हो सकता है), जो तब नई प्रतिबद्ध पंक्तियों को देखेगा।
या एक ही क्वेरी के भीतर लापता परिणाम पंक्तियों की जांच करें और एलेक्स्टोनी के उत्तर में दिखाए गए जानवर बल की चाल के साथ ओवरराइट करें ।
WITH input_rows(usr, contact, name) AS ( ... ) -- see above
, ins AS (
INSERT INTO chats AS c (usr, contact, name)
SELECT * FROM input_rows
ON CONFLICT (usr, contact) DO NOTHING
RETURNING id, usr, contact -- we need unique columns for later join
)
, sel AS (
SELECT 'i'::"char" AS source -- 'i' for 'inserted'
, id, usr, contact
FROM ins
UNION ALL
SELECT 's'::"char" AS source -- 's' for 'selected'
, c.id, usr, contact
FROM input_rows
JOIN chats c USING (usr, contact)
)
, ups AS ( -- RARE corner case
INSERT INTO chats AS c (usr, contact, name) -- another UPSERT, not just UPDATE
SELECT i.*
FROM input_rows i
LEFT JOIN sel s USING (usr, contact) -- columns of unique index
WHERE s.usr IS NULL -- missing!
ON CONFLICT (usr, contact) DO UPDATE -- we've asked nicely the 1st time ...
SET name = c.name -- ... this time we overwrite with old value
-- SET name = EXCLUDED.name -- alternatively overwrite with *new* value
RETURNING 'u'::"char" AS source -- 'u' for updated
, id --, usr, contact -- return more columns?
)
SELECT source, id FROM sel
UNION ALL
TABLE ups;
यह उपरोक्त क्वेरी की तरह है, लेकिन हम CTE के साथ एक और कदम जोड़ते हैं ups
, इससे पहले कि हम पूरा परिणाम सेट करें। पिछले CTE ज्यादातर समय कुछ भी नहीं करेगा। केवल अगर पंक्तियाँ दिए गए परिणाम से गायब हो जाती हैं, तो हम जानवर बल का उपयोग करते हैं।
अधिक उपरि, अभी तक। पहले से मौजूद पंक्तियों के साथ जितना अधिक टकराव होगा, उतनी ही सरल दृष्टिकोण को समझने की संभावना होगी।
एक पक्ष प्रभाव: दूसरा यूपीएसईआर क्रम से बाहर की पंक्तियों को लिखता है, इसलिए यह गतिरोध की संभावना को फिर से पेश करता है (नीचे देखें) यदि एक ही पंक्तियों को लिखने वाले तीन या अधिक लेनदेन ओवरलैप होते हैं। यदि यह समस्या है, तो आपको एक अलग समाधान की आवश्यकता है - जैसे कि ऊपर वर्णित पूरे विवरण को दोहराएं।
समसामयिक समस्या 2
यदि समवर्ती लेन-देन प्रभावित पंक्तियों के कॉलम को सम्मिलित करने के लिए लिख सकते हैं, और आपको यह सुनिश्चित करना होगा कि जो पंक्तियाँ आपको मिली हैं, वे उसी लेन-देन में बाद के चरण में हैं, तो आप मौजूदा पंक्तियों को CTE में सस्ते में लॉक कर सकते हैं ins
(जो अन्यथा अनलॉक हो जाएंगे) साथ में:
...
ON CONFLICT (usr, contact) DO UPDATE
SET name = name WHERE FALSE -- never executed, but still locks the row
...
और जैसे ही, एक लॉकिंग क्लॉजSELECT
FOR UPDATE
जोड़ें ।
यह प्रतिस्पर्धात्मक लेखन लेनदेन के अंत तक प्रतीक्षा करता है, जब सभी ताले जारी हो जाते हैं। इसलिए संक्षिप्त रहें।
अधिक जानकारी और विवरण:
गतिरोध?
सुसंगत क्रम में पंक्तियाँ डालकर गतिरोधों से बचाव करें । देख:
डेटा प्रकार और कास्ट
मौजूदा तालिका डेटा प्रकारों के लिए टेम्पलेट के रूप में ...
फ्री-स्टेंडिंग VALUES
एक्सप्रेशन में डेटा की पहली पंक्ति के लिए स्पष्ट प्रकार की कास्ट्स असुविधाजनक हो सकती हैं। इसके चारों ओर रास्ते हैं। आप किसी भी मौजूदा संबंध (तालिका, दृश्य, ...) को पंक्ति टेम्पलेट के रूप में उपयोग कर सकते हैं। लक्ष्य तालिका उपयोग के मामले के लिए स्पष्ट विकल्प है। इनपुट डेटा स्वचालित रूप से उचित प्रकार के लिए मजबूर किया जाता है, में की तरह VALUES
एक के खंड INSERT
:
WITH input_rows AS (
(SELECT usr, contact, name FROM chats LIMIT 0) -- only copies column names and types
UNION ALL
VALUES
('foo1', 'bar1', 'bob1') -- no type casts here
, ('foo2', 'bar2', 'bob2')
)
...
यह कुछ डेटा प्रकारों के लिए काम नहीं करता है। देख:
... और नाम
यह सभी डेटा प्रकारों के लिए भी काम करता है ।
तालिका के सभी (अग्रणी) स्तंभों में सम्मिलित करते समय, आप स्तंभ नामों को छोड़ सकते हैं। chats
उदाहरण के लिए मान लें कि केवल UPSERT में उपयोग किए गए 3 कॉलम हैं:
WITH input_rows AS (
SELECT * FROM (
VALUES
((NULL::chats).*) -- copies whole row definition
('foo1', 'bar1', 'bob1') -- no type casts needed
, ('foo2', 'bar2', 'bob2')
) sub
OFFSET 1
)
...
एक तरफ: का उपयोग नहीं करते आरक्षित शब्द की तरह "user"
पहचानकर्ता के रूप में। वह भरी हुई पगडंडी है। कानूनी, निचली स्थिति, अयोग्य पहचानकर्ताओं का उपयोग करें। मैंने इसे बदल दिया usr
।
ON CONFLICT UPDATE
ताकि पंक्ति में परिवर्तन हो। फिरRETURNING
कब्जा कर लेंगे।