Cara Melihat Apa yang Baru Saja Terjadi
Saat kamu menjalankan INSERT, UPDATE, atau DELETE, SQLite memberitahumu berapa banyak baris yang tersentuh, tapi tidak memberitahu baris yang mana, atau seperti apa nilai akhirnya. Solusi klasiknya adalah SELECT lanjutan. Itu berarti dua kali perjalanan ke database, dua pernyataan, dan celah waktu kecil di mana orang lain bisa mengubah baris itu di antara keduanya.
RETURNING mengatasinya. Kamu menambahkannya ke pernyataan tulis, menyebutkan kolom yang ingin dikembalikan, dan SQLite memberimu baris yang terpengaruh seolah-olah kamu baru saja menjalankan SELECT atas baris-baris itu:
Satu pernyataan, satu perjalanan, dan kamu mendapatkan kembali id yang dihasilkan serta nilai default created_at yang diisi database untukmu.
RETURNING ditambahkan di SQLite 3.35.0 (Maret 2021). Jika pernyataanmu ditolak sebagai error sintaks, cek SELECT sqlite_version();, karena build yang lebih lama tidak mengenal kata kunci ini.
Mendapatkan Kembali ID yang Dihasilkan
Alasan paling umum memakai RETURNING adalah untuk mengambil primary key yang dibuat otomatis tepat setelah insert:
Sebelum ada RETURNING, kamu melakukan insert lalu memanggil last_insert_rowid() (atau padanannya di driver-mu) pada koneksi yang sama. Cara itu masih berfungsi, tapi bergantung pada keadaan koneksi yang tersembunyi, sehingga mudah keliru jika memakai connection pool atau thread. RETURNING id itu eksplisit, hanya berlaku untuk pernyataan itu, dan bekerja dengan cara yang sama apa pun yang menjalankan koneksinya.
Jika tabelmu tidak mendeklarasikan INTEGER PRIMARY KEY secara eksplisit, kamu tetap bisa mendapatkan pengenal baris implisitnya:
Setiap tabel SQLite biasa punya rowid, dan RETURNING akan memberikannya.
Beberapa Kolom dan Ekspresi
RETURNING menerima bentuk yang sama dengan daftar kolom pada SELECT. Tuliskan kolom, pakai *, buat ekspresi, beri alias:
RETURNING * praktis jika kamu ingin semuanya, termasuk nilai default yang diisi database, tanpa menyebut setiap kolom:
Kamu melihat id yang baru, name yang kamu berikan, dan timestamp yang dihitung SQLite.
RETURNING dengan UPDATE
Pada UPDATE, RETURNING memberimu nilai setelah update, yaitu baris sebagaimana terlihat setelah perubahanmu diterapkan:
Kamu mendapatkan saldo baru Ada sebesar 125, bukan yang lama 100. Itu membuat RETURNING sangat cocok untuk penghitung atomik dan operasi kredit/debit: kamu tidak perlu membaca, menghitung, menulis, lalu membaca ulang.
Jika WHERE cocok dengan banyak baris, kamu mendapat satu baris kembali untuk setiap baris yang terpengaruh:
Tiga baris masuk, tiga baris keluar. Urutannya tidak dijamin. Jika kamu butuh urutan tertentu, urutkan hasilnya di sisi client.
RETURNING dengan DELETE
Pada DELETE, RETURNING memberimu baris sebagaimana adanya tepat sebelum dihapus. Berguna untuk pengarsipan, jejak audit, atau sekadar memastikan apa yang terhapus:
Kamu mendapatkan kembali dua sesi yang sudah kedaluwarsa, lengkap dengan semua field-nya, meskipun keduanya sudah tidak ada di tabel. Jika kamu ingin memindahkannya ke tempat lain, ini persiapan yang pas untuk tabel arsip: baca hasilnya lalu masukkan ke tempat lain dalam transaksi yang sama.
RETURNING dengan UPSERT
RETURNING juga bekerja dengan INSERT ... ON CONFLICT ... DO UPDATE. Baris yang dikembalikan mencerminkan cabang mana pun yang dijalankan, entah insert baru atau update karena konflik:
Jalankan pernyataan itu dua kali. Pertama kali, pernyataan itu melakukan insert dan mengembalikan ('visits', 1). Kedua kalinya, konfliknya terjadi, nilainya dinaikkan, dan kamu mendapatkan kembali ('visits', 2). Bagaimanapun, satu pernyataan dan satu baris keluar, tanpa perlu bertanya "tadi insert atau update?" sebelum melanjutkan.
Inilah pola paling rapi di SQLite untuk "berikan nilai saat ini, dan buat jika belum ada" tanpa bolak-balik ke database.
Beberapa Hal yang Perlu Diketahui
Beberapa detail yang sering mengejutkan orang:
RETURNINGselalu melihat baris setelah perubahan untukINSERTdanUPDATE, dan sebelum perubahan untukDELETE. Tidak ada sintaks untuk meminta sisi yang lain.- Urutan baris yang dikembalikan tidak dijamin. Tambahkan
ORDER BYdi sisi client jika urutannya penting. - Kamu tidak bisa menaruh
RETURNINGdi dalam subquery. Klausa ini adalah klausa tingkat atas pada pernyataan tulis, bukan sebuah ekspresi. RETURNINGtidak mengembalikan data hasil modifikasi triggerBEFORE, melainkan nilai yang benar-benar ditulis. TriggerAFTERberjalan di antara penulisan dan pengembalian barisnya.- Generated column dan nilai
DEFAULTterlihat di hasilnya. Justru karena itulahRETURNING *menjadi cara cepat untuk memeriksa apa yang diisi database untukmu.
Selanjutnya: Mengimpor Data CSV
RETURNING sangat berguna saat kamu menulis satu atau beberapa baris sekaligus dan ingin langsung melihat hasilnya. Saat kamu memuat ribuan baris dari sebuah file, kamu akan memakai tool impor CSV milik SQLite. Itulah topik halaman berikutnya.
Pertanyaan yang sering diajukan
Apakah SQLite mendukung klausa RETURNING?
Ya, sejak versi 3.35.0 (dirilis Maret 2021). Kamu bisa menambahkan RETURNING ke pernyataan INSERT, UPDATE, dan DELETE untuk mendapatkan kembali baris yang terpengaruh. Jika SQLite-mu versi lama, parser akan menolaknya; cek dengan SELECT sqlite_version();.
Bagaimana cara mendapatkan ID baris yang baru saja dimasukkan di SQLite?
Gunakan INSERT ... RETURNING id (atau RETURNING rowid jika tabelnya tidak punya primary key eksplisit). Nilai yang dihasilkan dikembalikan sebagai bagian dari pernyataan yang sama, jadi kamu tidak perlu query kedua seperti last_insert_rowid().
Bisakah RETURNING mengembalikan lebih dari satu kolom?
Bisa. Tuliskan kolom yang kamu inginkan, dipisahkan koma, seperti di SELECT: RETURNING id, name, created_at. Kamu juga bisa memakai RETURNING * untuk mendapatkan semua kolom, atau menulis ekspresi seperti RETURNING id, price * quantity AS total.
Apakah RETURNING bekerja dengan UPSERT dan ON CONFLICT?
Ya. INSERT ... ON CONFLICT ... DO UPDATE ... RETURNING ... mengembalikan baris itu, baik baris yang baru dimasukkan maupun yang diperbarui oleh penyelesaian konflik. Itulah cara paling rapi untuk melakukan upsert dan membaca keadaan akhirnya dalam satu kali perjalanan ke database.