पोस्टग्रैजेस 9.4 में टाइप JSONB के कॉलम पर अपडेट ऑपरेशन कैसे करें


133

Postgres 9.4 डेटासाइप JSONB के लिए प्रलेखन के माध्यम से देखते हुए, यह तुरंत मेरे लिए स्पष्ट नहीं है कि JSON कॉलम पर अपडेट कैसे करें।

JSONB प्रकार और कार्यों के लिए प्रलेखन:

http://www.postgresql.org/docs/9.4/static/functions-json.html http://www.postgresql.org/docs/9.4/static/datatype-json.html

एक उदाहरण के रूप में, मेरे पास यह आधारभूत संरचना है:

CREATE TABLE test(id serial, data jsonb);

सम्मिलित करना आसान है, जैसे:

INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}');

अब, मैं 'डेटा' कॉलम को कैसे अपडेट करूंगा? यह अमान्य सिंटैक्स है:

UPDATE test SET data->'name' = 'my-other-name' WHERE id = 1;

क्या यह दस्तावेज कहीं स्पष्ट है कि मैं चूक गया? धन्यवाद।

जवाबों:


33

आदर्श रूप से, आप JSON दस्तावेज़ों का उपयोग संरचित, नियमित डेटा के लिए नहीं करते हैं जिसे आप किसी रिलेशनल डेटाबेस के अंदर हेरफेर करना चाहते हैं। इसके बजाय एक सामान्यीकृत संबंधपरक डिज़ाइन का उपयोग करें।

JSON का मुख्य रूप से संपूर्ण दस्तावेजों को संग्रहीत करना है, जिन्हें RDBMS के अंदर हेरफेर करने की आवश्यकता नहीं है। सम्बंधित:

Postgres में एक पंक्ति को अपडेट करना हमेशा पूरी पंक्ति का एक नया संस्करण लिखता है । यही Postgres 'MVCC मॉडल का मूल सिद्धांत है । प्रदर्शन के दृष्टिकोण से, यह शायद ही मायने रखता है कि क्या आप किसी JSON ऑब्जेक्ट के अंदर या उसके सभी डेटा को बदलते हैं: पंक्ति का एक नया संस्करण लिखना होगा।

इस प्रकार मैनुअल में सलाह :

JSON डेटा तालिका में संग्रहीत होने पर किसी भी अन्य डेटा प्रकार के समान संगणक-नियंत्रण के विचारों के अधीन होता है। यद्यपि बड़े दस्तावेज़ों को संग्रहीत करना व्यावहारिक है, लेकिन ध्यान रखें कि कोई भी अपडेट पूरी पंक्ति पर एक पंक्ति-स्तरीय लॉक प्राप्त करता है। लेनदेन को अद्यतन करने के बीच लॉक विवाद को कम करने के लिए JSON दस्तावेज़ों को एक प्रबंधनीय आकार तक सीमित करने पर विचार करें। आदर्श रूप से, JSON दस्तावेजों में से प्रत्येक को एक परमाणु डेटाम का प्रतिनिधित्व करना चाहिए जो व्यावसायिक नियमों को यथोचित रूप से छोटे डेटा में विभाजित नहीं किया जा सकता है जिन्हें स्वतंत्र रूप से संशोधित किया जा सकता है।

इसका सार: किसी JSON ऑब्जेक्ट के अंदर कुछ भी संशोधित करने के लिए , आपको कॉलम में एक संशोधित ऑब्जेक्ट असाइन करना होगा। पोस्टग्रेज jsonअपनी भंडारण क्षमताओं के अतिरिक्त डेटा के निर्माण और हेरफेर करने के लिए सीमित साधनों की आपूर्ति करता है। संस्करण 9.2 के बाद से हर नए रिलीज के साथ उपकरणों का शस्त्रागार काफी हद तक बढ़ गया है। लेकिन प्रिंसिपल बनी हुई है: आपको हमेशा कॉलम के लिए एक पूर्ण संशोधित ऑब्जेक्ट असाइन करना होगा और पोस्टग्रैज हमेशा किसी भी अपडेट के लिए एक नया पंक्ति संस्करण लिखते हैं।

पोस्टग्रेज 9.3 या बाद के उपकरणों के साथ काम करने की कुछ तकनीकें:

इस उत्तर इतने पर मेरे सभी अन्य उत्तर के रूप में कई के रूप में downvotes के बारे में आकर्षित किया है एक साथ । लोग इस विचार को पसंद नहीं करते हैं: एक सामान्य डिज़ाइन गैर-गतिशील डेटा के लिए बेहतर है। क्रेग रिंगर का यह उत्कृष्ट ब्लॉग पोस्ट अधिक विस्तार से बताता है:


6
यह केवल JSON प्रकार के साथ चिंता करता है और JSONB को अनदेखा करता है।
फिएटजैफ

7
@fiatjaf: यह उत्तर पूरी तरह से डेटा प्रकारों jsonऔर एक jsonbजैसे पर लागू होता है। दोनों JSON डेटा को स्टोर करते हैं, jsonbइसे सामान्यीकृत बाइनरी फॉर्म में करते हैं जिसमें कुछ फायदे (और कुछ नुकसान) हैं। stackoverflow.com/a/10560761/939860 डेटाबेस में बहुत अधिक हेरफेर करने के लिए न तो डेटा प्रकार अच्छा है। कोई दस्तावेज़ प्रकार नहीं है। खैर, यह छोटे, मुश्किल से संरचित JSON दस्तावेजों के लिए ठीक है। लेकिन बड़े, नेस्टेड दस्तावेज एक मूर्खतापूर्ण तरीका होगा।
एरविन ब्रान्डसेट्टर

7
"निर्देश कैसे पोस्टग्रैस 9.3 के टूल के साथ काम करना है" वास्तव में आपके जवाब में सबसे पहले होना चाहिए क्योंकि यह पूछे गए प्रश्न का उत्तर देता है .. कभी-कभी यह रखरखाव / स्कीमा परिवर्तन आदि के लिए json को अपडेट करने के लिए समझ में आता है और अपडेट json डॉन नहीं करने का कारण बनता है। 'वास्तव में लागू नहीं
माइकल वासर

22
यह उत्तर सहायक नहीं है, क्षमा करें। @jvous, क्या आप जिमोथी के उत्तर को स्वीकार नहीं करना चाहते, क्योंकि यह वास्तव में आपके प्रश्न का उत्तर देता है?
बैस्टियन वोइगट

10
अपनी टिप्पणी / राय / चर्चा जोड़ने से पहले प्रश्न का उत्तर दें।
पीपी

332

यदि आप Postgresql 9.5 में अपग्रेड करने में सक्षम हैं, तो jsonb_setकमांड उपलब्ध है, जैसा कि अन्य ने उल्लेख किया है।

निम्नलिखित प्रत्येक एसक्यूएल बयान में, मैंने whereसंक्षिप्तता के लिए खंड को छोड़ दिया है ; जाहिर है, आप उसे वापस जोड़ना चाहते हैं।

अद्यतन नाम:

UPDATE test SET data = jsonb_set(data, '{name}', '"my-other-name"');

टैग बदलें (टैग जोड़ने या हटाने के विरोध के रूप में):

UPDATE test SET data = jsonb_set(data, '{tags}', '["tag3", "tag4"]');

दूसरा टैग बदलना (0-अनुक्रमित):

UPDATE test SET data = jsonb_set(data, '{tags,1}', '"tag5"');

एक टैग को जोड़ें ( यह तब तक काम करेगा जब तक 999 से कम टैग नहीं होंगे। तर्क 999 से 1000 या उससे अधिक होने पर त्रुटि उत्पन्न होती है । यह अब Postgres 9.5.3 में मामला प्रतीत नहीं होता है; एक बहुत बड़े सूचकांक का उपयोग किया जा सकता है) :

UPDATE test SET data = jsonb_set(data, '{tags,999999999}', '"tag6"', true);

अंतिम टैग निकालें:

UPDATE test SET data = data #- '{tags,-1}'

जटिल अपडेट (अंतिम टैग हटाएं, नया टैग डालें और नाम बदलें):

UPDATE test SET data = jsonb_set(
    jsonb_set(data #- '{tags,-1}', '{tags,999999999}', '"tag3"', true), 
    '{name}', '"my-other-name"');

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

जटिल उदाहरण में, तीन परिवर्तन और तीन अस्थायी संस्करण हैं: पहला, अंतिम टैग हटा दिया गया है। फिर, उस संस्करण को एक नया टैग जोड़कर बदल दिया जाता है। अगला, दूसरा संस्करण nameफ़ील्ड को बदलकर बदल दिया जाता है । dataकॉलम में मान को अंतिम संस्करण से बदल दिया जाता है।


42
आपको ओपी के अनुरोध के अनुसार तालिका में एक कॉलम को अपडेट करने का तरीका दिखाने के लिए बोनस अंक
मिलते हैं

1
@ अंडरिक: मैंने एक अधिक जटिल उदाहरण जोड़ा। यह वही नहीं है जो आपने अनुरोध किया था, लेकिन यह आपको एक विचार देना चाहिए। ध्यान दें कि बाहरी jsonb_setकॉल के लिए इनपुट आंतरिक कॉल से आउटपुट है, और उस आंतरिक कॉल के लिए इनपुट का परिणाम है data #- '{tags,-1}'। यानी, अंतिम टैग के साथ मूल डेटा हटा दिया गया है।
जिमोथी

1
@PranaySoni: उस उद्देश्य के लिए, मैं शायद एक संग्रहीत प्रक्रिया का उपयोग करूंगा या, यदि ओवरहेड एक चिंता का विषय नहीं है, तो उस डेटा को वापस लाएं, इसे एप्लिकेशन की भाषा में हेरफेर करें, फिर इसे वापस लिखें। यह भारी लगता है, लेकिन ध्यान रखें, मेरे द्वारा दिए गए सभी उदाहरणों में, आप अभी भी JSON (B) में एक भी फ़ील्ड को अपडेट नहीं कर रहे हैं: आप पूरे कॉलम को किसी भी तरह से अधिलेखित कर रहे हैं। तो एक संग्रहीत खरीद वास्तव में अलग नहीं है।
जिमोथी

1
@ एलेक्स: हाँ, थोड़ा सा हैक। अगर मैंने कहा {tags,0}, तो इसका मतलब होगा "सरणी का पहला तत्व tags", मुझे उस तत्व को एक नया मूल्य देने की अनुमति देता है। 0 के बजाय बड़ी संख्या का उपयोग करके, इसके बजाय सरणी में एक मौजूदा तत्व को प्रतिस्थापित करने से, यह सरणी में एक नया तत्व जोड़ता है। हालाँकि, अगर सरणी में वास्तव में 999,999,999 से अधिक तत्व थे, तो यह एक नया जोड़ने के बजाय अंतिम तत्व को बदल देगा।
जिमोथी

1
क्या होगा अगर फ़ील्ड में शून्य है? काम नहीं दिखता है। उदाहरण के लिए jsonb फ़ील्ड शून्य है: "UPDATE आयोजक SET info = jsonb_set (जानकारी, '{देश}', '" FRA ")) जहां जानकारी - >>' देश ':: पाठ IS NULL;" मैं UPDATE 105 रिकॉर्ड लेकिन db पर कोई परिवर्तन नहीं
स्टैकडेव

24

यह एंड्रयू डंस्टन द्वारा jsonb_set के रूप में 9.5 में आ रहा है जो मौजूदा एक्सटेंशन jsonbx पर आधारित है जो 9.4 के साथ काम करता है


इस पंक्ति में एक और मुद्दा, का उपयोग है jsonb_build_object(), क्योंकि x->key, कुंजी-वस्तु जोड़ी नहीं लौटाता है, जिससे आपको ज़रूरत होती है jsonb_set(target, path, jsonb_build_object('key',x->key))
पीटर क्रूस

19

उन लोगों के लिए जो इस समस्या में भाग लेते हैं और एक बहुत ही जल्दी ठीक करना चाहते हैं (और 9.4.5 या इससे पहले फंस गए हैं), यहां मैंने वही किया है:

परीक्षण तालिका का निर्माण

CREATE TABLE test(id serial, data jsonb);
INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}');

Jsonb प्रॉपर्टी का नाम बदलने के लिए स्टेटमेंट अपडेट करें

UPDATE test 
SET data = replace(data::TEXT,'"name":','"my-other-name":')::jsonb 
WHERE id = 1;

अंतत: स्वीकार किया गया उत्तर सही है कि आप एक जोंसब ऑब्जेक्ट के व्यक्तिगत टुकड़े (9.4.5 या पूर्व में) को संशोधित नहीं कर सकते हैं; हालाँकि, आप जन्सब ऑब्जेक्ट को एक स्ट्रिंग (:: TEXT) में डाल सकते हैं और फिर स्ट्रिंग में हेरफेर कर सकते हैं और वापस jsonb ऑब्जेक्ट (:: jsonb) में डाल सकते हैं।

दो महत्वपूर्ण कैवेट हैं

  1. यह "नाम" नामक सभी गुणों को प्रतिस्थापित करेगा (मामले में आपके पास एक ही नाम के साथ कई गुण हैं)
  2. यह उतना आसान नहीं है जितना कि jsonb_set होगा यदि आप 9.5 का उपयोग कर रहे हैं

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


1
अच्छा प्रभु, मैं खोज रहा हूं कि कैसे दो घंटे के लिए जोंसब को अपडेट करना है ताकि मैं सभी \u0000अशक्त पात्रों को बदल सकूं , उदाहरण ने पूरी तस्वीर दिखाई। इसके लिए धन्यवाद!
जोशुआ रॉबिन्सन

3
अच्छा लग रहा है! btw अपने उदाहरण में बदलने के लिए दूसरा तर्क कोलन शामिल है और तीसरा नहीं करता है। ऐसा लगता है कि आपका कॉल होना चाहिएreplace(data::TEXT, '"name":', '"my-other-name":')::jsonb
davidicus

शुक्रिया @davidicus! बहुत विलंबित अपडेट के लिए क्षमा करें, लेकिन मैं आपको दूसरों के लिए साझा करने की सराहना करता हूं!
चाड Capra

12

यह प्रश्न 9.4 पोस्टरों के संदर्भ में पूछा गया था, लेकिन इस सवाल पर आने वाले नए दर्शकों को यह पता होना चाहिए कि पोस्टग्रॉस्स 9.5 में, JSONB फ़ील्ड्स पर उप-दस्तावेज़ बनाएँ / अपडेट करें / हटाएं कार्रवाई मूल रूप से डेटाबेस द्वारा समर्थित हैं, बिना एक्सटेंशन की आवश्यकता के कार्य करता है।

देखें: ऑपरेटरों और कार्यों को संशोधित JSONB


8

'नाम' विशेषता को अद्यतन करें:

UPDATE test SET data=data||'{"name":"my-other-name"}' WHERE id = 1;

और यदि आप उदाहरण के लिए 'नाम' और 'टैग' विशेषताओं को हटाना चाहते हैं:

UPDATE test SET data=data-'{"name","tags"}'::text[] WHERE id = 1;

6

मैंने अपने लिए छोटा-सा फंक्शन लिखा है जो पोस्टग्रैजेस 9.4 में रिकर्सिवली काम करता है। मुझे भी यही समस्या थी (अच्छा लगा कि उन्होंने इस पोस्टग्रेस 9.5 में इस सिरदर्द को हल किया है)। वैसे भी यहाँ फ़ंक्शन है (मुझे आशा है कि यह आपके लिए अच्छा काम करेगा):

CREATE OR REPLACE FUNCTION jsonb_update(val1 JSONB,val2 JSONB)
RETURNS JSONB AS $$
DECLARE
    result JSONB;
    v RECORD;
BEGIN
    IF jsonb_typeof(val2) = 'null'
    THEN 
        RETURN val1;
    END IF;

    result = val1;

    FOR v IN SELECT key, value FROM jsonb_each(val2) LOOP

        IF jsonb_typeof(val2->v.key) = 'object'
            THEN
                result = result || jsonb_build_object(v.key, jsonb_update(val1->v.key, val2->v.key));
            ELSE
                result = result || jsonb_build_object(v.key, v.value);
        END IF;
    END LOOP;

    RETURN result;
END;
$$ LANGUAGE plpgsql;

यहाँ नमूना उपयोग है:

select jsonb_update('{"a":{"b":{"c":{"d":5,"dd":6},"cc":1}},"aaa":5}'::jsonb, '{"a":{"b":{"c":{"d":15}}},"aa":9}'::jsonb);
                            jsonb_update                             
---------------------------------------------------------------------
 {"a": {"b": {"c": {"d": 15, "dd": 6}, "cc": 1}}, "aa": 9, "aaa": 5}
(1 row)

जैसा कि आप देख सकते हैं कि यह गहराई से विश्लेषण करता है और जहां आवश्यक है वहां मूल्यों को अपडेट / जोड़ देता है।


यह 9.4 में काम नहीं करता है, क्योंकि jsonb_build_object9.5 में पेश किया गया था
ग्रेग

@Greg आप सही हैं, मैंने अभी जाँच की है और मैं अब PostgreSQL 9.5 चला रहा हूँ - यही कारण है कि यह काम करता है। यह इंगित करने के लिए धन्यवाद - मेरा समाधान 9.4 में काम नहीं करेगा।
जे। रेज़कविक्ज़

4

हो सकता है: अद्यतन डेटा सेट करें डेटा = '"मेरा-अन्य-नाम" ":: json WHERE आईडी = 1;

यह मेरे मामले के साथ काम करता है, जहां डेटा एक json प्रकार है


1
मेरे लिए भी काम किया, postgresql 9.4.5 पर। पूरे रिकॉर्ड को फिर से लिखा गया है ताकि कोई एक फ़ील्ड atm को अपडेट न कर सके।
kometen

2

मैथ्यूस डी ओलिवेरा ने पोस्टग्रेजिकल में JSON CRUD संचालन के लिए काम किया। उन्हें \ i निर्देश के उपयोग से आयात किया जा सकता है। अगर आपके डेटा प्रकार अगर jsonb अगर कार्यों के jsonb कांटा पर ध्यान दें।

9.3 json https://gist.github.com/matheusoliveira/9488951

9.4 jsonb https://gist.github.com/inindev/2219dff96851928c2282

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