मैं किसी तालिका में एक पंक्ति को अद्यतन कैसे करूं या यदि यह मौजूद नहीं है तो इसे सम्मिलित करें?


83

मेरे पास काउंटरों की निम्न तालिका है:

CREATE TABLE cache (
    key text PRIMARY KEY,
    generation int
);

मैं काउंटरों में से एक को बढ़ाना चाहूंगा, या इसे शून्य पर सेट कर सकता हूं यदि संबंधित पंक्ति अभी तक मौजूद नहीं है। वहाँ मानक SQL में समसामयिक मुद्दों के बिना ऐसा करने का एक तरीका है? ऑपरेशन कभी-कभी लेनदेन का हिस्सा होता है, कभी-कभी अलग होता है।

यदि संभव हो तो SQL को SQLite, PostgreSQL और MySQL पर अनमॉडिफाइड चलाना चाहिए।

एक खोज ने कई विचारों को जन्म दिया जो या तो समसामयिक मुद्दों से ग्रस्त हैं, या एक डेटाबेस के लिए विशिष्ट हैं:

  • INSERTएक नई पंक्ति के लिए प्रयास करें , और UPDATEयदि कोई त्रुटि थी। दुर्भाग्य से, INSERTवर्तमान लेनदेन को रद्द करने में त्रुटि ।

  • UPDATEपंक्ति, और यदि कोई पंक्तियों को संशोधित नहीं किया गया था, तो INSERTएक नई पंक्ति।

  • MySQL का एक ON DUPLICATE KEY UPDATEक्लॉज है।

संपादित करें: सभी महान उत्तरों के लिए धन्यवाद। ऐसा लगता है कि पॉल सही है, और ऐसा करने का एक भी, पोर्टेबल तरीका नहीं है। यह मेरे लिए काफी आश्चर्यजनक है, क्योंकि यह एक बहुत ही बुनियादी ऑपरेशन की तरह लगता है।


6
आप इन सभी RDBMS के लिए काम करने वाले एक भी समाधान को खोजने नहीं जा रहे हैं। माफ़ करना।
पॉल टॉम्बलिन


जवाबों:


137

MySQL (और बाद में SQLite) भी REPLACE INTO सिंटैक्स का समर्थन करता है:

REPLACE INTO my_table (pk_id, col1) VALUES (5, '123');

यह स्वचालित रूप से प्राथमिक कुंजी की पहचान करता है और अद्यतन करने के लिए एक मिलान पंक्ति पाता है, यदि कोई नहीं मिला है तो एक नया डालें।


13
वास्तव में, विशिष्ट होने के लिए, MySQL का REPLACE हमेशा एक सम्मिलित करता है, लेकिन यह पंक्ति को हटा देगा यदि यह पहले से मौजूद है, तो पहले। dev.mysql.com/doc/refman/4.1/en/replace.html
इवान

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

2
@Zoredache तकनीकी रूप से, यह एक 'डिलीट, फिर इन्सर्ट' है, (n) 'इंसर्ट + डिलीट' के रूप में प्रभावी रूप से डिलीट के समान है, लेकिन यह बालों को विभाजित करने वाला है।
एजी हैमरथिफ़

2
@ एगहैमरथिफ़ में एक बहुत ही वास्तविक अंतर है कि नई सम्मिलित पंक्ति में वही प्राथमिक कुंजी नहीं होगी जिस पंक्ति को हटा दिया गया है। ON DUPLICATE के साथ यह एक ही प्राथमिक कुंजी होगी (जब तक कि आप इसे विशेष रूप से नहीं बदलते)।
टिम स्ट्रीजहोर्स्ट

32

यदि यह पहले से मौजूद है तो SQLite पंक्ति को बदलने का समर्थन करता है:

INSERT OR REPLACE INTO [...blah...]

आप इसे छोटा कर सकते हैं

REPLACE INTO [...blah...]

इस शॉर्टकट को MySQL REPLACE INTOअभिव्यक्ति के साथ संगत होने के लिए जोड़ा गया था ।


इसके लिए आवश्यक है कि आप PRAMARY KEYअपने मूल्यों में एक को परिभाषित करें।
डॉनसॉन्ग

24

मैं निम्नलिखित की तरह कुछ करना होगा:

INSERT INTO cache VALUES (key, generation)
ON DUPLICATE KEY UPDATE (key = key, generation = generation + 1);

कोड या वर्ग में पीढ़ी मान को 0 पर सेट करना लेकिन मान बढ़ाने के लिए ON DUP ... का उपयोग करना। मुझे लगता है कि वैसे भी वाक्य रचना है।


1
यह उत्तर ईमानदारी से चयनित उत्तर होना चाहिए। यदि आप सभी फ़ील्ड को प्रविष्टि में सबमिट नहीं कर रहे हैं तो यह एक गैर-विनाशकारी संपादन है।
जॉर्डन

9

DUPLICATE KEY पर UPDATE क्लॉज सबसे अच्छा समाधान है क्योंकि: REPLACE एक DELETE करता है, जिसके बाद एक INSERT होता है, इसलिए एक छोटी सी अवधि के लिए रिकॉर्ड को हटा दिया जाता है जिससे कभी भी इतनी हल्की संभावना बनती है कि कोई क्वेरी वापस छोड़ दी जा सके, यदि पृष्ठ था REPLACE क्वेरी के दौरान देखा गया।

मैं उस कारण के लिए INSERT ... ON DUPLICATE UPDATE ... पसंद करता हूं।

jmoz का समाधान सबसे अच्छा है: हालाँकि मैं कोष्ठक में SET सिंटैक्स पसंद करता हूँ

INSERT INTO cache 
SET key = 'key', generation = 'generation'
ON DUPLICATE KEY 
UPDATE key = 'key', generation = (generation + 1)
;

4
REPLACE परमाणु है, इसलिए ऐसी कोई अवधि नहीं है जहाँ पंक्ति मौजूद नहीं है।
ब्रिलियनड

8

मुझे नहीं पता कि आप एक प्लेटफ़ॉर्म-न्यूट्रल समाधान खोजने जा रहे हैं।

इसे आमतौर पर "यूपीएसईआरटी" कहा जाता है।

देखिए कुछ संबंधित चर्चाएँ:


क्या वह समाधान वास्तव में लागू है? और पोर्टेबल?
रयान ग्राहम

5

PostgreSQL में कोई मर्ज कमांड नहीं है, और वास्तव में यह लिखना तुच्छ नहीं है - वास्तव में अजीब किनारे के मामले हैं जो कार्य को "दिलचस्प" बनाते हैं।

सबसे अच्छा (जैसा कि: सबसे अधिक संभव स्थितियों में काम करना) दृष्टिकोण, फ़ंक्शन का उपयोग करना है - जैसे कि मैनुअल (मर्ज_बैंक) में दिखाया गया है ।

यदि आप फ़ंक्शन का उपयोग नहीं करना चाहते हैं, तो आप आमतौर पर इसके साथ भाग सकते हैं :

updated = db.execute(UPDATE ... RETURNING 1)
if (!updated)
  db.execute(INSERT...)

बस याद रखें कि यह गलती का सबूत नहीं है और यह अंततः विफल हो जाएगा


4

Standard SQL इस कार्य के लिए MERGE स्टेटमेंट प्रदान करता है। सभी DBMS MERGE स्टेटमेंट का समर्थन नहीं करते हैं।


0

अगर आपके पास एटोमिकली अपडेट या इंसर्ट (जैसे ट्रांजेक्शन के जरिए) करने का सामान्य तरीका नहीं है तो आप किसी अन्य लॉकिंग स्कीम में वापस आ सकते हैं। 0-बाइट फ़ाइल, सिस्टम म्यूटेक्स, नाम पाइप, आदि ...


0

क्या आप एक आवेषण ट्रिगर का उपयोग कर सकते हैं? यदि यह विफल रहता है, तो एक अद्यतन करें।


जब कमांड काम करता है तो ट्रिगर (कम से कम PostgreSQL में) चल रहा होता है। यानी जब आधार कमांड विफल हो जाता है तो आप ट्रिगर नहीं कर सकते।

0

यदि आप लाइब्रेरी का उपयोग करने के लिए ठीक हैं जो आपके लिए SQL लिखता है, तो आप Upsert (वर्तमान में रूबी और पायथन केवल) का उपयोग कर सकते हैं :

Pet.upsert({:name => 'Jerry'}, :breed => 'beagle')
Pet.upsert({:name => 'Jerry'}, :color => 'brown')

यह MySQL, Postgres और SQLite3 में काम करता है।

यह MySQL और Postgres में एक संग्रहीत कार्यविधि या उपयोगकर्ता-परिभाषित फ़ंक्शन (UDF) लिखता है। यह INSERT OR REPLACESQLite3 में उपयोग करता है।

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