Question

Yep, plus plus-n-p-groupe des questions.

Compte tenu de la une table releases avec les colonnes suivantes:

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

Je veux choisir le composé max de volume, puis le chapitre pour un jeu de la série.

Maintenant, si j'ai une requête par-distincte de la série, je peux facilement faire le comme suit:

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;

Cependant, si j'ai un grand ensemble de series (et je le fais), c'rapidement heurte à des problèmes d'efficacité, là où je suis émission 100+ des requêtes afin de générer une seule page.

J'avais comme rouler le tout en une seule requête, où je peux simplement vous dire WHERE releases.series IN (1,2,3....), mais je n'ai pas compris comment convaincre Postgres de me laisser faire.

L'approche naïve serait:

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;

Ce qui évidemment ne fonctionne pas:

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

Sans l' GROUP BY, il ne s'extraire de tout, et avec une simple procédure de filtrage, il serait même travail, mais il doit y avoir une "bonne" façon de le faire en SQL.

Suivant les erreurs, et l'ajout d'agrégats:

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;

La plupart fonctionne, mais le problème est que les deux maximums ne sont pas cohérentes.Si j'ai deux lignes, l'une où le volume:chapitre 1:5 et 4:1, j'ai besoin de retour 4:1, mais les indépendants, les maximums de retour 4:5.

Franchement, ce serait tellement simple à mettre en œuvre dans ma demande de code que j'ai manquer quelque chose d'évident ici.Comment puis-je mettre en œuvre une requête qui fait satisfait mes exigences?

Était-ce utile?

La solution

La solution la plus simple dans Postgres est avec 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;

Détails:

Selon les données sur la distribution, il peut être plus rapide des techniques:

Aussi, il y a plus rapide des solutions de rechange pour les longues listes de IN ().

Combinant une désemboîtées tableau avec une LATERAL joindre:

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;

Est souvent plus rapide.Pour de meilleures performances, vous avez besoin d'un correspondant index multicolonne comme:

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

Connexes:

Et si il y a plus que quelques les lignes où include n'est pas true, pendant que vous êtes seulement intéressé dans les lignes include = true, puis envisager un partielle index multicolonne:

CREATE INDEX releases_series_volume_chapter_idx
ON releases(series, volume DESC NULLS LAST, chapter DESC NULLS LAST)
WHERE include;
Licencié sous: CC-BY-SA avec attribution
Non affilié à dba.stackexchange
scroll top