Menggunakan PostgreSQL v9.1. Saya memiliki tabel berikut:
CREATE TABLE foo
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
type VARCHAR(60) NOT NULL UNIQUE
);
CREATE TABLE bar
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
description VARCHAR(40) NOT NULL UNIQUE,
foo_id BIGINT NOT NULL REFERENCES foo ON DELETE RESTRICT
);
Katakanlah tabel pertama foo
diisi seperti ini:
INSERT INTO foo (type) VALUES
( 'red' ),
( 'green' ),
( 'blue' );
Apakah ada cara untuk menyisipkan baris bar
dengan mudah dengan mereferensikan foo
tabel? Atau haruskah saya melakukannya dalam dua langkah, pertama dengan mencari foo
tipe yang saya inginkan, dan kemudian memasukkan baris baru ke dalamnya bar
?
Berikut adalah contoh pseudo-code yang menunjukkan apa yang saya harapkan bisa dilakukan:
INSERT INTO bar (description, foo_id) VALUES
( 'testing', SELECT id from foo WHERE type='blue' ),
( 'another row', SELECT id from foo WHERE type='red' );
postgresql
foreign-key
postgresql-9.1
insert
Stéphane
sumber
sumber
MASUKKAN polos
Penggunaan
LEFT [OUTER] JOIN
bukan[INNER] JOIN
berarti bahwa baris darival
tidak dijatuhkan ketika tidak ada kecocokan ditemukan difoo
. Sebagai gantinya,NULL
dimasukkan untukfoo_id
.The
VALUES
ekspresi dalam subquery tidak sama dengan @ ypercube ini CTE. Common Table Expressions menawarkan fitur-fitur tambahan dan lebih mudah dibaca dalam permintaan besar, tetapi mereka juga berpose sebagai hambatan optimisasi. Jadi subqueries biasanya sedikit lebih cepat ketika tidak ada yang di atas diperlukan.id
sebagai nama kolom adalah anti-pola yang tersebar luas. Harusfoo_id
danbar_id
atau apa pun deskriptif. Saat bergabung dengan banyak tabel, Anda berakhir dengan beberapa kolom yang semuanya bernamaid
...Pertimbangkan yang polos
text
atauvarchar
bukanvarchar(n)
. Jika Anda benar-benar perlu menerapkan batasan panjang, tambahkanCHECK
batasan:Anda mungkin perlu menambahkan gips tipe eksplisit. Karena
VALUES
ekspresi tidak secara langsung dilampirkan ke tabel (seperti dalamINSERT ... VALUES ...
), tipe tidak dapat diturunkan dan tipe data default digunakan tanpa deklarasi tipe eksplisit, yang mungkin tidak berfungsi dalam semua kasus. Cukup dengan melakukannya di baris pertama, sisanya akan jatuh dalam barisan.MASUKKAN baris FK yang hilang pada saat yang sama
Jika Anda ingin membuat entri yang tidak ada
foo
dengan cepat, dalam satu pernyataan SQL , CTE berperan:Perhatikan dua baris boneka baru untuk dimasukkan. Keduanya berwarna ungu , yang belum ada
foo
. Dua baris untuk menggambarkan kebutuhanDISTINCT
dalamINSERT
pernyataan pertama .Penjelasan langkah demi langkah
CTE 1
sel
menyediakan beberapa baris input data. Subqueryval
denganVALUES
ekspresi dapat diganti dengan tabel atau subquery sebagai sumber. SegeraLEFT JOIN
untukfoo
menambahkan baris yangfoo_id
sudah ada sebelumnyatype
. Semua baris lainnya mendapatkanfoo_id IS NULL
cara ini.2 CTE
ins
menyisipkan berbeda jenis baru (foo_id IS NULL
) ke dalamfoo
, dan kembali yang baru dihasilkanfoo_id
- bersama-sama dengantype
untuk bergabung kembali untuk menyisipkan baris.Bagian luar terakhir
INSERT
sekarang dapat menyisipkan foo.id untuk setiap baris: baik tipe yang sudah ada sebelumnya, atau sudah dimasukkan pada langkah 2.Sebenarnya, kedua sisipan terjadi "secara paralel", tetapi karena ini adalah pernyataan tunggal ,
FOREIGN KEY
batasan default tidak akan mengeluh. Integritas referensial diberlakukan di akhir pernyataan secara default.SQL Fiddle untuk Postgres 9.3. (Bekerja sama di 9.1.)
Ada kondisi balapan kecil jika Anda menjalankan beberapa dari pertanyaan ini secara bersamaan. Baca lebih lanjut di bawah pertanyaan terkait di sini dan di sini dan di sini . Benar-benar hanya terjadi di bawah beban bersamaan yang berat, jika pernah. Dibandingkan dengan solusi caching seperti yang diiklankan dalam jawaban lain, peluangnya sangat kecil .
Fungsi untuk penggunaan berulang
Untuk penggunaan berulang saya akan membuat fungsi SQL yang mengambil array catatan sebagai parameter dan digunakan
unnest(param)
sebagai gantiVALUES
ekspresi.Atau, jika sintaks untuk array rekaman terlalu berantakan untuk Anda, gunakan string yang dipisahkan koma sebagai parameter
_param
. Misalnya bentuk:Kemudian gunakan ini untuk mengganti
VALUES
ekspresi dalam pernyataan di atas:Berfungsi dengan UPSERT di Postgres 9.5
Buat jenis baris khusus untuk melewati parameter. Kita bisa melakukannya tanpa itu, tetapi lebih sederhana:
Fungsi:
Panggilan:
Cepat dan solid untuk lingkungan dengan transaksi bersamaan.
Selain pertanyaan di atas, ini ...
... berlaku
SELECT
atauINSERT
aktiffoo
: Apa puntype
yang tidak ada dalam tabel FK, belum dimasukkan. Dengan asumsi sebagian besar tipe sudah ada. Untuk benar-benar yakin dan mengesampingkan kondisi balapan, baris yang kita butuhkan dikunci (sehingga transaksi bersamaan tidak dapat mengganggu). Jika itu terlalu paranoid untuk kasus Anda, Anda dapat mengganti:dengan
... berlaku
INSERT
atauUPDATE
(benar "UPSERT") padabar
: Jika yangdescription
sudah adatype
diperbarui:Tetapi hanya jika
type
benar-benar berubah:... melewati nilai juga tipe baris yang dikenal dengan
VARIADIC
parameter. Perhatikan maksimum default 100 parameter! Membandingkan:Ada banyak cara lain untuk melewati banyak baris ...
Terkait:
sumber
INSERT missing FK rows at the same time
contoh Anda , akan menempatkan ini dalam Transaksi mengurangi risiko kondisi balapan di SQL Server?SELECT
di dalamWITH
klausa). Sumber: Dokumentasi MS.INSERT ... RETURNING \gset
dipsql
kemudian menggunakan nilai-nilai dikembalikan sebagai psql:'variables'
, tapi ini hanya bekerja untuk menyisipkan baris tunggal.Lihatlah. Anda pada dasarnya memerlukan foo id untuk memasukkannya ke dalam bar.
Tidak spesifik postgres, btw. (dan Anda tidak menandainya seperti itu) - ini umumnya cara kerja SQL. Tidak ada jalan pintas di sini.
Dari sisi aplikasi, Anda mungkin memiliki cache item foo di memori. Tabel saya sering memiliki hingga 3 bidang unik:
Contoh:
Jelas ketika Anda ingin menautkan sesuatu ke akun - Anda pertama-tama harus, secara teknis, mendapatkan ID - tetapi mengingat baik Pengenal dan Kode tidak pernah berubah begitu mereka ada di sana, cache positif dalam memori tidak dapat menghentikan sebagian besar pencarian agar tidak memukul database.
sumber