Domanda

Sì, più domande più grandi-n-per gruppo.

Data la tabella releases con le seguenti colonne:

 id         | primary key                 | 
 volume     | double precision            |
 chapter    | double precision            |
 series     | integer-foreign-key         |
 include    | boolean                     | not null
.

Voglio selezionare il composto massimo del volume, quindi capitolo per un set di serie.

In questo momento, se query per la serie-distinzione-serie, posso facilmente realizzarlo come segue:

SELECT 
       releases.chapter AS releases_chapter,
       releases.include AS releases_include,
       releases.series AS releases_series
FROM releases
WHERE releases.series = 741
  AND releases.include = TRUE
ORDER BY releases.volume DESC NULLS LAST, releases.chapter DESC NULLS LAST LIMIT 1;
.

Tuttavia, se ho un grande set di generatori series (e lo faccio), questo corre rapidamente in problemi di efficienza in cui emetti 100 query per generare una singola pagina.

Io come per rotolare il tutto in una singola domanda, dove posso semplicemente dire WHERE releases.series IN (1,2,3....), ma non ho capito come convincere i postgres per farmi farlo. .

L'approccio ingenuo sarebbe:

SELECT releases.volume AS releases_volume,
       releases.chapter AS releases_chapter,
       releases.series AS releases_series
FROM 
    releases
WHERE 
    releases.series IN (12, 17, 44, 79, 88, 110, 129, 133, 142, 160, 193, 231, 235, 295, 340, 484, 499, 
                        556, 581, 664, 666, 701, 741, 780, 790, 796, 874, 930, 1066, 1091, 1135, 1137, 
                        1172, 1331, 1374, 1418, 1435, 1447, 1471, 1505, 1521, 1540, 1616, 1702, 1768, 
                        1825, 1828, 1847, 1881, 2007, 2020, 2051, 2085, 2158, 2183, 2190, 2235, 2255, 
                        2264, 2275, 2325, 2333, 2334, 2337, 2341, 2343, 2348, 2370, 2372, 2376, 2606, 
                        2634, 2636, 2695, 2696 )
  AND releases.include = TRUE
GROUP BY 
    releases_series
ORDER BY releases.volume DESC NULLS LAST, releases.chapter DESC NULLS LAST;
.

che ovviamente non funziona:

.
ERROR:  column "releases.volume" must appear in the 
        GROUP BY clause or be used in an aggregate function
.

Senza il GROUP BY, prende tutto, e con un semplice filtraggio procedurale funzionerà anche, ma ci deve essere un modo "corretto" per farlo in SQL.

A seguito degli errori e aggiungendo aggregati:

SELECT max(releases.volume) AS releases_volume,
       max(releases.chapter) AS releases_chapter,
       releases.series AS releases_series
FROM 
    releases
WHERE 
    releases.series IN (12, 17, 44, 79, 88, 110, 129, 133, 142, 160, 193, 231, 235, 295, 340, 484, 499, 
                        556, 581, 664, 666, 701, 741, 780, 790, 796, 874, 930, 1066, 1091, 1135, 1137, 
                        1172, 1331, 1374, 1418, 1435, 1447, 1471, 1505, 1521, 1540, 1616, 1702, 1768, 
                        1825, 1828, 1847, 1881, 2007, 2020, 2051, 2085, 2158, 2183, 2190, 2235, 2255, 
                        2264, 2275, 2325, 2333, 2334, 2337, 2341, 2343, 2348, 2370, 2372, 2376, 2606, 
                        2634, 2636, 2695, 2696 )
  AND releases.include = TRUE
GROUP BY 
    releases_series;
.

per lo più funziona, ma il problema è che i due massimi non sono coerenti. Se ho due righe, uno in cui il volume: il capitolo è 1: 5 e 4: 1, ho bisogno di restituire 4: 1, ma i massimi indipendenti ritornano 4: 5.

Francamente, questo sarebbe così semplice da implementare nel mio codice applicativo che devo mancare qualcosa di ovvio qui. Come posso implementare una query che soddisfa effettivamente le mie esigenze?

È stato utile?

Soluzione

La soluzione semplice in Postgres è con DISTINCT ON:

SELECT DISTINCT ON (r.series)
       r.volume  AS releases_volume
     , r.chapter AS releases_chapter
     , r.series  AS releases_series
FROM   releases r
WHERE  r.series IN (
    12, 17, 44, 79, 88, 110, 129, 133, 142, 160, 193, 231, 235, 295, 340, 484, 499
  , 556, 581, 664, 666, 701, 741, 780, 790, 796, 874, 930, 1066, 1091, 1135, 1137
  , 1172, 1331, 1374, 1418, 1435, 1447, 1471, 1505, 1521, 1540, 1616, 1702, 1768
  , 1825, 1828, 1847, 1881, 2007, 2020, 2051, 2085, 2158, 2183, 2190, 2235, 2255
  , 2264, 2275, 2325, 2333, 2334, 2337, 2341, 2343, 2348, 2370, 2372, 2376, 2606
  , 2634, 2636, 2695, 2696)
AND    r.include
ORDER  BY r.series, r.volume DESC NULLS LAST, r.chapter DESC NULLS LAST;
.

Dettagli:

A seconda della distribuzione dei dati ci possono essere tecniche più veloci:

Inoltre, ci sono Alternative più veloci per liste lunghe rispetto a IN () .

Combinazione di un array non credente con un join LATERAL:

SELECT r.*
FROM   unnest('{12, 17, 44, 79, 88, 110, 129}'::int[]) t(i)  -- or many more items
     , LATERAL (
   SELECT volume  AS releases_volume
        , chapter AS releases_chapter
        , series  AS releases_series
   FROM   releases
   WHERE  series = t.i 
   AND    include
   ORDER  BY series, volume DESC NULLS LAST, chapter DESC NULLS LAST
   LIMIT  1
   ) r;
.

è spesso più veloce. Per prestazioni migliori è necessario un indice di corrispondenza Multicolunn come:

CREATE INDEX releases_series_volume_chapter_idx
ON releases(series, volume DESC NULLS LAST, chapter DESC NULLS LAST);
.

Correlato:

E se ci sono più di pochi le righe in cui include non è true, mentre sei interessato solo alle righe con include = true, prendono in considerazione un Parziale Indice multicolumne :

CREATE INDEX releases_series_volume_chapter_idx
ON releases(series, volume DESC NULLS LAST, chapter DESC NULLS LAST)
WHERE include;
.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a dba.stackexchange
scroll top