Postgres में मौजूदा कॉलम में 'सीरियल' जोड़ना


91

मेरी पोस्टग्रेज 9.0 डेटाबेस में एक पूर्णांक आईडी फ़ील्ड (प्राथमिक कुंजी) के साथ एक छोटी तालिका (~ 30 पंक्तियाँ) है जिसमें वर्तमान में 1 पर शुरू होने वाले अद्वितीय अनुक्रमिक पूर्णांक हैं, लेकिन जिसे 'धारावाहिक' कीवर्ड का उपयोग करके नहीं बनाया गया था।

मैं इस तालिका को इस तरह कैसे बदल सकता हूं कि अब से इस तालिका में इस क्षेत्र में इस तरह का व्यवहार किया जाएगा जैसे कि यह एक प्रकार के रूप में 'धारावाहिक' के साथ बनाया गया था?


5
FYI करें, SERIALछद्म प्रकार अब विरासत है , जिसे SQL: 2003 में परिभाषित नए GENERATED … AS IDENTITYफ़ीचर द्वारा पोस्टग्रेज 10 और बाद में हटा दिया गया है। स्पष्टीकरण देखें ।
बेसिल बोर्ख

आधुनिक पोस्टग्रेज संस्करण के लिए (> = 10) यह प्रश्न देखें: stackoverflow.com/questions/2944499
a_horse_with_no_name

जवाबों:


132

निम्नलिखित आदेशों को देखें (विशेष रूप से टिप्पणी की गई ब्लॉक)।

DROP TABLE foo;
DROP TABLE bar;

CREATE TABLE foo (a int, b text);
CREATE TABLE bar (a serial, b text);

INSERT INTO foo (a, b) SELECT i, 'foo ' || i::text FROM generate_series(1, 5) i;
INSERT INTO bar (b) SELECT 'bar ' || i::text FROM generate_series(1, 5) i;

-- blocks of commands to turn foo into bar
CREATE SEQUENCE foo_a_seq;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
ALTER TABLE foo ALTER COLUMN a SET NOT NULL;
ALTER SEQUENCE foo_a_seq OWNED BY foo.a;    -- 8.2 or later

SELECT MAX(a) FROM foo;
SELECT setval('foo_a_seq', 5);  -- replace 5 by SELECT MAX result

INSERT INTO foo (b) VALUES('teste');
INSERT INTO bar (b) VALUES('teste');

SELECT * FROM foo;
SELECT * FROM bar;

जब से आप अपने ओपी में प्राथमिक कुंजी का उल्लेख कर रहे हैं, आप भी करना चाहते हैं ALTER TABLE foo ADD PRIMARY KEY (a)
स्किप्पी ले ग्रैंड गौरौ

सीरिएल सिनटैक्ट शुगर है और डीबी मेटाडेटा में संग्रहीत नहीं है, इसलिए उपरोक्त कोड 100% के बराबर होगा।
DKroot

यदि एक मौका है कि लक्ष्य तालिका एक अलग उपयोगकर्ता द्वारा बनाई गई थी, तो आपको ALTER TABLE foo OWNER TO current_user;पहले करना होगा ।
डीकेरोट

2
क्या आपको MAX(a)+1सेवल में सेटिंग नहीं करनी चाहिए ? SELECT MAX(a)+1 FROM foo; SELECT setval('foo_a_seq', 6);
सनीप्रो

48

आप START WITHकिसी विशेष बिंदु से अनुक्रम शुरू करने के लिए भी उपयोग कर सकते हैं , हालांकि सेवल एक ही चीज़ को पूरा करता है, जैसे कि यूलर के उत्तर में, उदाहरण के लिए,

SELECT MAX(a) + 1 FROM foo;
CREATE SEQUENCE foo_a_seq START WITH 12345; -- replace 12345 with max above
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');

28

टी एल; डॉ

यहाँ एक संस्करण है जहाँ आपको किसी मान को पढ़ने और उसे स्वयं टाइप करने के लिए मानव की आवश्यकता नहीं है।

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq'); 

एक अन्य विकल्प Functionइस उत्तर के अंत में साझा किए गए पुन: प्रयोज्य को नियोजित करना होगा ।


एक गैर-संवादात्मक समाधान

उदाहरण के लिए, एक लाइव-ईश डीबी को पैच करते हुए, हम में से उन दो लोगों के लिए, जो Sequenceएक गैर-इंटरैक्टिव स्क्रिप्ट द्वारा बनाए जाने की आवश्यकता है, उन दोनों के लिए अन्य दो उत्तरों को जोड़ते हुए।

यही है, जब आप SELECTमैन्युअल रूप से मूल्य नहीं चाहते हैं और बाद के CREATEबयान में खुद को टाइप करते हैं ।

संक्षेप में, आप कर सकते हैं नहीं :

CREATE SEQUENCE foo_a_seq
    START WITH ( SELECT max(a) + 1 FROM foo );

... के बाद से START [WITH] खंड CREATE SEQUENCEएक मूल्य की अपेक्षा करता है , उपशम की नहीं।

नोट: एक सामान्य नियम के, सभी गैर-CRUD (लागू होने के रूप में यानी : के अलावा और कुछ INSERT, SELECT,UPDATE , DELETEमें बयान) pgSQL AFAIK।

हालाँकि, setval()करता है! इस प्रकार, निम्नलिखित बिल्कुल ठीक है:

SELECT setval('foo_a_seq', max(a)) FROM foo;

यदि कोई डेटा नहीं है और आप इसके बारे में नहीं जानना चाहते हैं, तो उपयोग करें coalesce() तो डिफ़ॉल्ट मान सेट लिए करें:

SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
--                         ^      ^         ^
--                       defaults to:       0

हालाँकि, वर्तमान अनुक्रम मान सेट करना 0अनाड़ी है, यदि अवैध नहीं है।
के तीन-पैरामीटर रूप का उपयोग करनाsetval करना अधिक उपयुक्त होगा:

--                                             vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
--                                                  ^   ^
--                                                is_called

के वैकल्पिक तीसरे पैरामीटर की स्थापना setvalकरने के लिएfalsenextval से मूल्य को वापस करने से पहले अनुक्रम को आगे बढ़ाने से रोका जा सकता है , और इस प्रकार:

अगला nextval वास्तव में निर्दिष्ट मान लौटाएगा, और अनुक्रम उन्नति निम्नलिखित के साथ शुरू होगी nextval

- प्रलेखन में इस प्रविष्टि से

एक असंबंधित नोट पर, आप Sequenceसीधे कॉलम के साथ निर्दिष्ट कर सकते हैं CREATE, आपको इसे बाद में बदलने की आवश्यकता नहीं है:

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;

संक्षेप में:

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq'); 

इसका उपयोग करना Function

वैकल्पिक रूप से, यदि आप कई स्तंभों के लिए ऐसा करने की योजना बना रहे हैं, तो आप वास्तविक उपयोग करने का विकल्प चुन सकते हैं Function

CREATE OR REPLACE FUNCTION make_into_serial(table_name TEXT, column_name TEXT) RETURNS INTEGER AS $$
DECLARE
    start_with INTEGER;
    sequence_name TEXT;
BEGIN
    sequence_name := table_name || '_' || column_name || '_seq';
    EXECUTE 'SELECT coalesce(max(' || column_name || '), 0) + 1 FROM ' || table_name
            INTO start_with;
    EXECUTE 'CREATE SEQUENCE ' || sequence_name ||
            ' START WITH ' || start_with ||
            ' OWNED BY ' || table_name || '.' || column_name;
    EXECUTE 'ALTER TABLE ' || table_name || ' ALTER COLUMN ' || column_name ||
            ' SET DEFAULT nextVal(''' || sequence_name || ''')';
    RETURN start_with;
END;
$$ LANGUAGE plpgsql VOLATILE;

इसका इस्तेमाल ऐसे करें:

INSERT INTO foo (data) VALUES ('asdf');
-- ERROR: null value in column "a" violates not-null constraint

SELECT make_into_serial('foo', 'a');
INSERT INTO foo (data) VALUES ('asdf');
-- OK: 1 row(s) affected

महान उत्तर, लेकिन ध्यान रखें coalesce(max(a), 0))कि ज्यादातर समय काम नहीं करेगा, क्योंकि Ids आमतौर पर 1 से शुरू होता है। अधिक सही तरीका होगाcoalesce(max(a), 1))
Amiko

1
टिप्पणी के लिए @Amiko धन्यवाद! setvalसमारोह वास्तव में केवल दृश्य के लिए वर्तमान "हाल ही में उपयोग मूल्य" सेट। अगले उपलब्ध मूल्य (वास्तव में इस्तेमाल होने वाला पहला) एक और होगा! setval(..., coalesce(max(a), 1))एक खाली कॉलम का उपयोग करके इसे 2(अगले उपलब्ध मूल्य के साथ) "शुरू" करने के लिए सेट किया जाएगा , जैसा कि प्रलेखन में सचित्र है ।
ccjmne

1
@ अमिको आप यह कहने में सही हैं कि मेरे कोड में कोई समस्या है, हालांकि: currvalकभी 0भी ऐसा नहीं होना चाहिए , भले ही वह वास्तविक डेटासेट में प्रतिबिंबित न हो। के तीन-पैरामीटर रूप का उपयोग setvalकरना अधिक उपयुक्त होगा setval(..., coalesce(max(a), 0) + 1, false):। तदनुसार अद्यतन किया गया!
ccjmne

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