2010-08-12 7 views
10

मैं पोस्टग्रेज़ 8.4 डेटाबेस (सी # कोड से) अपडेट कर रहा हूं और मूल कार्य काफी सरल है: या तो मौजूदा पंक्ति को अपडेट करें या किसी एक को INSERT करें यदि कोई ' टी अभी तक मौजूद नहीं है। आम तौर पर मैं यह कर जाएगा:पोस्टग्रेर्स यूपीएसईआरटी (INSERT या UPDATE) केवल तभी मूल्य है जब 0 अलग-अलग

UPDATE my_table 
SET value1 = :newvalue1, ..., updated_time = now(), updated_username = 'evgeny' 
WHERE criteria1 = :criteria1 AND criteria2 = :criteria2 

और 0 पंक्तियों तो एक सम्मिलित कर प्रभावित हुए हैं:,

INSERT INTO my_table(criteria1, criteria2, value1, ...) 
VALUES (:criteria1, :criteria2, :newvalue1, ...) 

एक मामूली मोड़ नहीं है, हालांकि। मैं अपडेटेड_टाइम और अपडेटेड_सनेम स्तंभों को तब तक बदलना नहीं चाहता जब तक कि कोई भी नया मान मौजूदा मानों से वास्तव में अलग न हो, ताकि डेटा अपडेट होने के बारे में भ्रामक उपयोगकर्ताओं से बच सकें।

अगर मैं केवल एक अद्यतन कर रहा था तो मैं मूल्यों के लिए कहां स्थितियों को जोड़ सकता था, लेकिन यह यहां काम नहीं करेगा, क्योंकि अगर डीबी पहले से ही अद्यतित है तो अद्यतन 0 पंक्तियों को प्रभावित करेगा और फिर मैं INSERT करने का प्रयास करें।

कोई भी इसे चुनने के अलावा, या तो अद्यतन या INSERT के अलावा ऐसा करने का एक शानदार तरीका सोच सकता है?

+0

डुप्लिकेट अपडेट (पोस्टग्रेस्क्ल) पर सम्मिलित करें, डालें (http://stackoverflow.com/questions/1109061/insert-on-duplicate-update-postgresql) –

+0

नहीं, डुप्लिकेट नहीं। वहां जवाब जो मूल रूप से ऊपर लिखा है, उस कार्य में बस encapsulates। – EMP

उत्तर

-1

एक लेनदेन शुरू करें। यह देखने के लिए एक चयन का उपयोग करें कि आप जो डेटा डालना चाहते हैं, वह पहले से मौजूद है, अगर ऐसा होता है, तो कुछ भी नहीं करें, अन्यथा अपडेट करें, अगर यह अस्तित्व में नहीं है, तो सम्मिलित करें। अंत में लेनदेन बंद करो।

+2

यह उप-शीर्ष है क्योंकि लेनदेन में पंक्तियों का एक गुच्छा थोक करने के लिए एक सामान्य उपयोग मामला थोक हो सकता है –

4

की जाँच करें और सही मूल्यों को निर्धारित करने के लिए एक से पहले अद्यतन ट्रिगर पर एक नज़र डालें:

CREATE OR REPLACE FUNCTION my_trigger() RETURNS TRIGGER LANGUAGE plpgsql AS 
$$ 
BEGIN 
    IF OLD.content = NEW.content THEN 
     NEW.updated_time= OLD.updated_time; -- use the old value, not a new one. 
    ELSE 
     NEW.updated_time= NOW(); 
    END IF; 
    RETURN NEW; 
END; 
$$; 

अब तुम भी अपने अद्यतन क्वेरी में क्षेत्र updated_time उल्लेख करने के लिए नहीं है, यह द्वारा नियंत्रित किया जाएगा ट्रिगर। यहाँ

http://www.postgresql.org/docs/current/interactive/plpgsql-trigger.html

5

दो बातें। सबसे पहले अपने डेटाबेस में गतिविधि स्तरों के आधार पर आप रिकॉर्ड की जांच करने और इसे डालने के बीच दौड़ की स्थिति को हिट कर सकते हैं जहां एक और प्रक्रिया अंतरिम में उस रिकॉर्ड को बना सकती है। मैनुअल कैसे करना है इस link example

एक अद्यतन suppress_redundant_updates_trigger() प्रक्रिया है कर से बचने के लिए का एक उदाहरण में शामिल है। इसका उपयोग करने के लिए आप चाहते हैं कि आपके पास अद्यतन ट्रिगर्स से पहले दो होना चाहिए, पहले अगर कोई बदलाव नहीं किया गया है और दूसरा अपडेट टाइमस्टैम्प और उपयोगकर्ता नाम सेट करने के लिए अपडेट को रद्द करने के लिए suppress_redundant_updates_trigger() को कॉल करने के लिए कॉल करेगा। ट्रिगर्स को वर्णानुक्रम में निकाल दिया जाता है। ऐसा करने से पहले अद्यतन से पहले सम्मिलित करने का प्रयास करने के लिए ऊपर दिए गए उदाहरण में कोड को बदलना भी होगा।

कैसे दबाने अद्यतन काम करता है का उदाहरण:

DROP TABLE sru_test; 

    CREATE TABLE sru_test(id integer not null primary key, 
    data text, 
    updated timestamp(3)); 

    CREATE TRIGGER z_min_update 
    BEFORE UPDATE ON sru_test 
    FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); 

    DROP FUNCTION set_updated(); 

    CREATE FUNCTION set_updated() 
    RETURNS TRIGGER 
    AS $$ 
    DECLARE 
    BEGIN 
     NEW.updated := now(); 
     RETURN NEW; 
    END; 
    $$ LANGUAGE plpgsql; 

    CREATE TRIGGER zz_set_updated 
    BEFORE INSERT OR UPDATE ON sru_test 
    FOR EACH ROW EXECUTE PROCEDURE set_updated(); 

insert into sru_test(id,data) VALUES (1,'Data 1'); 
insert into sru_test(id,data) VALUES (2,'Data 2'); 

select * from sru_test; 

update sru_test set data = 'NEW'; 

select * from sru_test; 

update sru_test set data = 'NEW'; 

select * from sru_test; 

update sru_test set data = 'ALTERED' where id = 1; 

select * from sru_test; 

update sru_test set data = 'NEW' where id = 2; 

select * from sru_test; 
0

RETURNING खंड श्रृंखला आपके प्रश्नों में सक्षम बनाता है; दूसरी क्वेरी पहले से परिणामों का उपयोग करती है। (इस मामले में एक ही पंक्तियों को फिर से छूने से बचने के लिए) (वापसी पोस्टग्रेस 8 के बाद उपलब्ध है।4)

यहाँ दिखाया गया है एक एक समारोह में एम्बेडेड है, लेकिन यह सादा एसक्यूएल के लिए काम करता है, भी

DROP SCHEMA tmp CASCADE; 
CREATE SCHEMA tmp ; 
SET search_path=tmp; 

CREATE TABLE my_table 
     (updated_time timestamp NOT NULL DEFAULT now() 
     , updated_username varchar DEFAULT '_none_' 
     , criteria1 varchar NOT NULL 
     , criteria2 varchar NOT NULL 
     , value1 varchar 
     , value2 varchar 
     , PRIMARY KEY (criteria1,criteria2) 
     ); 

INSERT INTO my_table (criteria1,criteria2,value1,value2) 
SELECT 'C1_' || gs::text 
     , 'C2_' || gs::text 
     , 'V1_' || gs::text 
     , 'V2_' || gs::text 
FROM generate_series(1,10) gs 
     ; 

SELECT * FROM my_table ; 

CREATE function funky(_criteria1 text,_criteria2 text, _newvalue1 text, _newvalue2 text) 
RETURNS VOID 
AS $funk$ 
WITH ins AS (
     INSERT INTO my_table(criteria1, criteria2, value1, value2, updated_username) 
     SELECT $1, $2, $3, $4, COALESCE(current_user, 'evgeny') 
     WHERE NOT EXISTS (
       SELECT * FROM my_table nx 
       WHERE nx.criteria1 = $1 AND nx.criteria2 = $2 
       ) 
     RETURNING criteria1 AS criteria1, criteria2 AS criteria2 
     ) 
     UPDATE my_table upd 
     SET value1 = $3, value2 = $4 
     , updated_time = now() 
     , updated_username = COALESCE(current_user, 'evgeny') 
     WHERE 1=1 
     AND criteria1 = $1 AND criteria2 = $2 -- key-condition 
     AND (value1 <> $3 OR value2 <> $4) -- row must have changed 
     AND NOT EXISTS (
       SELECT * FROM ins -- the result from the INSERT 
       WHERE ins.criteria1 = upd.criteria1 
       AND ins.criteria2 = upd.criteria2 
       ) 
     ; 
$funk$ language sql 
     ; 

SELECT funky('AA', 'BB' , 'CC', 'DD');   -- INSERT 
SELECT funky('C1_3', 'C2_3' , 'V1_3', 'V2_3'); -- (null) UPDATE 
SELECT funky('C1_7', 'C2_7' , 'V1_7', 'V2_7777'); -- (real) UPDATE 

SELECT * FROM my_table ; 

परिणाम:

 updated_time  | updated_username | criteria1 | criteria2 | value1 | value2 
----------------------------+------------------+-----------+-----------+--------+--------- 
2013-03-13 16:37:55.405267 | _none_   | C1_1  | C2_1  | V1_1 | V2_1 
2013-03-13 16:37:55.405267 | _none_   | C1_2  | C2_2  | V1_2 | V2_2 
2013-03-13 16:37:55.405267 | _none_   | C1_3  | C2_3  | V1_3 | V2_3 
2013-03-13 16:37:55.405267 | _none_   | C1_4  | C2_4  | V1_4 | V2_4 
2013-03-13 16:37:55.405267 | _none_   | C1_5  | C2_5  | V1_5 | V2_5 
2013-03-13 16:37:55.405267 | _none_   | C1_6  | C2_6  | V1_6 | V2_6 
2013-03-13 16:37:55.405267 | _none_   | C1_8  | C2_8  | V1_8 | V2_8 
2013-03-13 16:37:55.405267 | _none_   | C1_9  | C2_9  | V1_9 | V2_9 
2013-03-13 16:37:55.405267 | _none_   | C1_10  | C2_10  | V1_10 | V2_10 
2013-03-13 16:37:55.463651 | postgres   | AA  | BB  | CC  | DD 
2013-03-13 16:37:55.472783 | postgres   | C1_7  | C2_7  | V1_7 | V2_7777 
(11 rows) 
2

Postgres Upsert समर्थन मिल रहा है।

यह सुविधा अक्सर Upsert के रूप में जाना जाता है: यह में tree 8 मई 2015 (commit) के बाद से वर्तमान में है।

इसे "सट्टा सम्मिलन" नामक एक नए आधारभूत संरचना का उपयोग करके लागू किया गया है। यह नियमित प्रविष्टि का एक आशावादी संस्करण है कि पहले मौजूदा tuples के लिए पूर्व-जांच करता है और फिर सम्मिलित करता है। यदि एक उल्लंघनकारी ट्यूपल समवर्ती रूप से डाला गया था, तो अनुमानित रूप से सम्मिलित टुपल हटा दिया गया है और एक नया प्रयास किया गया है। यदि प्री-चेक वैकल्पिक मिलान करने वाला विकल्प नहीं है या डीओ अद्यतन कार्रवाई की जाती है। यदि प्रविष्टि संघर्ष का पता लगाए बिना सफल हो जाती है, तो ट्यूपल को सम्मिलित समझा जाता है।

एक स्नैपशॉट available for download है। यह अभी तक a release नहीं बनाया है।

संबंधित मुद्दे