Pregunta

Sí, la más grande de n por grupo de preguntas.

Dada la tabla releases con las siguientes columnas:

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

Quiero seleccionar el compuesto max de volumen, a continuación, el capítulo de un conjunto de series.

Ahora mismo, si me consulta por distintos-serie, me puede lograr esto fácilmente de la siguiente manera:

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;

Sin embargo, si tengo un gran conjunto de series (y lo hago), este rápidamente se ejecuta en temas de eficiencia donde estoy emitiendo 100+ consultas para generar una sola página.

Me gustaría como para rodar la cosa entera en una sola consulta, donde puedo simplemente decir WHERE releases.series IN (1,2,3....), pero no he averiguado cómo convencer a Postgres para que me deje hacer eso.

El enfoque ingenuo sería:

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;

Que, obviamente, no funciona:

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

Sin el GROUP BY, no recuperar todo, y con unas simples procedimiento de filtrado incluso podría trabajar, pero no debe ser un "buen" manera de hacer esto en SQL.

Tras los errores, y la adición de agregados:

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;

En su mayoría funciona, pero el problema es que los dos máximos no son coherentes.Si tengo dos filas, una en volumen:capítulo 1:5 y 4:1, necesito volver 4:1, pero el independiente máximos de retorno 4:5.

Francamente, esto sería tan simple de implementar en mi código de la aplicación que me falta algo obvio aquí.¿Cómo puedo implementar una consulta que realmente satisfaga mis necesidades?

¿Fue útil?

Solución

La solución más simple en 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;

Detalles:

Dependiendo de la distribución de los datos no puede ser más rápido técnicas:

También, hay más rápido alternativas para largas listas de IN ().

La combinación de una unnested matriz con un LATERAL unirse:

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;

Es a menudo más rápido.Para un mejor rendimiento que necesita una coincidencia índice de varias columnas como:

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

Relacionado con:

Y si hay más de un par de las filas en las que include no es true, mientras que usted está interesado sólo en las filas include = true, a continuación, considere la posibilidad de un parcial de varias columnas de índice:

CREATE INDEX releases_series_volume_chapter_idx
ON releases(series, volume DESC NULLS LAST, chapter DESC NULLS LAST)
WHERE include;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a dba.stackexchange
scroll top