PostgreSQL में ON CONFLICT के साथ RETURNING का उपयोग कैसे करें?


149

मेरे पास PostgreSQL 9.5 में निम्नलिखित UPSERT हैं:

INSERT INTO chats ("user", "contact", "name") 
           VALUES ($1, $2, $3), 
                  ($2, $1, NULL) 
ON CONFLICT("user", "contact") DO NOTHING
RETURNING id;

यदि कोई संघर्ष नहीं है तो यह कुछ इस तरह लौटाता है:

----------
    | id |
----------
  1 | 50 |
----------
  2 | 51 |
----------

लेकिन अगर वहाँ संघर्ष है यह किसी भी पंक्तियों को वापस नहीं करता है:

----------
    | id |
----------

मैं नए idकॉलम वापस करना चाहता हूं अगर कोई संघर्ष नहीं है या परस्पर विरोधी idकॉलम के मौजूदा कॉलम को वापस करना है ।
क्या यह किया जा सकता है? यदि हां, तो कैसे?


1
उपयोग करें ON CONFLICT UPDATEताकि पंक्ति में परिवर्तन हो। फिर RETURNINGकब्जा कर लेंगे।
गॉर्डन लिनोफ़

1
@GordonLinoff क्या होगा अगर अपडेट करने के लिए कुछ नहीं है?
ओक्कू

1
यदि अद्यतन करने के लिए कुछ भी नहीं है, तो इसका मतलब है कि कोई संघर्ष नहीं था इसलिए यह सिर्फ नए मूल्यों को सम्मिलित करता है और अपनी आईडी लौटाता है
जोला

1
आपको यहां अन्य तरीके मिलेंगे । मैं प्रदर्शन के मामले में दोनों के बीच अंतर जानना पसंद करूंगा।
स्टैनिस्लासद्रग मोनिका

जवाबों:


88

मुझे वास्तव में यही समस्या थी, और मैंने इसे 'डू अपडेट' के बजाय 'डू अपडेट' का उपयोग करके हल किया, हालांकि मेरे पास अपडेट करने के लिए कुछ भी नहीं था। आपके मामले में यह कुछ इस तरह होगा:

INSERT INTO chats ("user", "contact", "name") 
       VALUES ($1, $2, $3), 
              ($2, $1, NULL) 
ON CONFLICT("user", "contact") DO UPDATE SET name=EXCLUDED.name RETURNING id;

यह क्वेरी सभी पंक्तियों को वापस कर देगी, भले ही उन्हें अभी डाला गया हो या वे पहले मौजूद थीं।


11
इस दृष्टिकोण के साथ एक समस्या यह है कि प्राथमिक कुंजी का अनुक्रम नंबर हर संघर्ष (फर्जी अपडेट) पर बढ़ा हुआ है, जिसका मूल रूप से मतलब है कि आप अनुक्रम में भारी अंतराल के साथ समाप्त हो सकते हैं। किसी भी विचार कि कैसे से बचने के लिए?
मिशा

9
@ मिस्का: तो क्या? दृश्यों को कभी भी पहली जगह में अंतरहीन होने की गारंटी नहीं दी जाती है और अंतराल मायने नहीं रखता (और यदि वे करते हैं, तो एक अनुक्रम गलत काम है)
a_horse_with_no_name

24
मैं ज्यादातर मामलों में इसका इस्तेमाल करने की सलाह नहीं दूंगा। मैंने एक उत्तर जोड़ा कि क्यों।
एरविन ब्रान्डस्टेट्टर

4
यह उत्तर DO NOTHINGमूल प्रश्न के पहलू को प्राप्त करने के लिए प्रकट नहीं होता है - मेरे लिए यह सभी पंक्तियों के लिए गैर-संघर्ष क्षेत्र (यहां, "नाम") को अपडेट करने के लिए प्रकट होता है।
पीटर जाक्लॉ

जैसा कि नीचे दिए गए बहुत लंबे उत्तर में चर्चा की गई है, "डू अपडेट" का उपयोग एक ऐसे क्षेत्र के लिए जो नहीं बदला है, एक "स्वच्छ" समाधान नहीं है और इससे कई समस्याएं हो सकती हैं।
बिल वर्थिंगटन

202

वर्तमान में स्वीकार जवाब एक भी संघर्ष लक्ष्य, कुछ संघर्ष, छोटे 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
...

और जैसे ही, एक लॉकिंग क्लॉजSELECTFOR 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


2
आपको लगता है कि यह विधि धारावाहिकों में अंतराल पैदा नहीं करेगी, लेकिन वे हैं: INSERT ... ON CONFLICT DO NOTHING हर बार धारावाहिक में जो कुछ भी मैं देख सकता हूं, उसमें वृद्धि करता है
हानिरहित

1
ऐसा नहीं है कि यह बहुत मायने रखता है, लेकिन ऐसा क्यों है कि धारावाहिकों में वृद्धि हुई है? और इससे बचने का कोई उपाय नहीं है?
salient

1
@ ढाल: जैसा कि मैंने ऊपर जोड़ा: संघर्षों और अनुक्रमों के परीक्षण से पहले कॉलम डिफ़ॉल्ट मान भरे जाते हैं और समवर्ती लेखन के साथ टकराव से बचने के लिए अनुक्रमों को कभी वापस नहीं लिया जाता है।
एरविन ब्रान्डसेट्टर

7
अतुल्य। एक बार ध्यान से देखने पर आकर्षण और समझने में आसान जैसा काम करता है। मुझे अभी भी इच्छा है ON CONFLICT SELECT...कि कोई बात कहाँ है :)
रोशाम्बो

3
अतुल्य। पोस्टग्रेज के निर्माता उपयोगकर्ताओं पर अत्याचार करते दिख रहे हैं। क्यों बस नहीं लौटने खंड हमेशा मान, कि क्या वहाँ आवेषण थे की परवाह किए बिना या नहीं?
अनातोली अलेक्सेव

16

ओवरराइड, INSERTक्वेरी का विस्तार होने से एक बाधा संघर्ष के मामले में दो अलग-अलग व्यवहारों के साथ परिभाषित किया जा सकता है: DO NOTHINGया DO UPDATE

INSERT INTO upsert_table VALUES (2, 6, 'upserted')
   ON CONFLICT DO NOTHING RETURNING *;

 id | sub_id | status
----+--------+--------
 (0 rows)

साथ ही ध्यान दें कि RETURNINGकुछ भी नहीं लौटाता है, क्योंकि कोई ट्यूल नहीं डाला गया है । अब DO UPDATE, टपल पर संचालन करना संभव है, इसके साथ एक संघर्ष है। पहले ध्यान दें कि एक बाधा को परिभाषित करना महत्वपूर्ण है जिसका उपयोग यह परिभाषित करने के लिए किया जाएगा कि एक संघर्ष है।

INSERT INTO upsert_table VALUES (2, 2, 'inserted')
   ON CONFLICT ON CONSTRAINT upsert_table_sub_id_key
   DO UPDATE SET status = 'upserted' RETURNING *;

 id | sub_id |  status
----+--------+----------
  2 |      2 | upserted
(1 row)

2
हमेशा प्रभावित पंक्ति आईडी प्राप्त करने का अच्छा तरीका है, और पता है कि यह एक सम्मिलित या upsert था। मुझे जिस चीज की जरूरत थी।
मोबी डक

यह अभी भी "डू अपडेट" का उपयोग कर रहा है, जिसके नुकसान पर पहले ही चर्चा की जा चुकी है।
बिल वर्थिंगटन

4

किसी एकल आइटम के सम्मिलन के लिए, मैं संभवत: आईडी लौटाने के दौरान एक मोटे पैमाने का उपयोग करूंगा:

WITH new_chats AS (
    INSERT INTO chats ("user", "contact", "name")
    VALUES ($1, $2, $3)
    ON CONFLICT("user", "contact") DO NOTHING
    RETURNING id
) SELECT COALESCE(
    (SELECT id FROM new_chats),
    (SELECT id FROM chats WHERE user = $1 AND contact = $2)
);

2
WITH e AS(
    INSERT INTO chats ("user", "contact", "name") 
           VALUES ($1, $2, $3), 
                  ($2, $1, NULL) 
    ON CONFLICT("user", "contact") DO NOTHING
    RETURNING id
)
SELECT * FROM e
UNION
    SELECT id FROM chats WHERE user=$1, contact=$2;

उपयोग करने का मुख्य उद्देश्य ON CONFLICT DO NOTHINGत्रुटि फेंकने से बचना है, लेकिन यह बिना पंक्ति रिटर्न के कारण होगा। इसलिए हमें SELECTमौजूदा आईडी प्राप्त करने के लिए दूसरे की आवश्यकता है ।

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


यह समाधान अच्छी तरह से काम करता है और DB को एक अनावश्यक लेखन (अपडेट) करने से बचता है !! अच्छा!
साइमन सी।

0

मैंने Erwin Brandstetter द्वारा अद्भुत उत्तर को संशोधित किया, जो अनुक्रम में वृद्धि नहीं करेगा, और किसी भी पंक्तियों को राइट-लॉक नहीं करेगा। मैं PostgreSQL के लिए अपेक्षाकृत नया हूं, इसलिए कृपया बेझिझक मुझे बताएं कि क्या आपको इस विधि में कोई कमियां दिखती हैं:

WITH input_rows(usr, contact, name) AS (
   VALUES
      (text 'foo1', text 'bar1', text 'bob1')  -- type casts in first row
    , ('foo2', 'bar2', 'bob2')
    -- more?
   )
, new_rows AS (
   SELECT 
     c.usr
     , c.contact
     , c.name
     , r.id IS NOT NULL as row_exists
   FROM input_rows AS r
   LEFT JOIN chats AS c ON r.usr=c.usr AND r.contact=c.contact
   )
INSERT INTO chats (usr, contact, name)
SELECT usr, contact, name
FROM new_rows
WHERE NOT row_exists
RETURNING id, usr, contact, name

यह मानता है कि तालिका chatsमें स्तंभों पर एक अद्वितीय बाधा है (usr, contact)

अद्यतन: सुझाए गए संशोधनों को स्पार्ट से जोड़ा गया (नीचे)। धन्यवाद!


1
इसके बजाय CASE WHEN r.id IS NULL THEN FALSE ELSE TRUE END AS row_existsसिर्फ लिखें r.id IS NOT NULL as row_exists। इसके बजाय WHERE row_exists=FALSEसिर्फ लिखें WHERE NOT row_exists
spatar
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.