PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Casting tipe NULL saat memperbarui beberapa baris

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 dari VALUES 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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Gabungkan dua tabel menjadi tabel baru sehingga memilih baris dari tabel lainnya diabaikan

  2. Evolusi Fault Tolerance di PostgreSQL

  3. PostgreSQL INSERT ON CONFLICT UPDATE (upsert) gunakan semua nilai yang dikecualikan

  4. menyimpan objek python di tabel postgres dengan acar

  5. PostgreSQL:Buat tabel jika tidak ada AS