CONFLICT क्लॉज में कई विरोध_टगार्ट का उपयोग करें


93

मैं तालिका में दो कॉलम है col1, col2, वे दोनों कर रहे हैं अद्वितीय अनुक्रमित (col1 अद्वितीय है और इसलिए col2 है)।

मुझे इस तालिका में सम्मिलित होने की आवश्यकता है, ON CONFLICTसिंटैक्स का उपयोग करें और अन्य कॉलम अपडेट करें, लेकिन मैं conflict_targetक्लॉज में दोनों कॉलम का उपयोग नहीं कर सकता ।

यह काम करता हैं:

INSERT INTO table
...
ON CONFLICT ( col1 ) 
DO UPDATE 
SET 
-- update needed columns here

लेकिन कई स्तंभों के लिए यह कैसे करना है, कुछ इस तरह से:

...
ON CONFLICT ( col1, col2 )
DO UPDATE 
SET 
....

4
"col1, col2, वे दोनों अद्वितीय अनुक्रमित हैं।" इसका मतलब यह है कि col1 अद्वितीय है और col2 अद्वितीय है या col1, col2 अद्वितीय के संयोजन हैं?
e4c5

1
इसका मतलब यह है कि col1 अद्वितीय है और col2 अद्वितीय है, व्यक्तिगत रूप से
Oto Shavadze

जवाबों:


48

एक नमूना तालिका और डेटा

CREATE TABLE dupes(col1 int primary key, col2 int, col3 text,
   CONSTRAINT col2_unique UNIQUE (col2)
);

INSERT INTO dupes values(1,1,'a'),(2,2,'b');

समस्या का पुन: प्रस्तुत करना

INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1) DO UPDATE SET col3 = 'c', col2 = 2

इसको Q1 कहते हैं। परिणाम है

ERROR:  duplicate key value violates unique constraint "col2_unique"
DETAIL:  Key (col2)=(2) already exists.

प्रलेखन क्या कहता है

विरोध_बनाते हुए अद्वितीय सूचकांक निष्कर्ष निकाल सकते हैं। निष्कर्ष निकालते समय, इसमें एक या एक से अधिक index_column_name कॉलम और / या index_expression अभिव्यक्तियाँ और एक वैकल्पिक index_predicate होता है। सभी तालिका_नाम अद्वितीय अनुक्रमणिकाएँ, जो आदेश के संबंध में, बिल्कुल परस्पर विरोधी_टर्ज-निर्दिष्ट कॉलम / अभिव्यक्तियों को अर्बिटर इंडेक्स के रूप में अनुमानित (चुना हुआ) हैं। यदि एक index_predicate निर्दिष्ट किया जाता है, तो इसे अनुमान के लिए एक और आवश्यकता के रूप में, arbiter indexes को संतुष्ट करना चाहिए।

यह धारणा देता है कि निम्नलिखित क्वेरी को काम करना चाहिए, लेकिन ऐसा नहीं है क्योंकि यह वास्तव में col1 और col2 पर एक साथ अद्वितीय सूचकांक की आवश्यकता होगी। हालाँकि ऐसा कोई इंडेक्स इस बात की गारंटी नहीं देगा कि col1 और col2 व्यक्तिगत रूप से अद्वितीय होंगे जो ओपी की आवश्यकताओं में से एक है।

INSERT INTO dupes values(3,2,'c') 
ON CONFLICT (col1,col2) DO UPDATE SET col3 = 'c', col2 = 2

चलिए इस प्रश्न को Q2 कहते हैं (यह सिंटैक्स त्रुटि के साथ विफल होता है)

क्यों?

Postgresql इस तरह से व्यवहार करता है, क्योंकि जब दूसरे स्तंभ पर कोई विरोध होता है, तो उसे अच्छी तरह से परिभाषित नहीं किया जाना चाहिए। संभावनाओं की संख्या है। उपरोक्त Q1 प्रश्न में उदाहरण के लिए, col1जब कोई विरोध हो , तो क्या अपडेट को स्थगित कर देना चाहिए col2? लेकिन क्या होगा अगर उस पर एक और संघर्ष होता है col1? कैसे postgresql को संभालने की उम्मीद है?

एक तरकीब

एक समाधान पुराने फैशन UPSERT के साथ CONFLICT को जोड़ना है

CREATE OR REPLACE FUNCTION merge_db(key1 INT, key2 INT, data TEXT) RETURNS VOID AS
$$
BEGIN
    LOOP
        -- first try to update the key
        UPDATE dupes SET col3 = data WHERE col1 = key1 and col2 = key2;
        IF found THEN
            RETURN;
        END IF;

        -- not there, so try to insert the key
        -- if someone else inserts the same key concurrently, or key2
        -- already exists in col2,
        -- we could get a unique-key failure
        BEGIN
            INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col1) DO UPDATE SET col3 = data;
            RETURN;
        EXCEPTION WHEN unique_violation THEN
            BEGIN
                INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col2) DO UPDATE SET col3 = data;
                RETURN;
            EXCEPTION WHEN unique_violation THEN
                -- Do nothing, and loop to try the UPDATE again.
            END;
        END;
    END LOOP;
END;
$$
LANGUAGE plpgsql;

आपको इस संग्रहित फ़ंक्शन के तर्क को संशोधित करने की आवश्यकता होगी ताकि यह कॉलम को उसी तरह अपडेट करे जिस तरह से आप इसे चाहते हैं। इसे ऐसे ही इन्वॉल्व करें

SELECT merge_db(3,2,'c');
SELECT merge_db(1,2,'d');

3
यह तरीका काम करता है लेकिन आवश्यकता से थोड़ा अधिक कार्य / तर्क है, आपको बस इतना करना है कि दो स्तंभों पर एक अद्वितीय अवरोध पैदा करना है। नीचे मेरा जवाब देखें।
जुबेर

अगर मैं एक बार में VALUES के कई सेट सम्मिलित कर रहा हूं, तो क्या मैं मर्ज_डब समाधान का उपयोग कर सकता हूं?
डेनियल

@daniyel आपको संग्रहीत कार्य को फिर से लिखना होगा
e4c5

3
मेरे लिए यह स्पष्ट नहीं है कि पुराने ज़माने के उथल-पुथल का उपयोग करने के बारे में सुझाव देना कितना उपयोगी है - यह प्रश्न "उत्तरोत्तर 9.5" के लिए संदर्भित है और यह सभी constraint_names विकल्पों के साथ इसका उपयोग करने के तरीके को समझाकर बेहतर हो सकता है।
पाक

3
@Pak यह आपके लिए अस्पष्ट है क्योंकि आपने प्रश्न को स्पष्ट रूप से नहीं पढ़ा है। ऑप उन क्षेत्रों पर एक समग्र कुंजी की तलाश नहीं कर रहा है। अन्य उत्तर समग्र कुंजियों के लिए काम करता है
e4c5

65

ON CONFLICTसंघर्ष का पता लगाने के लिए एक अद्वितीय सूचकांक * की आवश्यकता होती है। तो आपको बस दोनों स्तंभों पर एक अद्वितीय सूचकांक बनाने की आवश्यकता है:

t=# create table t (id integer, a text, b text);
CREATE TABLE
t=# create unique index idx_t_id_a on t (id, a);
CREATE INDEX
t=# insert into t values (1, 'a', 'foo');
INSERT 0 1
t=# insert into t values (1, 'a', 'bar') on conflict (id, a) do update set b = 'bar';
INSERT 0 1
t=# select * from t;
 id | a |  b  
----+---+-----
  1 | a | bar

* यूनिक इंडेक्स के अलावा, आप बहिष्करण बाधाओं का भी उपयोग कर सकते हैं । ये अद्वितीय बाधाओं की तुलना में थोड़ा अधिक सामान्य हैं। मान लीजिए कि आपकी मेज के लिए कॉलम था idऔर valid_time(और valid_timeएक है tsrange), और आप डुप्लिकेट अनुमति देने के लिए करना चाहता था idसमय अवधि ओवरलैपिंग के लिए है, लेकिन नहीं। एक अद्वितीय बाधा तुम्हारी मदद करेगा नहीं है, लेकिन एक अपवर्जन बाधा के साथ आप कह सकते हैं "नए रिकॉर्ड को बाहर करता है, तो उनके idबराबर एक पुरानी idऔर साथ ही उनका valid_timeओवरलैप इसकी valid_time।"


4
यह जो बनाता है वह एक साथ एक अनोखा सूचकांक है, जो t (id, a) पर अद्वितीय सूचकांक idx_t_id_a बनाता है; बेशक ओपी स्पष्ट रूप से नहीं बताता है कि दो कॉलम व्यक्तिगत रूप से या एक साथ अद्वितीय हैं।
e4c5

क्यों कभी-कभी पोस्टग्रेज कहते हैं कि सूचकांक के नाम पर कोई कॉलम नहीं है और उपयोग करने में विफल रहता है ON CONFLICT?
पाक

@Pak ऐसा लगता है कि आपको अपने द्वारा निर्दिष्ट कमांड के साथ अपना प्रश्न लिखना चाहिए और आपको प्राप्त होने वाला त्रुटि संदेश।
पॉल ए जुंगविर्थ

@PaulAJungwirth मुझे नहीं पता, आपका जवाब हाजिर है - on conflictकमांड के लिए बाधा के रूप में एक अद्वितीय सूचकांक । त्रुटि सिर्फ "कॉलम my_index_name मौजूद नहीं है"।
पाक

मैं वैसे भी प्रत्येक स्तंभ पर एक अलग अद्वितीय बाधा के साथ यह कोशिश की थी जैसा कि ओपी पूछ रहा था, और यह काम नहीं किया। ऐसा नहीं है कि मुझे इसकी उम्मीद थी, लेकिन मैं उम्मीद कर रहा था।
सूदो

5

आजकल में (लगता है) असंभव है। न तो ON CONFLICT वाक्यविन्यास का अंतिम संस्करण खंड को दोहराने की अनुमति देता है, न ही सीटीई के साथ संभव है: अधिक संघर्ष-लक्ष्यों को जोड़ने के लिए INFLERT को ON CONFLICT से तोड़ना संभव नहीं है।


3

यदि आप 9.5 पोस्टग्रेज का उपयोग कर रहे हैं, तो आप EXCLUDED स्थान का उपयोग कर सकते हैं।

PostgreSQL 9.5 में नया क्या है, से लिया गया उदाहरण :

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


1

व्लाद को सही विचार आया।

सबसे पहले आपको स्तंभों पर एक तालिका अद्वितीय बाधा बनानी होगी col1, col2 फिर एक बार जब आप ऐसा करेंगे तो आप निम्न कार्य कर सकते हैं:

INSERT INTO dupes values(3,2,'c') 
ON CONFLICT ON CONSTRAINT dupes_pkey 
DO UPDATE SET col3 = 'c', col2 = 2

4
क्षमा करें, लेकिन आपने प्रश्न को गलत समझा है। ओपी एक साथ एक अद्वितीय बाधा नहीं चाहता है।
e4c5

1

हैडी की तरह, लेकिन मैंने इसे col1 और col2 से दो मानों को एक नए कॉलम, col3 (दोनों के एक अनुक्रमणिका की तरह तरह) में और उस के मुकाबले तुलना करके हल किया। यह केवल तभी काम करता है जब आपको BOTH col1 और col2 से मिलान करने की आवश्यकता हो।

INSERT INTO table
...
ON CONFLICT ( col3 ) 
DO UPDATE 
SET 
-- update needed columns here

जहां col3 = col1 और col2 से मूल्यों का संघटन।


3
आप उन दो स्तंभों के लिए एक अद्वितीय सूचकांक बना सकते हैं और उस बाधा को दे सकते हैं on conflict
किशोर रिलंगी

0

आप आम तौर पर (मुझे लगता है) केवल एक के साथ एक बयान उत्पन्न करते हैं on conflictजो एक और केवल बाधा को निर्दिष्ट करता है जो प्रासंगिकता का है, उस चीज के लिए जिसे आप सम्मिलित कर रहे हैं।

क्योंकि आम तौर पर, केवल एक बाधा एक समय में "प्रासंगिक" होती है। (यदि कई, तो मुझे आश्चर्य हो रहा है कि कुछ अजीब / अजीब तरह से डिज़ाइन किया गया है, हम्म।)

उदाहरण:
(लाइसेंस: CC0 नहीं , केवल CC-by)

// there're these unique constraints:
//   unique (site_id, people_id, page_id)
//   unique (site_id, people_id, pages_in_whole_site)
//   unique (site_id, people_id, pages_in_category_id)
// and only *one* of page-id, category-id, whole-site-true/false
// can be specified. So only one constraint is "active", at a time.

val thingColumnName = thingColumnName(notfificationPreference)

val insertStatement = s"""
  insert into page_notf_prefs (
    site_id,
    people_id,
    notf_level,
    page_id,
    pages_in_whole_site,
    pages_in_category_id)
  values (?, ?, ?, ?, ?, ?)
  -- There can be only one on-conflict clause.
  on conflict (site_id, people_id, $thingColumnName)   <—— look
  do update set
    notf_level = excluded.notf_level
  """

val values = List(
  siteId.asAnyRef,
  notfPref.peopleId.asAnyRef,
  notfPref.notfLevel.toInt.asAnyRef,
  // Only one of these is non-null:
  notfPref.pageId.orNullVarchar,
  if (notfPref.wholeSite) true.asAnyRef else NullBoolean,
  notfPref.pagesInCategoryId.orNullInt)

runUpdateSingleRow(insertStatement, values)

तथा:

private def thingColumnName(notfPref: PageNotfPref): String =
  if (notfPref.pageId.isDefined)
    "page_id"
  else if (notfPref.pagesInCategoryId.isDefined)
    "pages_in_category_id"
  else if (notfPref.wholeSite)
    "pages_in_whole_site"
  else
    die("TyE2ABK057")

on conflictखंड डायनामिक रूप से बनाए मैं क्या कर रहा हूँ पर निर्भर करता है। यदि मैं एक पृष्ठ के लिए एक अधिसूचना वरीयता सम्मिलित कर रहा हूं - तो site_id, people_id, page_idबाधा पर, एक अद्वितीय संघर्ष हो सकता है । और अगर मैं एक वर्ग के लिए अधिसूचना प्रीफ़ को कॉन्फ़िगर कर रहा हूं - तो इसके बजाय मुझे पता है कि बाधा जो उल्लंघन हो सकती है, वह है site_id, people_id, category_id

इसलिए मैं यह कर सकते हैं, और काफी संभावना है तुम भी, आपके मामले में ?, सही उत्पन्न on conflict (... columns )है, क्योंकि मुझे पता है कि मैं चाहता हूँ करने के लिए, और फिर मैं जो कई अद्वितीय बाधाओं की एक एक पता है, एक है कि का उल्लंघन हो सकता है।


-4

ON CONFLICT बहुत ही भद्दा घोल है, भागो

UPDATE dupes SET key1=$1, key2=$2 where key3=$3    
if rowcount > 0    
  INSERT dupes (key1, key2, key3) values ($1,$2,$3);

Oracle, Postgres और अन्य सभी डेटाबेस पर काम करता है


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