PostgreSQL Autoincrement


578

ฉันเปลี่ยนจาก MySQL เป็น PostgreSQL และสงสัยว่าฉันจะทำค่าการรวมอัตโนมัติได้อย่างไร ฉันเห็นใน PostgreSQL เอกสารประเภทข้อมูล "อนุกรม" แต่ฉันได้รับข้อผิดพลาดทางไวยากรณ์เมื่อใช้มัน (ใน v8.0)


9
หากคุณระบุคำถามและข้อผิดพลาดที่คุณได้รับอาจมีบางคนบอกคุณได้ว่าเกิดอะไรขึ้นกับแบบสอบถาม

2
การตีครั้งแรกของฉันก็เช่นกัน Mich 'และเนื่องจากเป็นคำถามที่ได้รับการดูที่เกี่ยวข้องเพียงพอทำไมไม่โหวตมัน ป.ล. มันไม่สำคัญถ้าคุณไม่รู้ว่าจะทำอย่างไร
baash05

1
SERIAL เป็นตัวเลือกที่ต้องการหากไดรเวอร์ไคลเอ็นต์ของคุณคือ Npgsql ผู้ให้บริการกำลังเลือกค่าใหม่ภายใน INSERT โดยใช้ SELECT currval (pg_get_serial_sequence ('table', 'column')) สิ่งนี้จะล้มเหลวหากคอลัมน์ที่อยู่ภายในนั้นไม่ได้เป็นประเภทอนุกรม (เช่นประเภทตัวเลข + ลำดับที่ชัดเจนเป็นต้น)
Olivier MATROT

เพื่อความอยากรู้อยากเห็น ... ทำไมบางคนต้องโยกย้ายจาก MySQL ซึ่งดีมากไปยัง PostgreSql
villamejia

17
... ไหนดีกว่ากัน
Rohmer

คำตอบ:


701

ใช่แล้ว SERIAL เป็นฟังก์ชั่นที่เทียบเท่า

CREATE TABLE foo (
id SERIAL,
bar varchar);

INSERT INTO foo (bar) values ('blah');
INSERT INTO foo (bar) values ('blah');

SELECT * FROM foo;

1,blah
2,blah

SERIAL เป็นเพียงสร้างตารางเวลาแมโครรอบลำดับ คุณไม่สามารถแก้ไข SERIAL ลงในคอลัมน์ที่มีอยู่


19
การอ้างถึงชื่อตารางเป็นการปฏิบัติที่เลวร้ายจริงๆ
Evan Carroll

71
การอ้างอิงชื่อตารางเป็นนิสัยเนื่องจากฉันสืบทอด DB ที่มีชื่อเคสแบบผสมและชื่อตารางเป็นข้อกำหนดในการใช้งาน
แต้ม

26
@Evan Carroll - ทำไมมันเป็นนิสัยที่ไม่ดี (แค่ถาม)?
Christian

27
เพราะถ้าคุณมีตาราง"Table"และ"table"แล้วก็ปล่อยให้มัน unquoted และ canonicalize tableมัน การประชุมไม่เคยใช้เครื่องหมายคำพูดใน Pg คุณสามารถใช้ชื่อกรณีแบบผสมเพื่อการปรากฏถ้าคุณต้องการคุณไม่จำเป็นต้องใช้: CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;จะทำงานตามที่SELECT * FROM foobarต้องการ
Evan Carroll

26
อ้างอิงต่อจาก postgres doc ไม่ว่าจะเป็นคำพูดหรือ unquote: postgresql.org/docs/current/interactive/
......

226

คุณสามารถใช้อื่น ๆชนิดข้อมูลจำนวนเต็มsmallintเช่น

ตัวอย่าง:

CREATE SEQUENCE user_id_seq;
CREATE TABLE user (
    user_id smallint NOT NULL DEFAULT nextval('user_id_seq')
);
ALTER SEQUENCE user_id_seq OWNED BY user.user_id;

ดีกว่าที่จะใช้ชนิดข้อมูลของคุณเองมากกว่าผู้ใช้ชนิดข้อมูลแบบอนุกรม


11
ฉันจะบอกว่านี่เป็นคำตอบที่ดีกว่าเพราะมันอนุญาตให้ฉันแก้ไขตารางที่ฉันเพิ่งสร้างใน PostgreSQL โดยการตั้งค่าคอลัมน์เริ่มต้น (หลังจากอ่านบนCREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.html ) . อย่างไรก็ตามฉันไม่แน่ใจว่าทำไมคุณเปลี่ยนเจ้าของ
JayC

12
@JayC: จากเอกสาร : สุดท้ายลำดับถูกทำเครื่องหมายเป็น "คอลัมน์" เพื่อให้มันจะถูกทิ้งหากคอลัมน์หรือตารางถูกทิ้ง
272735

8
ทำไมชุมชน postgres ไม่เพียงสร้างคำหลักการสร้างอัตโนมัติใหม่อีกครั้ง
ดร. Deo

2
@Dr โอ: พวกเขาใช้คำหลัก autoincrement แทนอนุกรมผมไม่ทราบว่าทำไม :)
อาห์หมัด

4
นอกจากนี้ยังมีขนาดเล็กลงถ้าคุณต้องการชนิดข้อมูลที่เล็กลง
beldaz

110

หากคุณต้องการเพิ่มลำดับลงใน id ซึ่งมีอยู่แล้วคุณสามารถใช้:

CREATE SEQUENCE user_id_seq;
ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');

ลำดับคืออะไร AUTO_INCREMENT อยู่ที่ไหน
สีเขียว

23
@Green: AUTO_INCREMENT ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน SQL โดยเฉพาะกับ MySQL ลำดับเป็นสิ่งที่ทำงานคล้ายกันใน PostgreSQL
beldaz

5
ถ้าคุณใช้ 'id SERIAL' มันจะสร้างลำดับใน PostgreSQL โดยอัตโนมัติ ชื่อของลำดับนั้นจะเป็น <table name> _ <column name> _seq
Jude Niroshan

คุณไม่ต้องใช้ALTER COLUMN user_idเหรอ?
Alec

ฉันลองวิธีนี้ แต่ได้รับข้อผิดพลาด: ERROR: syntax error at or near "DEFAULT"คำแนะนำใด ๆ
Ely Fialkoff

44

ในขณะที่ดูเหมือนว่าลำดับจะเทียบเท่ากับ MySQL auto_increment แต่ก็มีความแตกต่างเล็กน้อย แต่สำคัญ:

1. การค้นหาล้มเหลวการเพิ่มลำดับ / อนุกรม

คอลัมน์อนุกรมได้รับการเพิ่มขึ้นจากการสืบค้นที่ล้มเหลว สิ่งนี้นำไปสู่การกระจายตัวของแบบสอบถามที่ล้มเหลวไม่ใช่แค่การลบแถว ตัวอย่างเช่นรันเคียวรีต่อไปนี้บนฐานข้อมูล PostgreSQL ของคุณ:

CREATE TABLE table1 (
  uid serial NOT NULL PRIMARY KEY,
  col_b integer NOT NULL,
  CHECK (col_b>=0)
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

SELECT * FROM table1;

คุณควรได้รับผลลัพธ์ต่อไปนี้:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
(2 rows)

สังเกตว่า uid ไปจาก 1 ถึง 3 แทนที่จะเป็น 1 ถึง 2 ได้อย่างไร

สิ่งนี้ยังคงเกิดขึ้นหากคุณต้องสร้างลำดับของคุณเองด้วย:

CREATE SEQUENCE table1_seq;
CREATE TABLE table1 (
    col_a smallint NOT NULL DEFAULT nextval('table1_seq'),
    col_b integer NOT NULL,
    CHECK (col_b>=0)
);
ALTER SEQUENCE table1_seq OWNED BY table1.col_a;

หากคุณต้องการทดสอบว่า MySQL แตกต่างกันอย่างไรให้รันสิ่งต่อไปนี้บนฐานข้อมูล MySQL:

CREATE TABLE table1 (
  uid int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  col_b int unsigned NOT NULL
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

คุณควรได้รับสิ่งต่อไปนี้โดยไม่มีการจัดการ :

+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
+-----+-------+
2 rows in set (0.00 sec)

2. การกำหนดค่าคอลัมน์อนุกรมด้วยตนเองสามารถทำให้การสืบค้นในอนาคตล้มเหลว

สิ่งนี้ชี้ให้เห็นโดย @trev ในคำตอบก่อนหน้า

เพื่อจำลองสิ่งนี้ตั้ง uid เป็น 4 ซึ่งจะ "ปะทะกัน" ในภายหลัง

INSERT INTO table1 (uid, col_b) VALUES(5, 5);

ข้อมูลตาราง:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
   5 |     5
(3 rows)

เรียกใช้ตัวแทรกอื่น:

INSERT INTO table1 (col_b) VALUES(6);

ข้อมูลตาราง:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
   5 |     5
   4 |     6

ตอนนี้ถ้าคุณเรียกใช้ตัวแทรกอื่น:

INSERT INTO table1 (col_b) VALUES(7);

มันจะล้มเหลวด้วยข้อความแสดงข้อผิดพลาดต่อไปนี้:

ข้อผิดพลาด: ค่าคีย์ที่ซ้ำกันละเมิดข้อ จำกัด ที่ไม่ซ้ำกัน "table1_pkey" รายละเอียด: Key (uid) = (5) มีอยู่แล้ว

ในทางตรงกันข้าม MySQL จะจัดการสิ่งนี้อย่างสง่างามดังที่แสดงด้านล่าง:

INSERT INTO table1 (uid, col_b) VALUES(4, 4);

ตอนนี้แทรกแถวอื่นโดยไม่มีการตั้งค่า uid

INSERT INTO table1 (col_b) VALUES(3);

การสืบค้นไม่ได้ล้มเหลว uid เพียงข้ามไปที่ 5:

+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
|   4 |     4 |
|   5 |     3 |
+-----+-------+

ทำการทดสอบบน MySQL 5.6.33 สำหรับ Linux (x86_64) และ PostgreSQL 9.4.9


10
คุณกำลังเปรียบเทียบ แต่ฉันไม่เห็นวิธีแก้ปัญหาที่นี่! มันคือคำตอบ?
อันวาร์

4
@ รู้ว่ามันแค่ขยายคำตอบต่าง ๆ ที่ระบุว่าคำตอบคือการใช้อนุกรม / ลำดับ นี่เป็นบริบทที่สำคัญที่ต้องคำนึงถึง
โปรแกรม

38

เริ่มต้นด้วย Postgres 10 สนับสนุนคอลัมน์ข้อมูลประจำตัวตามที่กำหนดโดยมาตรฐาน SQL:

create table foo 
(
  id integer generated always as identity
);

สร้างคอลัมน์ข้อมูลประจำตัวที่ไม่สามารถเขียนทับได้เว้นแต่จะมีการขออย่างชัดเจน การแทรกต่อไปนี้จะล้มเหลวโดยมีคอลัมน์ที่กำหนดเป็นgenerated always:

insert into foo (id) 
values (1);

อย่างไรก็ตามสิ่งนี้สามารถแก้ไขได้:

insert into foo (id) overriding system value 
values (1);

เมื่อใช้ตัวเลือกgenerated by defaultนี่เป็นพฤติกรรมเดียวกับserialการใช้งานที่มีอยู่:

create table foo 
(
  id integer generated by default as identity
);

เมื่อมีการให้ค่าด้วยตนเองลำดับที่จำเป็นต้องปรับด้วยตนเองเช่นเดียวกับserialคอลัมน์


คอลัมน์ข้อมูลประจำตัวไม่ใช่คีย์หลักโดยค่าเริ่มต้น (เช่นเดียวกับserialคอลัมน์) หากควรเป็นหนึ่งข้อ จำกัด คีย์หลักจะต้องกำหนดด้วยตนเอง


26

ขออภัยที่จะถามคำถามเก่า แต่นี่เป็นคำถาม / คำตอบสแต็คล้นแรกที่โผล่ขึ้นมาบน Google

โพสต์นี้ (ซึ่งขึ้นมาเป็นลำดับแรกบน Google) พูดถึงการใช้ไวยากรณ์ที่อัปเดตมากขึ้นสำหรับ PostgreSQL 10: https://blog.2ndquadrant.com/postgresql-10-identity-columns/

ซึ่งเกิดขึ้นเป็น:

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
);

หวังว่าจะช่วย :)


1
นี่เป็นวิธีไปใน PostgreSQL 10 และเป็นไวยากรณ์เดียวกับซอฟต์แวร์ฐานข้อมูลอื่นเช่น DB2 หรือ Oracle
adriaan

1
@adriaan จริง ๆ แล้วGENERATED … AS IDENTITYคำสั่งคือ SQL มาตรฐาน เพิ่มครั้งแรกในSQL: 2003 ที่แล้วชี้แจงในSQL: 2008 ดูคุณสมบัติ # T174 & F386 & T178
Basil Bourque

16

คุณต้องระวังไม่ให้แทรกลงใน SERIAL หรือฟิลด์ลำดับโดยตรงมิฉะนั้นการเขียนของคุณจะล้มเหลวเมื่อลำดับมาถึงค่าที่แทรกไว้:

-- Table: "test"

-- DROP TABLE test;

CREATE TABLE test
(
  "ID" SERIAL,
  "Rank" integer NOT NULL,
  "GermanHeadword" "text" [] NOT NULL,
  "PartOfSpeech" "text" NOT NULL,
  "ExampleSentence" "text" NOT NULL,
  "EnglishGloss" "text"[] NOT NULL,
  CONSTRAINT "PKey" PRIMARY KEY ("ID", "Rank")
)
WITH (
  OIDS=FALSE
);
-- ALTER TABLE test OWNER TO postgres;
 INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (1, '{"der", "die", "das", "den", "dem", "des"}', 'art', 'Der Mann küsst die Frau und das Kind schaut zu', '{"the", "of the" }');


 INSERT INTO test("ID", "Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (2, 1, '{"der", "die", "das"}', 'pron', 'Das ist mein Fahrrad', '{"that", "those"}');

 INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (1, '{"der", "die", "das"}', 'pron', 'Die Frau, die nebenen wohnt, heißt Renate', '{"that", "who"}');

SELECT * from test; 

15

ในบริบทของคำถามที่ถามและในการตอบกลับความคิดเห็นโดย @ sereja1c การSERIALสร้างจะสร้างลำดับโดยปริยายดังนั้นสำหรับตัวอย่างด้านบน -

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEโดยปริยายจะสร้างลำดับสำหรับคอลัมน์อนุกรมfoo_id_seq foo.idดังนั้นSERIAL[4 Bytes] นั้นดีสำหรับการใช้งานที่ง่ายเว้นแต่คุณต้องการประเภทข้อมูลเฉพาะสำหรับ id ของคุณ


3

วิธีนี้จะทำงานได้อย่างแน่นอนฉันหวังว่ามันจะช่วย:

CREATE TABLE fruits(
   id SERIAL PRIMARY KEY,
   name VARCHAR NOT NULL
);

INSERT INTO fruits(id,name) VALUES(DEFAULT,'apple');

or

INSERT INTO fruits VALUES(DEFAULT,'apple');

คุณสามารถตรวจสอบรายละเอียดนี้ได้ในลิงค์ถัดไป: http://www.postgresqltutorial.com/postgresql-serial/


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.