INSERT यदि नहीं है तो ELSE UPDATE?


275

मैंने पाया है कि क्लासिक के लिए "कुछ" समाधान होगा "मैं एक नया रिकॉर्ड कैसे डालूं या एक को अपडेट करूं अगर यह पहले से मौजूद है" लेकिन मैं उनमें से किसी को भी SQLite में काम करने के लिए नहीं मिल सकता।

मेरे पास एक तालिका इस प्रकार है:

CREATE TABLE Book 
ID     INTEGER PRIMARY KEY AUTOINCREMENT,
Name   VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level  INTEGER,
Seen   INTEGER

मैं जो करना चाहता हूं वह एक अनोखे नाम के साथ एक रिकॉर्ड जोड़ना है। यदि नाम पहले से मौजूद है, तो मैं खेतों को संशोधित करना चाहता हूं।

क्या कोई मुझे बता सकता है कि यह कैसे करना है?


3
"इंसर्ट या रिप्लेस" "इंसर्ट या अपडेट" से बिलकुल अलग है
फेटी

जवाबों:


320

Http://sqlite.org/lang_conflict.html पर देखें

आप कुछ इस तरह चाहते हैं:

insert or replace into Book (ID, Name, TypeID, Level, Seen) values
((select ID from Book where Name = "SearchName"), "SearchName", ...);

ध्यान दें कि सम्मिलित सूची में कोई भी फ़ील्ड NULL पर सेट नहीं होगी यदि पंक्ति तालिका में पहले से मौजूद है। यही कारण है कि IDकॉलम के लिए एक सबसिलेक्ट है : प्रतिस्थापन मामले में स्टेटमेंट इसे NULL पर सेट करेगा और फिर एक नई आईडी आवंटित की जाएगी।

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

उदाहरण के लिए, यह मानते हुए कि आप Seenअकेले छोड़ना चाहते हैं:

insert or replace into Book (ID, Name, TypeID, Level, Seen) values (
   (select ID from Book where Name = "SearchName"),
   "SearchName",
    5,
    6,
    (select Seen from Book where Name = "SearchName"));

109
गलत "इन्सर्ट या रिप्लेस" "इन्सर्ट या अपडेट" से अलग है। मान्य उत्तर के लिए, देखें stackoverflow.com/questions/418898/...
आरडीएस

12
@ नहीं, यह गलत नहीं है क्योंकि यह प्रश्न कहता है कि "फ़ील्ड को संशोधित करें" और प्राथमिक कुंजी कॉलम सूची का हिस्सा नहीं है, लेकिन अन्य सभी फ़ील्ड हैं। यदि आपके पास कोने के मामले हैं जहां आप सभी फ़ील्ड मानों की जगह नहीं ले रहे हैं, या यदि आप प्राथमिक कुंजी के साथ गड़बड़ कर रहे हैं तो आपको कुछ अलग करना चाहिए। यदि आपके पास नए क्षेत्रों का पूरा सेट है तो यह दृष्टिकोण ठीक है। क्या आपके पास एक विशिष्ट समस्या है जिसे मैं नहीं देख सकता हूं?
जनम

9
यदि आप सभी क्षेत्रों के लिए सभी नए मूल्य जानते हैं तो यह मान्य है। यदि उपयोगकर्ता केवल अपडेट करता है, तो कहो Level, इस दृष्टिकोण का पालन नहीं किया जा सकता है।
आरडीएस

4
सही किया । अन्य उत्तर प्रासंगिक है, लेकिन यह दृष्टिकोण सभी क्षेत्रों को अपडेट करने (कुंजी को छोड़कर) के लिए मान्य है।
урослав Рахматуллин

2
हां यह पूरी तरह से गलत है। क्या होगा यदि मैं नए से एकल स्तंभ मान को संघर्ष पर अद्यतन करना चाहता हूं। उपरोक्त मामले में अन्य सभी डेटा को नए द्वारा बदल दिया जाएगा जो सही नहीं है।
मृग

85

आपको INSERT OR IGNOREकमांड के बाद कमांड का उपयोग करना चाहिए UPDATE: निम्नलिखित उदाहरण nameमें एक प्राथमिक कुंजी है:

INSERT OR IGNORE INTO my_table (name, age) VALUES ('Karen', 34)
UPDATE my_table SET age = 34 WHERE name='Karen'

पहला कमांड रिकॉर्ड डालेगा। यदि रिकॉर्ड मौजूद है, तो यह मौजूदा प्राथमिक कुंजी के साथ संघर्ष के कारण हुई त्रुटि को अनदेखा करेगा।

दूसरा कमांड रिकॉर्ड अपडेट करेगा (जो अब निश्चित रूप से मौजूद है)


6
किस पल में यह नजरअंदाज कर देगा? जब नाम और उम्र दोनों समान हो?
एमओ

यह समाधान होना चाहिए ... यदि आप डालने पर किसी भी ट्रिगर का उपयोग कर रहे हैं, तो स्वीकृत उत्तर हर बार आग लगाता है। यह केवल अपडेट नहीं करता है और करता है
PodTech.io

1
यह पूरी तरह से नाम पर आधारित है। याद रखें कि केवल "नाम" कॉलम एक प्राथमिक कुंजी है।
गेब्रियल फेरर

3
जब रिकॉर्ड नया है, तो अद्यतन आवश्यक नहीं है, लेकिन वैसे भी निष्पादित किया जाएगा, जिससे खराब प्रदर्शन हो सकता है?
मार्कग

इसके लिए तैयार विवरण को कैसे निष्पादित किया जाए?
प्रशान्त

65

आपको एक " संघर्ष " को ट्रिगर करने के लिए मेज पर एक बाधा स्थापित करने की आवश्यकता है जिसे आप एक प्रतिस्थापन करके हल करते हैं:

CREATE TABLE data   (id INTEGER PRIMARY KEY, event_id INTEGER, track_id INTEGER, value REAL);
CREATE UNIQUE INDEX data_idx ON data(event_id, track_id);

तब आप जारी कर सकते हैं:

INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 2, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 5);

"सेलेक्ट * FROM डेटा" आपको देगा:

2|2|2|3.0
3|1|2|5.0

ध्यान दें कि data.id "3" है और "1" नहीं है क्योंकि REPLACE एक DELETE और INSERT करता है, एक UPDATE नहीं। इसका अर्थ यह भी है कि आपको यह सुनिश्चित करना होगा कि आप सभी आवश्यक कॉलम परिभाषित करें या आपको अनपेक्षित NULL मान मिलेंगे।


33

सबसे पहले इसे अपडेट करें। यदि प्रभावित पंक्ति गणना = 0 है तो इसे डालें। सभी RDBMS के लिए यह सबसे आसान और उपयुक्त है ।


11
डेटाबेस की परवाह किए बिना, दो ऑपरेशनों को सही अलगाव स्तर पर लेनदेन के साथ कोई समस्या नहीं होनी चाहिए।
जनम

5
Insert or Replaceवास्तव में अधिक बेहतर है।
MPelletier

1
मैं वास्तव में चाहता हूं कि आईटी प्रलेखन में अधिक उदाहरण होंगे। मैंने इसे नीचे की कोशिश की है और यह काम नहीं करता है (मेरा सिंटैक्स स्पष्ट रूप से गलत है)। यह क्या होना चाहिए पर कोई विचार? INSERT INTO Book (नाम, टाइपआईडी, स्तर, सीन) VALUES ('सुपरमैन', '2', '14', '0') ON CONFLICT REPLACE Book (नाम, टाइपआईडी, स्तर, सीन, वैल्यू) ('सुपरमैन', ' 2 ',' 14 ',' 0 ')
स्पार्कीएनजेड

6
यह भी कि यदि पंक्ति मान बिल्कुल समान हैं, तो प्रभावित पंक्ति संख्या शून्य होगी, और एक नई डुप्लिकेट पंक्ति बनाई जाएगी।
बर्कसाइड

2
+1, क्योंकि INSERT या REPLACE संघर्ष पर मूल पंक्ति को हटा देगा और यदि आप सभी कॉलम सेट नहीं कर रहे हैं, तो आप मूल मान खो देंगे
Pavel Machyniak

20

INSERT OR REPLACE डिफ़ॉल्ट मान के लिए अन्य फ़ील्ड प्रतिस्थापित करेगा।

sqlite> CREATE TABLE Book (
  ID     INTEGER PRIMARY KEY AUTOINCREMENT,
  Name   TEXT,
  TypeID INTEGER,
  Level  INTEGER,
  Seen   INTEGER
);

sqlite> INSERT INTO Book VALUES (1001, 'C++', 10, 10, 0);
sqlite> SELECT * FROM Book;
1001|C++|10|10|0

sqlite> INSERT OR REPLACE INTO Book(ID, Name) VALUES(1001, 'SQLite');

sqlite> SELECT * FROM Book;
1001|SQLite|||

यदि आप दूसरे क्षेत्र को संरक्षित करना चाहते हैं

sqlite> SELECT * FROM Book;
1001|C++|10|10|0

sqlite> INSERT OR IGNORE INTO Book(ID) VALUES(1001);
sqlite> UPDATE Book SET Name='SQLite' WHERE ID=1001;

sqlite> SELECT * FROM Book;
1001|SQLite|10|10|0

या यूपीएसईआरटी का उपयोग करते हुए (वाक्यविन्यास 3.24.0 संस्करण 2018-06-04 के साथ SQLite में जोड़ा गया था)

INSERT INTO Book (ID, Name)
  VALUES (1001, 'SQLite')
  ON CONFLICT (ID) DO
  UPDATE SET Name=excluded.Name;

excluded.उपसर्ग में मूल्य के बराबर VALUES


16

उप्पर आप जो चाहते है। UPSERTवाक्यविन्यास 3.24.0 (2018-06-04) संस्करण के साथ SQLite में जोड़ा गया था।

CREATE TABLE phonebook2(
  name TEXT PRIMARY KEY,
  phonenumber TEXT,
  validDate DATE
);

INSERT INTO phonebook2(name,phonenumber,validDate)
  VALUES('Alice','704-555-1212','2018-05-08')
  ON CONFLICT(name) DO UPDATE SET
    phonenumber=excluded.phonenumber,
    validDate=excluded.validDate
  WHERE excluded.validDate>phonebook2.validDate;

चेतावनी दी है कि इस बिंदु पर वास्तविक शब्द "यूपीएसईआरटी" अपग्रेड सिंटैक्स का हिस्सा नहीं है।

सही सिंटैक्स है

INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...

और यदि आप INSERT INTO SELECT ...अपनी सिलेक्ट आवश्यकताओं को कम से कम कर रहे हैं तो WHERE trueपार्कर अस्पष्टता को हल करने ONके लिए ज्वाइन सिंटैक्स के साथ हल करें ।

चेतावनी दी जाती है कि INSERT OR REPLACE...यदि इसे बदलना है तो एक नया डालने से पहले रिकॉर्ड को हटा दिया जाएगा, जो कि विदेशी कुंजी कैस्केड या अन्य डिलीट ट्रिगर होने पर खराब हो सकता है।


यह दस्तावेज़ीकरण में मौजूद है और मेरे पास sqlite का सबसे अंतिम संस्करण है, जो 3.25.1 है, लेकिन यह मेरे लिए काम नहीं करता है
Bentaiba मिल्ड बसमा

क्या आपने उन दो प्रश्नों को शब्दशः से ऊपर आज़माया है?
कॉमरेडजोकूल

ध्यान दें कि Ubuntu 18.04 SQLite3 v3.22 के साथ आता है , न कि 3.25 पर, इसलिए यह UPSERTसिंटैक्स का समर्थन नहीं करता है ।
Starbeamrainbowlabs

सुविधा पर संदर्भ संस्करण के लिए धन्यवाद!
मट्टियो

6

यदि आपके पास कोई प्राथमिक कुंजी नहीं है, तो आप मौजूद नहीं होने पर सम्मिलित कर सकते हैं, फिर एक अपडेट करें। तालिका का उपयोग करने से पहले कम से कम एक प्रविष्टि होनी चाहिए।

INSERT INTO Test 
   (id, name)
   SELECT 
      101 as id, 
      'Bob' as name
   FROM Test
       WHERE NOT EXISTS(SELECT * FROM Test WHERE id = 101 and name = 'Bob') LIMIT 1;

Update Test SET id='101' WHERE name='Bob';

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

4

मेरा मानना ​​है कि आप यूपीएसईआरटी चाहते हैं ।

उस उत्तर में अतिरिक्त चालबाजी के बिना "INSERT OR REPLACE" उन सभी क्षेत्रों को रीसेट कर देगा जिन्हें आप NULL या अन्य डिफ़ॉल्ट मान के लिए निर्दिष्ट नहीं करते हैं। (INSERT या REPLACE का यह व्यवहार UPDATE के विपरीत है; यह वास्तव में INSERT की तरह है, क्योंकि यह वास्तव में INSERT है; हालाँकि यदि आप चाहते थे कि UPDATE-if-मौजूद हो तो आप शायद UPDATE शब्दार्थ चाहते हैं और वास्तविक परिणाम से अप्रिय आश्चर्य होगा।)

सुझाए गए यूपीएसईआरटी कार्यान्वयन से चालबाजी मूल रूप से INSERT या REPLACE का उपयोग करने के लिए है, लेकिन उन सभी फ़ील्ड्स को निर्दिष्ट करें, जिन्हें आप परिवर्तित नहीं करना चाहते हैं, उनके लिए मौजूदा मूल्य को प्राप्त करने के लिए एम्बेडेड चयन खंडों का उपयोग करें।


1

मुझे लगता है कि यह इंगित करने के लायक है कि यहां कुछ अप्रत्याशित व्यवहार हो सकता है यदि आप अच्छी तरह से नहीं समझते हैं कि कैसे प्राथमिक कुंजी और UNIQUE बातचीत करते हैं।

एक उदाहरण के रूप में, यदि आप केवल एक रिकॉर्ड सम्मिलित करना चाहते हैं यदि NAME फ़ील्ड वर्तमान में नहीं ली गई है, और यदि यह है, तो आप चाहते हैं कि आपको बताने के लिए आग के लिए एक बाधा अपवाद हो, तो INSERT या REPLACE फेंक और अपवाद नहीं करेगा और इसके बजाय होगा परस्पर विरोधी रिकॉर्ड (एक ही NAME के साथ मौजूदा रिकॉर्ड ) को बदलकर स्वयं UNIQUE बाधा को हल करें । गैस्पर्ड ने ऊपर दिए अपने जवाब में यह वास्तव में अच्छी तरह से प्रदर्शित किया है।

यदि आप आग के लिए एक बाधा अपवाद चाहते हैं, तो आपको एक INSERT स्टेटमेंट का उपयोग करना होगा , और एक बार अद्यतन नाम को अपडेट करने के लिए एक अलग UPDATE कमांड पर भरोसा करना चाहिए जब तक कि आप नाम नहीं ले लेते।

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