Pengantar
PostgreSQL secara native memasok beragam tipe data yang mendukung banyak kasus penggunaan praktis. Artikel ini memperkenalkan implementasi khusus tipe data serial yang biasanya digunakan untuk pembuatan kunci primer sintetis.
Tombol Unik
Prinsip dasar teori desain database adalah bahwa setiap tupel (yaitu, baris) dari suatu relasi (yaitu, tabel) harus diidentifikasi secara unik dari tupel lainnya. Atribut, atau kolom, yang bersama-sama secara jelas mengidentifikasi satu tupel dari yang lainnya disebut "kunci". Beberapa puritan berpendapat bahwa setiap objek atau konsep yang dimodelkan secara inheren memiliki atribut atau kumpulan atribut yang dapat berfungsi sebagai kunci dan bahwa penting untuk mengidentifikasi kumpulan atribut kunci ini dan menggunakannya untuk pemilihan tupel yang unik.
Tetapi sebagai masalah praktis, mengidentifikasi satu set atribut yang cukup besar yang menjamin keunikan untuk objek yang dimodelkan mungkin tidak praktis, dan untuk implementasi dunia nyata, pengembang sering beralih ke kunci sintetis sebagai pengganti. Artinya, daripada mengandalkan beberapa kombinasi atribut aktual, nilai internal ke database, biasanya nilai integer bertambah, dan sebaliknya tidak memiliki arti fisik didefinisikan sebagai kunci. Selain kesederhanaan kunci kolom tunggal, fakta bahwa tidak ada ketergantungan dunia nyata berarti bahwa faktor eksternal tidak pernah dapat memaksa kebutuhan untuk mengubah nilai, seperti misalnya, mungkin terjadi jika nama seseorang digunakan sebagai kunci ... dan kemudian orang tersebut menikah atau memasuki program perlindungan saksi pemerintah federal dan mengubah nama mereka. Bahkan beberapa nilai yang umumnya dianggap oleh orang awam sebagai unik dan tidak dapat diubah, seperti nomor jaminan sosial A.S., tidak keduanya:seseorang dapat memperoleh SSN baru, dan SSN terkadang digunakan kembali.
Mendeklarasikan Tipe Data Serial
PostgreSQL menyediakan deklarasi tipe data khusus untuk memenuhi kebutuhan akan kunci sintetis ini. Mendeklarasikan kolom tabel database sebagai tipe SERIAL memenuhi persyaratan untuk kunci sintetis dengan menyediakan bilangan bulat unik pada penyisipan tupel baru. Tipe data semu ini mengimplementasikan kolom tipe data integer dengan nilai default terkait yang diturunkan melalui pemanggilan fungsi yang memasok nilai integer yang bertambah. Jalankan kode berikut untuk membuat tabel sederhana dengan kolom id tipe serial:
CREATE TABLE person (id serial, full_name text);
actually executes the following DDL
CREATE TABLE person (
id integer NOT NULL,
full_name text
);
CREATE SEQUENCE person_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE person_id_seq OWNED BY person.id;
ALTER TABLE ONLY person
ALTER COLUMN id
SET DEFAULT nextval('person_id_seq'::regclass);
Artinya, kata kunci "serial" sebagai spesifikasi tipe data menyiratkan eksekusi pernyataan DDL yang membuat kolom tipe integer dengan batasan NOT NULL, SEQUENCE, dan kemudian default kolom DIUBAH untuk memanggil fungsi bawaan yang mengakses SEQUENCE itu.
Fungsi bawaan nextval melakukan layanan peningkatan otomatis:setiap kali nextval dipanggil, itu menambah penghitung urutan yang ditentukan dan mengembalikan nilai yang baru ditambahkan.
Anda dapat melihat hasil dari efek ini dengan memeriksa definisi tabel:
postgres=# \d person
Table "public.person"
Column | Type | Modifiers
-----------+---------+-----------------------------------------
Id | integer | not null default nextval('person_id_seq'::regclass)
full_name | text |
Memasukkan Nilai Serial
Untuk memanfaatkan fungsi peningkatan otomatis, kita cukup menyisipkan baris, mengandalkan nilai default untuk kolom serial:
INSERT INTO person (full_name) VALUES ('Alice');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
(1 row)
Kami melihat bahwa nilai untuk kolom id yang sesuai dengan baris "Alice" baru telah dibuat secara otomatis. Atau, seseorang dapat menggunakan kata kunci DEFAULT jika secara eksplisit mencantumkan semua nama kolom yang diinginkan:
INSERT INTO person (id, full_name) VALUES (DEFAULT, 'Bob');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
(2 rows)
di mana kita melihat fungsionalitas peningkatan otomatis dengan lebih jelas, menetapkan nilai serial-berikutnya ke baris baru untuk penyisipan kedua "Bob".
Memasukkan beberapa baris bahkan berfungsi:
INSERT INTO person (full_name) VALUES ('Cathy'), ('David');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
3 | Cathy
4 | David
(4 rows)
Unduh Whitepaper Hari Ini Pengelolaan &Otomatisasi PostgreSQL dengan ClusterControlPelajari tentang apa yang perlu Anda ketahui untuk menerapkan, memantau, mengelola, dan menskalakan PostgreSQLUnduh Whitepaper Nilai Serial Tidak Ada
Fungsi nextval built-in dioptimalkan untuk non-blocking, aplikasi konkurensi tinggi dan tidak menghormati rollback. Akibatnya, ini berarti mungkin ada nilai yang hilang dalam urutan. Di bawah, kami mengembalikan sisipan, tetapi kemudian melihat sisipan berikutnya mendapatkan nilai baru yang melompati nilai yang akan dikaitkan dengan transaksi yang dibatalkan:
BEGIN TRANSACTION;
INSERT INTO person (full_name) VALUES ('Eve');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
3 | Cathy
4 | David
5 | Eve
(5 rows)
ROLLBACK;
INSERT INTO person (full_name) VALUES ('Fred');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
3 | Cathy
4 | David
6 | Fred
(5 rows)
Keuntungan dari tidak menghormati rollback adalah sesi lain yang mencoba penyisipan bersamaan tidak diblokir oleh sesi penyisipan lainnya.
Cara lain untuk mendapatkan nilai yang hilang adalah jika baris dihapus:
DELETE FROM person WHERE full_name = 'Cathy';
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
6 | Fred
(4 rows)
Perhatikan bahwa bahkan setelah menghapus baris yang paling baru dimasukkan yang sesuai dengan nilai kolom id kenaikan otomatis terbesar, penghitung urutan tidak kembali, yaitu, meskipun setelah menghapus baris yang sesuai dengan 'Fred', untuk penyisipan berikutnya penghitung urutan masih mempertahankan nilai terbesar yang diketahui sebelumnya dan kenaikan dari sana:
DELETE FROM person WHERE full_name = 'Fred';
INSERT INTO person (full_name) VALUES ('Gina');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
7 | Gina
(4 rows)
Kesenjangan atau nilai yang hilang seperti yang ditunjukkan di atas dilaporkan dipandang sebagai masalah oleh beberapa pengembang aplikasi karena di milis Umum PostgreSQL, ada pengulangan pertanyaan yang perlahan tapi pasti tentang bagaimana menghindari kesenjangan urutan saat menggunakan tipe pseudo-data serial. Kadang-kadang tidak ada persyaratan bisnis yang mendasari sebenarnya, itu hanya masalah keengganan pribadi untuk nilai-nilai yang hilang. Tetapi ada keadaan ketika mencegah nomor yang hilang adalah kebutuhan nyata, dan itu adalah subjek untuk artikel berikutnya.
TIDAK, ANDA TIDAK BISA - YA ANDA BISA!
Batasan NOT NULL yang diperhitungkan oleh pseudo-datatype serial melindungi terhadap penyisipan NULL untuk kolom id dengan menolak upaya penyisipan seperti itu:
INSERT INTO person (id, full_name) VALUES (NULL, 'Henry');
ERROR: null value in column "id" violates not-null constraint
DETAIL: Failing row contains (null, Henry).
Dengan demikian, kami yakin memiliki nilai untuk atribut tersebut.
Namun, masalah yang dihadapi beberapa orang adalah, seperti yang dinyatakan di atas, tidak ada yang mencegah penyisipan nilai secara eksplisit, melewati nilai peningkatan otomatis default yang diperoleh melalui pemanggilan fungsi nextval:
INSERT INTO person (id, full_name) VALUES (9, 'Ingrid');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
7 | Gina
9 | Ingrid
(5 rows)
Tapi kemudian dua sisipan kemudian menggunakan default menghasilkan nilai duplikat untuk kolom id jika tidak ada pemeriksaan kendala nilai kolom terhadap nilai urutan:
INSERT INTO person (full_name) VALUES ('James');
INSERT INTO person (full_name) VALUES ('Karen');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
7 | Gina
9 | Ingrid
8 | James
9 | Karen
(7 rows)
Jika kita menggunakan kolom serial id sebagai key, kita akan mendeklarasikannya sebagai PRIMARY KEY atau setidaknya membuat UNIQUE INDEX. Jika kita melakukannya, maka penyisipan 'Karen' di atas akan gagal dengan kesalahan kunci duplikat. Rilis PostgreSQL terbaru menyertakan sintaks deklarasi batasan baru 'dihasilkan secara default sebagai identitas' yang menghindari perangkap ini dan beberapa masalah warisan lainnya yang terkait dengan tipe data semu serial.
Fungsi Manipulasi Urutan
Selain fungsi nextval yang telah kami sebutkan yang memajukan urutan dan mengembalikan nilai baru, ada beberapa fungsi lain untuk menanyakan dan mengatur nilai urutan:fungsi currval mengembalikan nilai yang paling baru diperoleh dengan nextval untuk urutan yang ditentukan, fungsi lastval mengembalikan nilai yang paling baru diperoleh dengan nextval untuk urutan apa pun, dan fungsi setval menetapkan nilai urutan saat ini. Fungsi-fungsi ini dipanggil dengan kueri sederhana., misalnya
SELECT currval('person_id_seq');
currval
---------
9
(1 row)
Dan perhatikan bahwa jika panggilan dibuat ke fungsi nextval secara independen dari benar-benar melakukan penyisipan, itu akan menambah urutan, dan itu akan tercermin dalam penyisipan berikutnya:
SELECT nextval('person_id_seq');
nextval
---------
10
(1 row)
INSERT INTO person (full_name) VALUES ('Larry');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
7 | Gina
9 | Ingrid
8 | James
9 | Karen
11 | Larry
(8 rows)
Kesimpulan
Kami telah memperkenalkan pemahaman dasar tentang pseudo-datatype PostgreSQL SERIAL untuk kunci sintetis yang ditambahkan secara otomatis. Sebagai ilustrasi dalam artikel ini, kami menggunakan deklarasi tipe SERIAL, yang membuat kolom bilangan bulat 4-byte. PostgreSQL mengakomodasi kebutuhan rentang yang berbeda dengan tipe data semu SMALLSERIAL dan BIGSERIAL untuk, masing-masing, ukuran kolom 2-byte dan 8-byte. Cari artikel mendatang tentang satu cara untuk mengatasi kebutuhan akan urutan tanpa nilai yang hilang.