sql >> Databáze >  >> RDS >> PostgreSQL

Test na nulovou funkci s různými parametry

Nesouhlasím s některými radami v jiných odpovědích. To lze provést pomocí PL/pgSQL a myslím, že je většinou daleko lepší k sestavování dotazů v klientské aplikaci. Je rychlejší a čistší a aplikace v požadavcích posílá přes drát jen minimum. Příkazy SQL jsou uloženy v databázi, což usnadňuje údržbu – pokud nechcete shromažďovat veškerou obchodní logiku v klientské aplikaci, závisí to na obecné architektuře.

Funkce PL/pgSQL s dynamickým 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$;

Volejte:

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

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

Protože všechny parametry funkcí mají výchozí hodnoty, můžete použít poziční notace, pojmenovaný notový zápis nebo smíšený zápis podle vašeho výběru ve volání funkce. Viz:

  • Funkce s proměnným počtem vstupních parametrů

Další vysvětlení základů dynamického SQL:

  • Refaktorujte funkci PL/pgSQL tak, aby vrátila výstup různých SELECT dotazů

concat() Tato funkce slouží ke stavbě struny. Byl představen s Postgres 9.1.

ELSE větev CASE výchozí příkaz je NULL když není přítomen. Zjednodušuje kód.

USING klauzule pro EXECUTE znemožňuje vkládání SQL, protože hodnoty jsou předávány jako hodnoty a umožňuje přímo používat hodnoty parametrů, přesně jako v připravených příkazech.

NULL hodnoty se zde používají k ignorování parametrů. Ve skutečnosti se nepoužívají k vyhledávání.

Nepotřebujete závorky kolem SELECT pomocí RETURN QUERY .

Jednoduchá funkce SQL

Mohli byste udělejte to pomocí jednoduché funkce SQL a vyhněte se dynamickému SQL. V některých případech to může být rychlejší. Ale v tomto případě bych to nečekal . Plánování dotazu bez zbytečných spojení a predikátů obvykle přináší nejlepší výsledky. Plánovací náklady na jednoduchý dotaz, jako je tento, jsou téměř zanedbatelné.

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

Identický hovor.

Chcete-li efektivně ignorovat parametry pomocí NULL hodnoty :

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

Chcete-li skutečně použít hodnoty NULL jako parametry , použijte místo toho tento konstrukt:

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

To také umožňuje indexy k použití.
V tomto případě nahraďte všechny výskyty LEFT JOIN pomocí JOIN .

db<>zde hrajte - s jednoduchou ukázkou pro všechny varianty.
Starý sqlfiddle

Asides

  • Nepoužívejte name a id jako názvy sloupců. Nejsou popisné a když se připojíte k hromadě stolů (jako a lot v relační databázi), skončíte s několika sloupci, všechny pojmenované name nebo id , a pro třídění nepořádku musíte připojit aliasy.

  • Naformátujte prosím správně své SQL, alespoň při kladení veřejných otázek. Ale dělejte to také soukromě, pro své vlastní dobro.



  1. SQL připojovací otázka

  2. Zkontrolujte nastavení relace pomocí SESSIONPROPERTY() na serveru SQL Server

  3. Převeďte sadu výsledků z pole SQL na pole řetězců

  4. Připojení PHP na Linuxu k Microsoft Access na Windows Share