Peningkatan Otomatis PostgreSQL

579

Saya beralih dari MySQL ke PostgreSQL dan bertanya-tanya bagaimana saya bisa melakukan nilai autoincrement. Saya melihat di PostgreSQL mendokumentasikan datatype "serial", tapi saya mendapatkan kesalahan sintaks saat menggunakannya (dalam v8.0).

Ian
sumber
9
jika Anda memberikan kueri dan kesalahan yang Anda dapatkan - mungkin seseorang dapat memberi tahu Anda apa yang salah dengan kueri.
2
Hit pertama saya juga Mich 'dan karena ini adalah pertanyaan yang mendapat cukup banyak pandangan agar relevan, mengapa tidak memilihnya. PS itu tidak sepele jika Anda tidak tahu bagaimana melakukannya.
baash05
1
SERIAL adalah pilihan yang lebih disukai jika driver klien Anda adalah Npgsql. Penyedia secara internal memilih nilai baru setelah INSERT menggunakan SELECT currval (pg_get_serial_ berikutnyaence ('table', 'kolom')). Ini akan gagal jika kolom yang mendasarinya bukan dari tipe serial (tipe numerik + urutan eksplisit misalnya)
Olivier MATROT
Hanya untuk rasa ingin tahu ... Mengapa seseorang harus bermigrasi dari MySQL yang sangat bagus, ke PostgreSql?
villamejia
17
... yang bahkan lebih baik.
Rohmer

Jawaban:

702

Ya, SERIAL adalah fungsi yang setara.

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 hanyalah makro waktu pembuatan tabel di sekitar sekuens. Anda tidak dapat mengubah SERIAL ke kolom yang ada.

Angka tiga
sumber
19
mengutip nama tabel adalah praktik yang sangat buruk
Evan Carroll
71
Mengutip nama tabel adalah kebiasaan karena saya mewarisi DB yang memiliki nama kasus campuran dan mengutip nama tabel adalah persyaratan penggunaan.
Trey
26
@ Evan Carroll - Mengapa itu kebiasaan buruk (hanya bertanya)?
Christian
27
karena kecuali Anda memiliki meja "Table"dan "table"kemudian hanya meninggalkannya kuotasi dan canonicalize ke table. Konvensi ini tidak pernah menggunakan kutipan dalam hal. Anda dapat, jika ingin, menggunakan nama case campuran untuk penampilan, hanya saja tidak memerlukannya: CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;akan berfungsi, seperti yang akan terjadi SELECT * FROM foobar.
Evan Carroll
26
Per postgres doc, baik kutipan atau tanda kutip secara konsisten: postgresql.org/docs/current/interactive/…
Καrτhικ
225

Anda dapat menggunakan tipe data integer lainnya , seperti smallint.

Contoh:

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;

Lebih baik menggunakan tipe data Anda sendiri, daripada tipe data serial pengguna .

Ahmad
sumber
11
Saya akan mengatakan ini sebenarnya jawaban yang lebih baik karena memungkinkan saya untuk mengubah tabel yang baru saja saya buat di PostgreSQL dengan mengatur kolom default (setelah membaca CREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.html ) . NAMUN, saya tidak yakin mengapa Anda mengubah pemiliknya.
JayC
12
@ JayC: Dari dokumentasi : Terakhir, urutan ditandai sebagai "dimiliki oleh" kolom, sehingga akan dijatuhkan jika kolom atau tabel dijatuhkan.
user272735
9
mengapa komunitas postgres tidak menemukan kembali kata kunci peningkatan otomatis?
Dr Deo
2
@Dr Deo: mereka menggunakan kata kunci serial bukannya autoincrement, saya tidak tahu mengapa :)
Ahmad
4
Ada juga server yang lebih kecil jika Anda hanya menginginkan tipe data yang lebih kecil.
beldaz
110

Jika Anda ingin menambahkan urutan ke id di tabel yang sudah ada, Anda dapat menggunakan:

CREATE SEQUENCE user_id_seq;
ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');
sereja
sumber
Apa itu urutan? Di mana AUTO_INCREMENT?
Hijau
23
@Green: AUTO_INCREMENT bukan bagian dari standar SQL, ini khusus untuk MySQL. Urutan adalah sesuatu yang melakukan pekerjaan serupa di PostgreSQL.
beldaz
5
jika Anda menggunakan 'id SERIAL', itu akan secara otomatis membuat urutan di PostgreSQL. Nama urutan itu adalah <nama tabel> _ <nama kolom> _seq
Jude Niroshan
Apakah kamu tidak harus menggunakan ALTER COLUMN user_id?
Alec
Saya mencoba metode ini tetapi saya mendapatkan kesalahan: ERROR: syntax error at or near "DEFAULT"Ada saran?
Ely Fialkoff
44

Walaupun sepertinya urutannya setara dengan MySQL auto_increment, ada beberapa perbedaan yang halus tetapi penting:

1. Permintaan Gagal Peningkatan Urutan / Serial

Kolom serial akan bertambah pada kueri yang gagal. Ini mengarah ke fragmentasi dari kueri yang gagal, bukan hanya penghapusan baris. Sebagai contoh, jalankan query berikut pada database PostgreSQL Anda:

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;

Anda harus mendapatkan output berikut:

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

Perhatikan bagaimana Anda beralih dari 1 ke 3 bukannya 1 ke 2.

Ini masih terjadi jika Anda secara manual membuat urutan Anda sendiri dengan:

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;

Jika Anda ingin menguji perbedaan MySQL, jalankan yang berikut di database 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);

Anda harus mendapatkan yang berikut tanpa frmentasi :

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

2. Mengatur Nilai Kolom Serial Secara Manual Dapat Menyebabkan Gagal Permintaan Masa Depan.

Ini ditunjukkan oleh @trev dalam jawaban sebelumnya.

Untuk mensimulasikan ini secara manual atur uid ke 4 yang akan "clash" nanti.

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

Data tabel:

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

Jalankan sisipan lain:

INSERT INTO table1 (col_b) VALUES(6);

Data tabel:

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

Sekarang jika Anda menjalankan sisipan lain:

INSERT INTO table1 (col_b) VALUES(7);

Ini akan gagal dengan pesan kesalahan berikut:

GALAT: nilai kunci duplikat melanggar batasan unik "table1_pkey" DETAIL: Kunci (uid) = (5) sudah ada.

Sebaliknya, MySQL akan menangani ini dengan anggun seperti yang ditunjukkan di bawah ini:

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

Sekarang masukkan baris lain tanpa pengaturan uid

INSERT INTO table1 (col_b) VALUES(3);

Permintaan tidak gagal, Anda hanya perlu melompat ke 5:

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

Pengujian dilakukan pada MySQL 5.6.33, untuk Linux (x86_64) dan PostgreSQL 9.4.9

Programster
sumber
10
Anda memberikan perbandingan tetapi saya tidak melihat solusi apa pun di sini! Apakah ini sebuah jawaban?
Anwar
4
@ Anwar itu hanya memperluas berbagai jawaban yang menyatakan bahwa jawabannya adalah menggunakan serial / urutan. Ini memberikan beberapa konteks penting untuk dipertimbangkan.
Programster
38

Dimulai dengan Postgres 10, kolom identitas seperti yang didefinisikan oleh standar SQL juga didukung:

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

membuat kolom identitas yang tidak bisa diganti kecuali diminta secara eksplisit. Sisipan berikut akan gagal dengan kolom yang didefinisikan sebagai generated always:

insert into foo (id) 
values (1);

Namun ini dapat ditolak:

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

Saat menggunakan opsi generated by defaultini pada dasarnya perilaku yang sama dengan serialimplementasi yang ada :

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

Ketika nilai diberikan secara manual, urutan yang mendasarinya perlu disesuaikan secara manual juga - sama seperti dengan serialkolom.


Kolom identitas bukan kunci utama secara default (seperti serialkolom). Jika harus satu, batasan kunci primer perlu didefinisikan secara manual.

seekor kuda tanpa nama
sumber
26

Maaf, untuk mengulangi pertanyaan lama, tapi ini adalah pertanyaan / jawaban Stack Overflow pertama yang muncul di Google.

Posting ini (yang muncul pertama kali di Google) berbicara tentang penggunaan sintaks yang lebih diperbarui untuk PostgreSQL 10: https://blog.2ndquadrant.com/postgresql-10-identity-columns/

yang kebetulan adalah:

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

Semoga itu bisa membantu :)

Zhao Li
sumber
1
Ini memang cara untuk masuk dalam PostgreSQL 10 dan itu adalah sintaksis yang sama dengan perangkat lunak database lain seperti DB2 atau Oracle.
adriaan
1
@adriaan Sebenarnya GENERATED … AS IDENTITYperintahnya adalah SQL standar. Pertama ditambahkan dalam SQL: 2003 , kemudian diklarifikasi dalam SQL: 2008 . Lihat fitur # T174 & F386 & T178.
Basil Bourque
16

Anda harus berhati-hati untuk tidak memasukkan langsung ke bidang SERIAL atau urutan Anda, jika tidak, penulisan Anda akan gagal ketika urutan mencapai nilai yang dimasukkan:

-- 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; 
trev
sumber
15

Dalam konteks pertanyaan yang diajukan dan sebagai balasan atas komentar oleh @ sereja1c, membuat SERIALsecara implisit menciptakan urutan, jadi untuk contoh di atas-

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEsecara implisit akan membuat urutan foo_id_sequntuk kolom serial foo.id. Karenanya, SERIAL[4 Bytes] bagus untuk kemudahan penggunaan kecuali Anda membutuhkan tipe data khusus untuk id Anda.

Pangeran
sumber
3

Cara ini pasti akan berhasil, saya harap ini membantu:

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

Anda dapat memeriksa detail ini di tautan berikutnya: http://www.postgresqltutorial.com/postgresql-serial/

webtechnelson
sumber
3

Sejak PostgreSQL 10

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    payload text
);
Sergey Vishnevetskiy
sumber