sql >> Database teknologi >  >> RDS >> PostgreSQL

Valg af flere max()-værdier ved hjælp af en enkelt SQL-sætning

Endnu en gang, for mere end blot nogle få "datatyper", foreslår jeg at bruge crosstab() :

SELECT * FROM crosstab(
     $$SELECT DISTINCT ON (1, 2)
              'max' AS "type", data_type, val
       FROM   tbl
       ORDER  BY 1, 2, val DESC$$

    ,$$VALUES ('Final Fantasy'), ('Quake 3'), ('World of Warcraft')$$)
AS x ("type" text, "Final Fantasy" int, "Quake 3" int, "World of Warcraft" int)

Returnerer:

type | Final Fantasy | Quake 3 | World of Warcraft
-----+---------------+---------+-------------------
max  | 500           | 1500    |    1200

Mere forklaring på det grundlæggende:
PostgreSQL Crosstab Query

Dynamisk løsning

Det vanskelige er at gøre dette helt dynamisk :for at få det til at fungere for

  • et ukendt nummer af kolonner (data_typer i dette tilfælde)
  • med ukendte navne (data_types igen)

I det mindste typen er velkendt:integer i dette tilfælde.

Kort sagt:det er ikke muligt med nuværende PostgreSQL (inklusive 9.3). Der er tilnærmelser med polymorfe typer og måder at omgå begrænsningerne med arrays eller hstore-typer. Kan være god nok for dig. Men det er strengt ikke muligt for at få resultatet med individuelle kolonner i en enkelt SQL-forespørgsel. SQL er meget rigid med hensyn til typer og vil gerne vide, hvad de kan forvente tilbage.

Men , kan det gøres med to forespørgsler. Den første bygger selve forespørgslen til brug. Bygger på ovenstående simple case:

SELECT $f$SELECT * FROM crosstab(
     $$SELECT DISTINCT ON (1, 2)
              'max' AS "type", data_type, val
       FROM   tbl
       ORDER  BY 1, 2, val DESC$$

    ,$$VALUES ($f$     || string_agg(quote_literal(data_type), '), (') || $f$)$$)
AS x ("type" text, $f$ || string_agg(quote_ident(data_type), ' int, ') || ' int)'
FROM  (SELECT DISTINCT data_type FROM tbl) x

Dette genererer den forespørgsel, du faktisk har brug for. Kør den anden i samme transaktion for at undgå problemer med samtidighed.

Bemærk den strategiske brug af quote_literal() og quote_ident() at rense alle former for ulovlige (til kolonner) navne og forhindre SQL-injektion .

Bliv ikke forvirret af flere lag af dollarnotering. Det er nødvendigt for at bygge dynamiske forespørgsler. Jeg siger det så enkelt som muligt.



  1. problem med at finde listen over filer i mappen

  2. Enkel måde at transponere kolonner og rækker i SQL?

  3. Er det bedre at udføre mange sql-kommandoer med én forbindelse, eller oprette forbindelse igen hver gang?

  4. Migrer fra Oracle til MySQL