PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Тествайте за нула във функция с различни параметри

Не съм съгласен с някои от съветите в други отговори. Това може да се направи с PL/pgSQL и мисля, че най-вече е далеч по-добро за сглобяване на заявки в клиентско приложение. Той е по-бърз и по-чист и приложението изпраща само минимума по проводника в заявки. SQL изразите се записват в базата данни, което я прави по-лесна за поддръжка - освен ако не искате да съберете цялата бизнес логика в клиентското приложение, това зависи от общата архитектура.

PL/pgSQL функция с динамичен 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$;

Обадете се:

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

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

Тъй като всички параметри на функцията имат стойности по подразбиране, можете да използвате позиционен нотация,именна нотация илисмесено нотация по ваш избор в извикването на функция. Вижте:

  • Функции с променлив брой входни параметри

Още обяснение за основите на динамичния SQL:

  • Рефакторирайте функция PL/pgSQL, за да върнете резултатите от различни SELECT заявки

concat() функцията е важна за изграждането на низа. Той беше представен с Postgres 9.1.

ELSE клон на CASE операторът по подразбиране е NULL когато не присъства. Опростява кода.

USING клауза за EXECUTE прави SQL инжектирането невъзможно, тъй като стойностите се предават като стойности и позволява директно използване на стойностите на параметрите, точно както в подготвените оператори.

NULL стойностите се използват за игнориране на параметрите тук. Те всъщност не се използват за търсене.

Нямате нужда от скоби около SELECT с RETURN QUERY .

Проста SQL функция

Вие можете направете го с обикновена SQL функция и избягвайте динамичния SQL. В някои случаи това може да е по-бързо. Но не бих го очаквал в този случай . Планирането на заявката без ненужни съединения и предикати обикновено дава най-добри резултати. Разходите за планиране за проста заявка като тази са почти незначителни.

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

Идентично обаждане.

За ефективно игнориране на параметри с NULL ценностите :

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

За действително използване на NULL стойности като параметри , вместо това използвайте тази конструкция:

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

Това също позволява индекси да се използва.
За конкретния случай заменете всички екземпляри на LEFT JOIN с JOIN .

db<>цигулка тук - спроста демонстрация за всички варианти.
Стар sqlfiddle

Отстрани

  • Не използвайте name и id като имена на колони. Те не са описателни и когато се присъедините към куп таблици (както правите с a lot в релационна база данни), получавате няколко колони, всички с име name или id , и трябва да прикачите псевдоними, за да сортирате бъркотията.

  • Моля, форматирайте правилно своя SQL, поне когато задавате обществени въпроси. Но направете го и насаме, за ваше добро.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. конвертирайте типа данни на MySQL SET в Postgres

  2. Postgres Left Join с условието къде

  3. Най-добрият начин да проверите за празна или нулева стойност

  4. Какво е новото в PostgreSQL 11

  5. Typecast низ към цяло число