PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Testuj funkcję zerową przy różnych parametrach

Nie zgadzam się z niektórymi radami w innych odpowiedziach. Można to zrobić za pomocą PL/pgSQL i myślę, że jest to w większości znacznie lepsze do składania zapytań w aplikacji klienckiej. Jest szybszy i czystszy, a aplikacja wysyła tylko absolutne minimum w żądaniach. Wyrażenia SQL są zapisywane w bazie danych, co ułatwia utrzymanie - chyba że chcesz zebrać całą logikę biznesową w aplikacji klienckiej, zależy to od ogólnej architektury.

Funkcja PL/pgSQL z dynamicznym SQL

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$;

Zadzwoń:

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

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

Ponieważ wszystkie parametry funkcji mają wartości domyślne, możesz użyć pozycyjnego notacja, nazwana notacja lub mieszana notacja według własnego wyboru w wywołaniu funkcji. Zobacz:

  • Funkcje ze zmienną liczbą parametrów wejściowych

Więcej wyjaśnień podstaw dynamicznego SQL:

  • Refaktoryzuj funkcję PL/pgSQL, aby zwrócić dane wyjściowe różnych zapytań SELECT

concat() funkcja jest kluczowa dla budowania ciągu. Został wprowadzony w Postgresie 9.1.

ELSE gałąź CASE domyślna instrukcja to NULL gdy nie jest obecny. Upraszcza kod.

USING klauzula EXECUTE uniemożliwia wstrzyknięcie SQL, ponieważ wartości są przekazywane jako wartości i pozwala na bezpośrednie użycie wartości parametrów, dokładnie tak jak w przygotowanych zestawieniach.

NULL wartości są tutaj używane do ignorowania parametrów. W rzeczywistości nie są używane do wyszukiwania.

Nie potrzebujesz nawiasów wokół SELECT z RETURN QUERY .

Prosta funkcja SQL

możesz zrób to za pomocą zwykłej funkcji SQL i unikaj dynamicznego SQL. W niektórych przypadkach może to być szybsze. Ale nie spodziewałbym się tego w tym przypadku . Planowanie zapytania bez zbędnych sprzężeń i predykatów zazwyczaj daje najlepsze wyniki. Koszt planowania prostego zapytania, takiego jak to, jest prawie znikomy.

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$;

Identyczne połączenie.

Aby skutecznie ignorować parametry za pomocą NULL wartości :

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

Aby faktycznie używać wartości NULL jako parametrów , zamiast tego użyj tej konstrukcji:

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

Pozwala to również na indeksy do użycia.
W omawianym przypadku zastąp wszystkie wystąpienia LEFT JOIN z JOIN .

db<>graj tutaj - z prostym demo dla wszystkich wariantów.
Stary sqlfiddle

Na marginesie

  • Nie używaj name i id jako nazwy kolumn. Nie są one opisowe i kiedy dołączasz do kilku stołów (tak jak robisz to a lot w relacyjnej bazie danych), otrzymujesz kilka kolumn o nazwie name lub id i trzeba dołączyć aliasy, aby uporządkować bałagan.

  • Proszę sformatować poprawnie swój kod SQL, przynajmniej podczas zadawania pytań publicznych. Ale rób to również prywatnie, dla własnego dobra.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jaki jest najlepszy graficzny interfejs użytkownika PostgreSQL? Porównanie 2021

  2. Jak chronić bazy danych PostgreSQL przed cyberatakami za pomocą zapory SQL?

  3. [Wideo] Ansible i PostgreSQL

  4. Jak wyświetlić numery wierszy w zapytaniu PostgreSQL?

  5. Odrzuć część milisekundową ze znacznika czasu