Dengan VALUES
yang berdiri sendiri ekspresi PostgreSQL tidak tahu apa tipe data yang seharusnya. Dengan literal numerik sederhana, sistem dengan senang hati mengasumsikan tipe yang cocok. Tetapi dengan input lain (seperti NULL
) Anda perlu mentransmisikan secara eksplisit - seperti yang telah Anda ketahui.
Anda dapat menanyakan pg_catalog
(cepat, tetapi khusus PostgreSQL) atau information_schema
(lambat, tetapi SQL standar) untuk mengetahui dan menyiapkan pernyataan Anda dengan tipe yang sesuai.
Atau Anda dapat menggunakan salah satu "trik" sederhana ini (saya menyimpan yang terbaik untuk terakhir ):
0. Pilih baris dengan LIMIT 0
, tambahkan baris dengan UNION ALL VALUES
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
(SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
UNION ALL
VALUES
(1, 20, NULL) -- no type casts here
, (2, 50, NULL)
) t -- column names and types are already defined
WHERE f.pkid = t.pkid;
Sub-pilihan pertama dari subkueri:
(SELECT x, y, pkid FROM foo LIMIT 0)
mendapatkan nama dan jenis kolom, tetapi LIMIT 0
mencegahnya menambahkan baris yang sebenarnya. Baris berikutnya dipaksa ke jenis baris yang sekarang terdefinisi dengan baik - dan segera diperiksa apakah cocok dengan jenisnya. Harus ada peningkatan tambahan yang halus dari bentuk asli Anda.
Sambil memberikan nilai untuk semua kolom tabel sintaks pendek ini dapat digunakan untuk baris pertama:
(TABLE foo LIMIT 0)
Batasan utama :Postgres mentransmisikan literal input dari VALUES
. yang berdiri sendiri ekspresi ke tipe "usaha terbaik" segera. Ketika nanti mencoba untuk mentransmisikan ke jenis yang diberikan dari SELECT
. pertama , mungkin sudah terlambat untuk beberapa jenis jika tidak ada pemeran penugasan terdaftar antara tipe yang diasumsikan dan tipe target. Contoh:text
-> timestamp
atau text
-> json
.
Pro:
- Overhead minimum.
- Dapat dibaca, sederhana, dan cepat.
- Anda hanya perlu mengetahui nama kolom tabel yang relevan.
Penipu:
- Resolusi jenis dapat gagal untuk beberapa jenis.
1. Pilih baris dengan LIMIT 0
, tambahkan baris dengan UNION ALL SELECT
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
(SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
UNION ALL SELECT 1, 20, NULL
UNION ALL SELECT 2, 50, NULL
) t -- column names and types are already defined
WHERE f.pkid = t.pkid;
Pro:
- Seperti 0. , tetapi menghindari resolusi jenis yang gagal.
Penipu:
UNION ALL SELECT
lebih lambat dariVALUES
ekspresi untuk daftar panjang baris, seperti yang Anda temukan dalam pengujian Anda.- Sintaksis verbose per baris.
2. VALUES
ekspresi dengan tipe per kolom
...
FROM (
VALUES
((SELECT pkid FROM foo LIMIT 0)
, (SELECT x FROM foo LIMIT 0)
, (SELECT y FROM foo LIMIT 0)) -- get type for each col individually
, (1, 20, NULL)
, (2, 50, NULL)
) t (pkid, x, y) -- columns names not defined yet, only types.
...
Bertentangan dengan 0. ini menghindari resolusi tipe prematur.
Baris pertama di VALUES
ekspresi adalah deretan NULL
nilai yang mendefinisikan tipe untuk semua baris berikutnya. Baris kebisingan terdepan ini difilter berdasarkan WHERE f.pkid = t.pkid
kemudian, sehingga tidak pernah melihat cahaya hari. Untuk tujuan lain, Anda dapat menghilangkan baris pertama yang ditambahkan dengan OFFSET 1
dalam subkueri.
Pro:
- Biasanya lebih cepat dari 1. (atau bahkan 0. )
- Sintaks pendek untuk tabel dengan banyak kolom dan hanya sedikit yang relevan.
- Anda hanya perlu mengetahui nama kolom tabel yang relevan.
Penipu:
- Sintaksis verbose hanya untuk beberapa baris
- Kurang terbaca (IMO).
3. VALUES
ekspresi dengan tipe baris
UPDATE foo f
SET x = (t.r).x -- parenthesis needed to make syntax unambiguous
, y = (t.r).y
FROM (
VALUES
('(1,20,)'::foo) -- columns need to be in default order of table
,('(2,50,)') -- nothing after the last comma for NULL
) t (r) -- column name for row type
WHERE f.pkid = (t.r).pkid;
Anda tentu tahu nama tabelnya. Jika Anda juga mengetahui jumlah kolom dan urutannya, Anda dapat mengerjakan ini.
Untuk setiap tabel di PostgreSQL, sebuah tipe baris didaftarkan secara otomatis. Jika Anda mencocokkan jumlah kolom dalam ekspresi Anda, Anda dapat mentransmisikan ke jenis baris tabel ('(1,50,)'::foo
) dengan demikian menetapkan jenis kolom secara implisit. Jangan letakkan apa pun di belakang koma untuk memasukkan NULL
nilai. Tambahkan koma untuk setiap kolom tambahan yang tidak relevan.
Pada langkah berikutnya Anda dapat mengakses kolom individual dengan sintaks yang ditunjukkan. Selengkapnya tentang Pemilihan Lapangan dalam manual.
Atau Anda dapat menambahkan deretan nilai NULL dan gunakan sintaks yang seragam untuk data aktual:
...
VALUES
((NULL::foo)) -- row of NULL values
, ('(1,20,)') -- uniform ROW value syntax for all
, ('(2,50,)')
...
Pro:
- Tercepat (setidaknya dalam pengujian saya dengan beberapa baris dan kolom).
- Sintaks terpendek untuk beberapa baris atau tabel yang membutuhkan semua kolom.
- Anda tidak perlu mengeja kolom tabel - semua kolom secara otomatis memiliki nama yang cocok.
Penipu:
- Sintaks yang tidak begitu dikenal untuk pemilihan bidang dari jenis rekaman / baris / komposit.
- Anda perlu mengetahui jumlah dan posisi kolom yang relevan dalam urutan default.
4. VALUES
ekspresi dengan terurai jenis baris
Suka 3. , tetapi dengan baris yang didekomposisi dalam sintaks standar:
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
VALUES
(('(1,20,)'::foo).*) -- decomposed row of values
, (2, 50, NULL)
) t(pkid, x, y) -- arbitrary column names (I made them match)
WHERE f.pkid = t.pkid; -- eliminates 1st row with NULL values
Atau, dengan baris terdepan dari nilai NULL lagi:
...
VALUES
((NULL::foo).*) -- row of NULL values
, (1, 20, NULL) -- uniform syntax for all
, (2, 50, NULL)
...
Pro dan kontra seperti 3. , tetapi dengan sintaks yang lebih umum dikenal.
Dan Anda perlu mengeja nama kolom (jika Anda membutuhkannya).
5. VALUES
ekspresi dengan tipe yang diambil dari tipe baris
Seperti komentar Unril, kita dapat menggabungkan kebaikan 2. dan 4. untuk hanya menyediakan subset kolom:
UPDATE foo f
SET ( x, y)
= (t.x, t.y) -- short notation, see below
FROM (
VALUES
((NULL::foo).pkid, (NULL::foo).x, (NULL::foo).y) -- subset of columns
, (1, 20, NULL)
, (2, 50, NULL)
) t(pkid, x, y) -- arbitrary column names (I made them match)
WHERE f.pkid = t.pkid;
Pro dan kontra seperti 4. , tetapi kita dapat bekerja dengan subset kolom apa pun dan tidak perlu mengetahui daftar lengkapnya.
Juga menampilkan sintaks pendek untuk UPDATE
itu sendiri yang nyaman untuk kasus dengan banyak kolom. Terkait:
- Pembaruan massal semua kolom
4. dan 5. adalah favorit saya.
db<>main biola di sini - mendemonstrasikan semua