Menu

ROWID SQLite: Key Tersembunyi dan WITHOUT ROWID

Apa sebenarnya ROWID di SQLite, kapan INTEGER PRIMARY KEY menjadikannya kolommu sendiri, dan kenapa tabel WITHOUT ROWID ada.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Setiap Tabel Punya Kolom Rahasia

Buat tabel SQLite biasa, dan kamu sudah punya satu kolom yang tidak kamu deklarasikan:

Kolom rowid itu nyata. SQLite memberikannya ke setiap baris di setiap tabel biasa, entah kamu memintanya atau tidak. Kolom ini berupa integer 64-bit bertanda, unik di dalam tabel, dan merupakan key sebenarnya yang dipakai SQLite untuk menemukan baris di penyimpanan B-tree-nya. Anggap saja sebagai tulang punggung tabel: index yang menjaga semua hal lain tetap teratur.

Biasanya kamu tidak melihatnya karena SELECT * tidak menyertakannya. Kamu harus memintanya dengan menyebut namanya.

ROWID Punya Tiga Alias

Karena rowid sering muncul di SQL yang ditulis untuk database lain, SQLite menerima tiga nama untuk kolom yang sama:

rowid, oid, dan _rowid_ semuanya merujuk ke kolom tersembunyi yang sama. Jika kamu sudah mendeklarasikan kolom sungguhan dengan salah satu nama itu, kolommu yang menang dan aliasnya tidak bisa dipakai, tapi hanya itu jebakannya. Di kode sehari-hari, cukup tulis rowid.

INTEGER PRIMARY KEY Adalah Frasa Ajaibnya

Inilah bagian yang menjebak semua orang yang datang dari database lain. Jika kamu mendeklarasikan sebuah kolom persis sebagai INTEGER PRIMARY KEY, kolom itu tidak disimpan secara terpisah, melainkan menjadi rowid:

rowid dan id adalah kolom yang sama dengan dua nama. Insert yang tidak menyertakan id mendapat integer yang dipilih otomatis (biasanya rowid terbesar + 1). Itulah sebabnya INTEGER PRIMARY KEY adalah cara paling efisien untuk memberi tabel key yang bertambah otomatis di SQLite: tidak ada kolom tambahan, tidak ada index tambahan, hanya rowid itu sendiri.

Ejaannya harus persis. INT PRIMARY KEY tidak sama, karena INT dan INTEGER punya efek yang berbeda di sini:

Di tabel a, id dan rowid cocok. Di tabel b, id adalah kolom biasa dan rowid adalah integer tersembunyi yang terpisah. Lebih parah lagi, b.id tidak terisi otomatis saat insert: nilainya NULL sampai kamu mengaturnya. Tetap pakai INTEGER PRIMARY KEY (kata lengkapnya) jika kamu ingin perilaku alias itu.

Mendapatkan ROWID dari Sebuah Insert

Setelah INSERT, kamu sering ingin tahu rowid yang baru saja diberikan, biasanya untuk menghubungkan baris anak ke baris itu. SQLite memberimu last_insert_rowid():

Fungsi ini mengembalikan rowid dari insert terakhir yang berhasil pada koneksi saat ini. Kebanyakan driver database menyediakan nilai yang sama sebagai cursor.lastrowid atau yang serupa. Klausa RETURNING (dibahas nanti) adalah cara lain untuk mendapatkannya kembali sebagai bagian dari insert itu sendiri.

ROWID Tidak Permanen

Rowid sebuah baris stabil selama baris itu ada, tapi bukan pengenal seumur hidup. VACUUM bisa menomori ulang rowid, dan jika kamu menghapus sebuah baris, nomornya bisa dipakai ulang oleh insert berikutnya:

Perhatikan bahwa baris baru itu mungkin memakai ulang rowid lama atau tidak, tergantung versi dan keadaannya. Intinya, kamu tidak bisa mengandalkannya tetap unik selamanya. Jika kamu butuh pengenal yang bertahan melewati penghapusan, vacuum, dan ekspor, deklarasikan kolom INTEGER PRIMARY KEY milikmu sendiri (yang mengunci nilainya ke baris itu), dan pertimbangkan kata kunci AUTOINCREMENT jika kamu memang butuh nilai yang selalu naik dan tidak pernah dipakai ulang.

Tabel WITHOUT ROWID

Terkadang rowid adalah beban tambahan yang tidak kamu inginkan, paling sering saat key sebenarnya bukan integer. Tabel kota dengan nama sebagai key, misalnya, akhirnya punya dua struktur: B-tree rowid, dan index terpisah pada name untuk menegakkan primary key. WITHOUT ROWID menggabungkan keduanya menjadi satu:

Sekarang name adalah key penyimpanan yang sebenarnya. Pencarian berdasarkan name melewati satu lapisan perantara, dan tabelnya lebih kecil. Konsekuensinya:

  • Tidak ada rowid, oid, atau _rowid_; kolom-kolom itu memang tidak ada.
  • last_insert_rowid() tidak diperbarui untuk insert pada tabel ini.
  • Incremental BLOB I/O dan beberapa fitur replikasi tidak tersedia.
  • Tabel itu wajib punya PRIMARY KEY yang dideklarasikan.

WITHOUT ROWID adalah optimasi yang terukur, bukan default. Pakai saat primary key-nya bukan integer dan tabelnya besar atau sering ditulis. Untuk tabel biasa dengan key integer, tata letak rowid yang biasa sudah optimal.

Model Berpikirnya

Jika diringkas ke intinya:

  • Setiap tabel SQLite biasa punya key integer 64-bit tersembunyi bernama rowid.
  • INTEGER PRIMARY KEY (dengan ejaan persis) menjadikan kolommu alias untuknya.
  • Pakai last_insert_rowid() untuk membaca nilai yang baru saja diberikan.
  • Rowid bisa dipakai ulang setelah penghapusan dan dinomori ulang oleh VACUUM.
  • Tabel WITHOUT ROWID membuang key tersembunyi itu dan langsung memakai primary key yang kamu deklarasikan. Berguna untuk key non-integer, tapi beberapa fitur jadi hilang.

Sering kali kamu sama sekali tidak memikirkan rowid. Kamu mendeklarasikan id INTEGER PRIMARY KEY, membiarkan SQLite mengurus penomorannya, lalu lanjut. Mekanismenya baru penting saat kamu menyetel penyimpanan, membaca skema yang sudah ada, atau bertanya-tanya kenapa INT PRIMARY KEY berperilaku berbeda dari INTEGER PRIMARY KEY.

Selanjutnya: NOT NULL dan DEFAULT

Sekarang identitas baris sudah beres, lapisan berikutnya adalah memastikan kolom-kolom lainnya berisi nilai yang masuk akal. NOT NULL dan DEFAULT adalah dua klausa yang mengerjakan sebagian besar tugas itu, dan keduanya dibahas berikutnya.

Pertanyaan yang sering diajukan

Apa itu ROWID di SQLite?

Setiap tabel SQLite biasa punya kolom integer 64-bit bertanda yang tersembunyi bernama rowid, yang mengidentifikasi setiap baris secara unik. SQLite memakainya secara internal sebagai key sebenarnya di penyimpanan B-tree. Kamu bisa membacanya dengan SELECT rowid, * FROM t meskipun kamu tidak pernah mendeklarasikannya.

Apa perbedaan ROWID dan PRIMARY KEY di SQLite?

rowid selalu ada; primary key adalah sesuatu yang kamu deklarasikan. Kasus khususnya adalah INTEGER PRIMARY KEY: kolom itu menjadi alias untuk rowid, bukan kolom terpisah. Primary key lain apa pun (teks, komposit, atau INT PRIMARY KEY tanpa kata INTEGER lengkap) disimpan di samping rowid, bukan sebagai rowid itu sendiri.

Apa fungsi WITHOUT ROWID di SQLite?

WITHOUT ROWID menyuruh SQLite melewati rowid tersembunyi dan memakai PRIMARY KEY yang kamu deklarasikan sebagai key penyimpanan yang sebenarnya. Ini bisa menghemat ruang dan mempercepat pencarian untuk tabel dengan key non-integer, tapi menonaktifkan beberapa fitur seperti last_insert_rowid() dan incremental BLOB I/O. Pakai dengan sengaja, bukan sebagai default.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai