sql >> Database >  >> RDS >> PostgreSQL

Een dynamische kruistabelquery uitvoeren

Wat je vraagt ​​is onmogelijk . SQL is een strikt getypte taal. PostgreSQL-functies moeten een retourtype declareren (RETURNS .. ) op het tijdstip van creatie .

Een beperkte manier om dit te omzeilen is met polymorfe functies. Als u het retourtype kunt opgeven op het tijdstip van de functie aanroep . Maar dat blijkt niet uit uw vraag.

  • Refactor een PL/pgSQL-functie om de uitvoer van verschillende SELECT-query's te retourneren

Je kunt een volledig dynamisch resultaat retourneren met anonieme records. Maar dan moet u bij elke oproep een kolomdefinitielijst opgeven. En hoe weet u van de geretourneerde kolommen? Vang 22.

Er zijn verschillende oplossingen, afhankelijk van wat u nodig heeft of waarmee u kunt werken. Aangezien al uw gegevenskolommen hetzelfde gegevenstype lijken te delen, raad ik u aan een array te retourneren :text[] . Of u kunt een documenttype retourneren zoals hstore of json . Gerelateerd:

  • Dynamisch alternatief voor draaien met CASE en GROUP BY

  • Zet hstore-sleutels dynamisch om in kolommen voor een onbekende set sleutels

Maar het is misschien eenvoudiger om gewoon twee aanroepen te gebruiken:1:Laat Postgres de query bouwen. 2:Voer geretourneerde rijen uit en haal ze op.

  • Meerdere max()-waarden selecteren met een enkele SQL-instructie

Ik zou de functie van Eric Minikel zoals gepresenteerd in je vraag helemaal niet . gebruiken . Het is niet veilig tegen SQL-injectie door middel van kwaadwillig misvormde identifiers. Gebruik format() om queryreeksen te bouwen, tenzij u een verouderde versie gebruikt die ouder is dan Postgres 9.1.

Een kortere en schonere implementatie zou er als volgt uit kunnen zien:

CREATE OR REPLACE FUNCTION xtab(_tbl regclass, _row text, _cat text
                              , _expr text  -- still vulnerable to SQL injection!
                              , _type regtype)
  RETURNS text AS
$func$
DECLARE
   _cat_list text;
   _col_list text;
BEGIN

-- generate categories for xtab param and col definition list    
EXECUTE format(
 $$SELECT string_agg(quote_literal(x.cat), '), (')
        , string_agg(quote_ident  (x.cat), %L)
   FROM  (SELECT DISTINCT %I AS cat FROM %s ORDER BY 1) x$$
 , ' ' || _type || ', ', _cat, _tbl)
INTO  _cat_list, _col_list;

-- generate query string
RETURN format(
'SELECT * FROM crosstab(
   $q$SELECT %I, %I, %s
      FROM   %I
      GROUP  BY 1, 2  -- only works if the 3rd column is an aggregate expression
      ORDER  BY 1, 2$q$
 , $c$VALUES (%5$s)$c$
   ) ct(%1$I text, %6$s %7$s)'
, _row, _cat, _expr  -- expr must be an aggregate expression!
, _tbl, _cat_list, _col_list, _type
);

END
$func$ LANGUAGE plpgsql;

Dezelfde functieaanroep als uw originele versie. De functie crosstab() wordt geleverd door de extra module tablefunc die moet worden geïnstalleerd. Basis:

  • PostgreSQL-kruistabelquery

Hiermee worden kolom- en tabelnamen veilig verwerkt. Let op het gebruik van objectidentificatietypes regclass en regtype . Werkt ook voor namen die in het schema zijn gekwalificeerd.

  • Tabelnaam als een PostgreSQL-functieparameter

Het is echter niet helemaal veilig terwijl u een tekenreeks doorgeeft die als expressie moet worden uitgevoerd (_expr - cellc in uw oorspronkelijke zoekopdracht). Dit soort invoer is inherent onveilig tegen SQL-injectie en mag nooit worden blootgesteld aan het grote publiek.

  • SQL-injectie in Postgres-functies versus voorbereide zoekopdrachten

Scant de tabel slechts één keer voor beide lijsten met categorieën en zou iets sneller moeten zijn.

Kan nog steeds geen volledig dynamische rijtypen retourneren, omdat dat absoluut niet mogelijk is.



  1. Hoe de conversieratio berekenen in MySQL?

  2. Opgeslagen procedure uitvoeren met behulp van entiteitsraamwerk

  3. Verschillende manieren om de SQL CONVERT-datumfunctie te gebruiken

  4. DAYOFWEEK() Voorbeelden – MySQL