sql >> Databasteknik >  >> RDS >> PostgreSQL

Välja flera max()-värden med en enda SQL-sats

Återigen, för mer än bara några "datatyper", föreslår jag att du använder 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)

Returnerar:

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

Mer förklaring till grunderna:
PostgreSQL Crosstab Query

Dynamisk lösning

Det knepiga är att göra detta helt dynamiskt :för att få det att fungera för

  • ett okänt nummer av kolumner (data_typer i det här fallet)
  • med okända namn (data_types igen)

Åtminstone typen är välkänt:integer i det här fallet.

Kort sagt:det är inte möjligt med nuvarande PostgreSQL (inklusive 9.3). Det finns approximationer med polymorfa typer och sätt att kringgå begränsningarna med arrayer eller hstore-typer. Kan vara tillräckligt bra för dig. Men det är strängt taget inte möjligt för att få resultatet med enskilda kolumner i en enda SQL-fråga. SQL är väldigt rigid när det gäller typer och vill veta vad man kan förvänta sig tillbaka.

Men , det kan göras med två frågor. Den första bygger den faktiska frågan som ska användas. Bygger på ovanstående enkla fall:

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

Detta genererar den fråga du faktiskt behöver. Kör den andra i samma transaktion för att undvika samtidighetsproblem.

Notera den strategiska användningen av quote_literal() och quote_ident() att sanera alla typer av olagliga (för kolumner) namn och förhindra SQL-injektion .

Bli inte förvirrad av flera lager av dollarnotering. Det är nödvändigt för att bygga dynamiska frågor. Jag uttryckte det så enkelt som möjligt.



  1. anropa en lagrad proc över en dblink

  2. Hur kan jag kombinera flera rader till en kommaavgränsad lista i SQL Server 2005?

  3. Vilken objekttyp returnerar exekveringsmetoden Spring Hibernate Template för en räknefråga på Oracle?

  4. GI 19c RPM Package Manager Database