PostgreQuery Autoincrement


579

Tôi đang chuyển từ MySQL sang PostgreSQL và đang tự hỏi làm thế nào tôi có thể thực hiện các giá trị tự động. Tôi đã thấy trong tài liệu PostgreQuery một kiểu dữ liệu "nối tiếp", nhưng tôi gặp lỗi cú pháp khi sử dụng nó (trong v8.0).


9
nếu bạn cung cấp truy vấn và lỗi bạn gặp phải - có lẽ ai đó có thể cho bạn biết điều gì sai với truy vấn.

2
Lần đầu tiên tôi trúng Mich 'và vì đó là một câu hỏi có đủ lượt xem có liên quan, tại sao không bỏ phiếu. PS nó không tầm thường nếu bạn không biết làm thế nào.
baash05

1
SERIAL là lựa chọn ưu tiên nếu trình điều khiển máy khách của bạn là Npgsql. Nhà cung cấp đang chọn nội bộ các giá trị mới sau một INSERT bằng cách sử dụng dòng CHỌN (pg_get_serial_ resultence ('bảng', 'cột')). Điều này sẽ thất bại nếu cột bên dưới không phải là kiểu nối tiếp (ví dụ kiểu số + chuỗi rõ ràng)
Olivier MATROT

Chỉ vì tò mò ... Tại sao ai đó phải di chuyển từ MySQL, điều này rất tốt, sang PostgreSql?
Villamejia

17
... Điều đó thậm chí còn tốt hơn.
Rohmer

Câu trả lời:


702

Có, SERIAL là chức năng tương đương.

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 chỉ là một macro thời gian tạo bảng xung quanh các chuỗi. Bạn không thể thay đổi SERIAL trên một cột hiện có.


19
trích dẫn tên bảng là một thực tế tồi tệ
Evan Carroll

71
Trích dẫn tên bảng là một thói quen vì tôi được thừa hưởng một DB có tên trường hợp hỗn hợp và trích dẫn tên bảng là một yêu cầu sử dụng.
Trey

26
@Evan Carroll - Tại sao nó là một thói quen xấu (chỉ hỏi)?
Christian

27
bởi vì trừ khi bạn có một bảng "Table""table"sau đó chỉ để lại nó không được trích dẫn và chuẩn hóa nó table. Quy ước đơn giản là không bao giờ sử dụng dấu ngoặc kép trong PGS. Nếu bạn muốn, bạn có thể sử dụng tên trường hợp hỗn hợp để xuất hiện, chỉ cần không yêu cầu nó: CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;sẽ hoạt động, như ý muốn SELECT * FROM foobar.
Evan Carroll

26
Mỗi tài liệu postgres, được trích dẫn một cách nhất quán hoặc bỏ qua: postgresql.org/docs/civerse/interactive/
mẹo

225

Bạn có thể sử dụng bất kỳ loại dữ liệu số nguyên khác , chẳng hạn như smallint.

Thí dụ :

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;

Tốt hơn là sử dụng kiểu dữ liệu của riêng bạn, thay vì kiểu dữ liệu nối tiếp của người dùng .


11
Tôi muốn nói rằng đây thực sự là câu trả lời tốt hơn vì nó cho phép tôi sửa đổi một bảng mà tôi vừa tạo trong PostgreQuery bằng cách đặt các cột mặc định (sau khi đọc trên CREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.html ) . TUY NHIÊN, tôi không chắc tại sao bạn thay đổi chủ sở hữu.
JayC

12
@JayC: Từ tài liệu : Cuối cùng, chuỗi được đánh dấu là "thuộc sở hữu" của cột, do đó nó sẽ bị hủy nếu cột hoặc bảng bị hủy.
dùng272735

9
Tại sao cộng đồng postgres không chỉ phát minh lại từ khóa tự động?
Bác sĩ Đèo

2
@Dr Deo: họ sử dụng từ khóa thay thế tự động nối tiếp, tôi không biết tại sao :)
Ahmad

4
Cũng có smallserial nếu bạn chỉ muốn một kiểu dữ liệu nhỏ hơn.
beldaz

110

Nếu bạn muốn thêm chuỗi vào id trong bảng đã tồn tại, bạn có thể sử dụng:

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

Trình tự là gì? AUTO_INCREMENT ở đâu?
Màu xanh lá cây

23
@Green: AUTO_INCREMENT không phải là một phần của tiêu chuẩn SQL, nó dành riêng cho MySQL. Chuỗi là một cái gì đó làm một công việc tương tự trong PostgreSQL.
beldaz

5
nếu bạn sử dụng 'id SERIAL', nó sẽ tự động tạo một chuỗi trong PostgreSQL. Tên của chuỗi đó sẽ là <tên bảng> _ <tên cột> _seq
Jude Niroshan

Bạn không phải sử dụng ALTER COLUMN user_id?
Alec

Tôi đã thử phương pháp này nhưng tôi gặp lỗi: ERROR: syntax error at or near "DEFAULT"Có gợi ý nào không?
Ely Fialkoff

44

Mặc dù có vẻ như các chuỗi tương đương với auto_increment của MySQL, có một số khác biệt tinh tế nhưng quan trọng:

1. Truy vấn không thành công Tăng trình tự / nối tiếp

Cột nối tiếp được tăng lên trên các truy vấn không thành công. Điều này dẫn đến sự phân mảnh từ các truy vấn không thành công, không chỉ xóa hàng. Ví dụ: chạy các truy vấn sau trên cơ sở dữ liệu PostgreSQL của bạn:

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;

Bạn sẽ nhận được đầu ra sau:

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

Lưu ý cách uid đi từ 1 đến 3 thay vì 1 đến 2.

Điều này vẫn xảy ra nếu bạn tự tạo trình tự của mình bằng:

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;

Nếu bạn muốn kiểm tra MySQL khác nhau như thế nào, hãy chạy như sau trên cơ sở dữ liệu 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);

Bạn sẽ nhận được những điều sau đây không có phân đoạn :

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

2. Đặt thủ công giá trị cột nối tiếp có thể khiến các truy vấn trong tương lai không thành công.

Điều này đã được @trev chỉ ra trong câu trả lời trước đó.

Để mô phỏng điều này theo cách thủ công, đặt uid thành 4 sẽ "đụng độ" sau.

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

Bảng dữ liệu:

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

Chạy một chèn khác:

INSERT INTO table1 (col_b) VALUES(6);

Bảng dữ liệu:

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

Bây giờ nếu bạn chạy chèn khác:

INSERT INTO table1 (col_b) VALUES(7);

Nó sẽ thất bại với thông báo lỗi sau:

LRI: giá trị khóa trùng lặp vi phạm ràng buộc duy nhất "table1_pkey" CHI TIẾT: Khóa (uid) = (5) đã tồn tại.

Ngược lại, MySQL sẽ xử lý việc này một cách duyên dáng như hình dưới đây:

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

Bây giờ chèn một hàng khác mà không đặt uid

INSERT INTO table1 (col_b) VALUES(3);

Truy vấn không thất bại, uid chỉ nhảy đến 5:

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

Thử nghiệm đã được thực hiện trên MySQL 5.6.33, cho Linux (x86_64) và PostgreQuery 9.4.9


10
Bạn đang đưa ra một so sánh nhưng tôi không thấy bất kỳ giải pháp nào ở đây! Có phải là một câu trả lời?
Anwar

4
@Anwar nó chỉ đơn giản là mở rộng các câu trả lời khác nhau nói rằng câu trả lời là sử dụng một chuỗi / chuỗi. Điều này cung cấp một số bối cảnh quan trọng để xem xét.
trình viên

38

Bắt đầu với Postgres 10, các cột định danh theo định nghĩa của tiêu chuẩn SQL cũng được hỗ trợ:

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

tạo một cột danh tính không thể bị ghi đè trừ khi được yêu cầu rõ ràng. Chèn sau đây sẽ thất bại với một cột được xác định là generated always:

insert into foo (id) 
values (1);

Điều này tuy nhiên có thể được ghi đè:

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

Khi sử dụng tùy chọn, generated by defaultđây thực chất là hành vi tương tự như serialtriển khai hiện có:

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

Khi một giá trị được cung cấp thủ công, trình tự cơ bản cũng cần được điều chỉnh thủ công - giống như với một serialcột.


Một cột định danh không phải là khóa chính theo mặc định (giống như một serialcột). Nếu nó phải là một, một ràng buộc khóa chính cần được xác định bằng tay.


26

Xin lỗi, để làm lại một câu hỏi cũ, nhưng đây là câu hỏi / câu trả lời Stack Overflow đầu tiên xuất hiện trên Google.

Bài đăng này (xuất hiện đầu tiên trên Google) nói về việc sử dụng cú pháp cập nhật hơn cho PostgreQuery 10: https://blog.2ndquadrant.com/postgresql-10-identity-columns/

xảy ra là:

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

Mong rằng sẽ giúp :)


1
Đây thực sự là cách để đi trong PostgreQuery 10 và nó là cú pháp giống như các phần mềm cơ sở dữ liệu khác như DB2 hoặc Oracle.
adriaan

1
@adriaan Trên thực tế các GENERATED … AS IDENTITYlệnh là SQL chuẩn. Lần đầu tiên được thêm vào SQL: 2003 , sau đó được làm rõ trong SQL: 2008 . Xem các tính năng # T174 & F386 & T178.
Basil Bourque

16

Bạn phải cẩn thận không chèn trực tiếp vào trường SERIAL hoặc chuỗi, nếu không việc ghi của bạn sẽ thất bại khi chuỗi đạt đến giá trị được chèn:

-- 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

Trong ngữ cảnh của câu hỏi được hỏi và trả lời nhận xét của @ sereja1c, việc tạo SERIALngầm tạo ra các chuỗi, vì vậy, ví dụ trên-

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEsẽ ngầm tạo chuỗi foo_id_seqcho cột nối tiếp foo.id. Do đó, SERIAL[4 byte] rất tốt cho việc sử dụng trừ khi bạn cần một kiểu dữ liệu cụ thể cho id của mình.


3

Cách này sẽ hoạt động chắc chắn, tôi hy vọng nó sẽ giúp:

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');

Bạn có thể kiểm tra chi tiết này trong liên kết tiếp theo: http://www.postgresqltutorial.com/postgresql-serial/


3

Kể từ PostgreSQL 10

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    payload text
);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.