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

Uji fungsi null dengan parameter yang bervariasi

Saya tidak setuju dengan beberapa saran dalam jawaban lain. Ini dapat dilakukan dengan PL/pgSQL dan menurut saya sebagian besar jauh lebih unggul untuk merakit kueri dalam aplikasi klien. Ini lebih cepat dan lebih bersih dan aplikasi hanya mengirimkan permintaan minimum melalui kabel. Pernyataan SQL disimpan di dalam database, yang membuatnya lebih mudah untuk dipelihara - kecuali jika Anda ingin mengumpulkan semua logika bisnis dalam aplikasi klien, ini tergantung pada arsitektur umum.

Fungsi PL/pgSQL dengan SQL dinamis

CREATE OR REPLACE FUNCTION func(
      _ad_nr       int  = NULL
    , _ad_nr_extra text = NULL
    , _ad_info     text = NULL
    , _ad_postcode text = NULL
    , _sname       text = NULL
    , _pname       text = NULL
    , _cname       text = NULL)
  RETURNS TABLE(id int, match text, score int, nr int, nr_extra text
              , info text, postcode text, street text, place text
              , country text, the_geom geometry)
  LANGUAGE plpgsql AS
$func$
BEGIN
   -- RAISE NOTICE '%', -- for debugging
   RETURN QUERY EXECUTE concat(
   $$SELECT a.id, 'address'::text, 1 AS score, a.ad_nr, a.ad_nr_extra
        , a.ad_info, a.ad_postcode$$

   , CASE WHEN (_sname, _pname, _cname) IS NULL THEN ', NULL::text' ELSE ', s.name' END  -- street
   , CASE WHEN (_pname, _cname) IS NULL         THEN ', NULL::text' ELSE ', p.name' END  -- place
   , CASE WHEN _cname IS NULL                   THEN ', NULL::text' ELSE ', c.name' END  -- country
   , ', a.wkb_geometry'

   , concat_ws('
   JOIN   '
   , '
   FROM   "Addresses" a'
   , CASE WHEN NOT (_sname, _pname, _cname) IS NULL THEN '"Streets"   s ON s.id = a.street_id' END
   , CASE WHEN NOT (_pname, _cname) IS NULL         THEN '"Places"    p ON p.id = s.place_id' END
   , CASE WHEN _cname IS NOT NULL                   THEN '"Countries" c ON c.id = p.country_id' END
   )

   , concat_ws('
   AND    '
      , '
   WHERE  TRUE'
      , CASE WHEN $1 IS NOT NULL THEN 'a.ad_nr = $1' END
      , CASE WHEN $2 IS NOT NULL THEN 'a.ad_nr_extra = $2' END
      , CASE WHEN $3 IS NOT NULL THEN 'a.ad_info = $3' END
      , CASE WHEN $4 IS NOT NULL THEN 'a.ad_postcode = $4' END
      , CASE WHEN $5 IS NOT NULL THEN 's.name = $5' END
      , CASE WHEN $6 IS NOT NULL THEN 'p.name = $6' END
      , CASE WHEN $7 IS NOT NULL THEN 'c.name = $7' END
   )
   )
   USING $1, $2, $3, $4, $5, $6, $7;
END
$func$;

Telepon:

SELECT * FROM func(1, '_ad_nr_extra', '_ad_info', '_ad_postcode', '_sname');

SELECT * FROM func(1, _pname := 'foo');

Karena semua parameter fungsi memiliki nilai default, Anda dapat menggunakan posisional notasi, bernama notasi atau campuran notasi yang Anda pilih dalam panggilan fungsi. Lihat:

  • Fungsi dengan jumlah variabel parameter input

Penjelasan lebih lanjut untuk dasar-dasar SQL dinamis:

  • Memfaktorkan ulang fungsi PL/pgSQL untuk mengembalikan output dari berbagai kueri SELECT

concat() fungsi adalah instrumental untuk membangun string. Itu diperkenalkan dengan Postgres 9.1.

ELSE cabang dari CASE pernyataan default ke NULL ketika tidak hadir. Menyederhanakan kode.

USING klausa untuk EXECUTE membuat injeksi SQL tidak mungkin karena nilai dilewatkan sebagai nilai dan memungkinkan untuk menggunakan nilai parameter secara langsung, persis seperti dalam pernyataan yang disiapkan.

NULL nilai digunakan untuk mengabaikan parameter di sini. Mereka sebenarnya tidak digunakan untuk mencari.

Anda tidak perlu tanda kurung di sekitar SELECT dengan RETURN QUERY .

Fungsi SQL sederhana

Anda bisa lakukan dengan fungsi SQL biasa dan hindari SQL dinamis. Untuk beberapa kasus ini mungkin lebih cepat. Tapi saya tidak mengharapkannya dalam kasus ini . Merencanakan kueri tanpa bergabung dan predikat yang tidak perlu biasanya menghasilkan hasil terbaik. Biaya perencanaan untuk kueri sederhana seperti ini hampir dapat diabaikan.

CREATE OR REPLACE FUNCTION func_sql(
     _ad_nr       int  = NULL
   , _ad_nr_extra text = NULL
   , _ad_info     text = NULL
   , _ad_postcode text = NULL
   , _sname       text = NULL
   , _pname       text = NULL
   , _cname       text = NULL)
  RETURNS TABLE(id int, match text, score int, nr int, nr_extra text
              , info text, postcode text, street text, place text
              , country text, the_geom geometry)
  LANGUAGE sql AS 
$func$
SELECT a.id, 'address' AS match, 1 AS score, a.ad_nr, a.ad_nr_extra
     , a.ad_info, a.ad_postcode
     , s.name AS street, p.name AS place
     , c.name AS country, a.wkb_geometry
FROM   "Addresses"      a
LEFT   JOIN "Streets"   s ON s.id = a.street_id
LEFT   JOIN "Places"    p ON p.id = s.place_id
LEFT   JOIN "Countries" c ON c.id = p.country_id
WHERE ($1 IS NULL OR a.ad_nr = $1)
AND   ($2 IS NULL OR a.ad_nr_extra = $2)
AND   ($3 IS NULL OR a.ad_info = $3)
AND   ($4 IS NULL OR a.ad_postcode = $4)
AND   ($5 IS NULL OR s.name = $5)
AND   ($6 IS NULL OR p.name = $6)
AND   ($7 IS NULL OR c.name = $7)
$func$;

Panggilan yang sama.

Untuk secara efektif mengabaikan parameter dengan NULL nilai :

($1 IS NULL OR a.ad_nr = $1)

Untuk benar-benar menggunakan nilai NULL sebagai parameter , gunakan konstruksi ini sebagai gantinya:

($1 IS NULL AND a.ad_nr IS NULL OR a.ad_nr = $1)  -- AND binds before OR

Ini juga memungkinkan indeks untuk digunakan.
Untuk kasus yang ada, ganti semua instance LEFT JOIN dengan JOIN .

db<>main biola di sini - dengan demo sederhana untuk semua varian.
sqlfiddle lama

Selain

  • Jangan gunakan name dan id sebagai nama kolom. Mereka tidak deskriptif dan ketika Anda bergabung dengan sekelompok tabel (seperti yang Anda lakukan untuk a lot dalam database relasional), Anda berakhir dengan beberapa kolom yang semuanya bernama name atau id , dan harus melampirkan alias untuk menyortir kekacauan.

  • Harap format SQL Anda dengan benar, setidaknya saat mengajukan pertanyaan publik. Tapi lakukan juga secara pribadi, untuk kebaikan Anda sendiri.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Perbarui beberapa kolom dalam fungsi pemicu di plpgsql

  2. Ketika autovacuum tidak vakum

  3. Bagaimana Mod() Bekerja di PostgreSQL

  4. Pemantauan Database PostgreSQL:Kiat untuk Apa yang Harus Dipantau

  5. Cara Mengizinkan Akses Jarak Jauh ke database PostgreSQL