Rejoindre une sous-requête limitée?
-
05-07-2019 - |
Question
J'ai cette table releases
dans une base de données SQLite3, répertoriant chaque version publiée d'une application:
|release_id|release_date|app_id|
|==========|============|======|
| 1001| 2009-01-01 | 1|
| 1003| 2009-01-01 | 1|
| 1004| 2009-02-02 | 2|
| 1005| 2009-01-15 | 1|
Donc, pour chaque app_id, il y aura plusieurs lignes. J'ai une autre table, apps
:
|app_id|name |
|======|========|
| 1|Everest |
| 2|Fuji |
Je souhaite afficher le nom de l'application et la version la plus récente, où "tout récent". signifie (a) date de publication la plus récente, et s’il existe des doublons, (b) ID de publication le plus élevé.
Je peux le faire pour une application individuelle:
SELECT apps.name,releases.release_id,releases.release_date
FROM apps
INNER JOIN releases
ON apps.app_id = releases.app_id
WHERE releases.release_id = 1003
ORDER BY releases.release_date,releases.release_id
LIMIT 1
mais bien entendu, ORDER BY s’applique à l’ensemble de la requête SELECT, et si je laisse de côté la clause WHERE, elle ne renvoie toujours qu’une ligne.
Il s'agit d'une requête unique sur une petite base de données. Les requêtes lentes, les tables temporaires, etc. sont donc acceptables. Je n'arrive pas à comprendre comment SQL le fait.
La solution
C’est facile à faire avec la fonction analytique ROW_NUMBER (), que je suppose que sqlite3 ne prend pas en charge. Mais vous pouvez le faire de manière un peu plus souple que ce qui a été dit dans les réponses précédentes:
SELECT
apps.name,
releases.release_id,
releases.release_date
FROM apps INNER JOIN releases
ON apps.app_id = releases.app_id
WHERE NOT EXISTS (
-- // where there doesn't exist a more recent release for the same app
SELECT * FROM releases AS R
WHERE R.app_id = apps.app_id
AND R.release_data > releases.release_data
)
Par exemple, si vous avez plusieurs colonnes de classement définissant le paramètre "dernière", " MAX ne fonctionnerait pas pour vous, mais vous pouvez modifier la sous-requête EXISTS pour capturer le sens plus compliqué de "dernières".
Autres conseils
C’est le "plus grand N par groupe" problème. Il arrive plusieurs fois par semaine sur StackOverflow.
J'utilise généralement une solution similaire à celle de @Steve Kass, answer , mais je le fais sans sous-requêtes (je me suis habitué il y a des années avec MySQL 4.0, qui ne supportait pas les sous-requêtes):
SELECT a.name, r1.release_id, r1.release_date
FROM apps a
INNER JOIN releases r1
LEFT OUTER JOIN releases r2 ON (r1.app_id = r2.app_id
AND (r1.release_date < r2.release_date
OR r1.release_date = r2.release_date AND r1.release_id < r2.release_id))
WHERE r2.release_id IS NULL;
En interne, cela optimise probablement la même chose que la syntaxe NOT EXISTS
. Vous pouvez analyser la requête avec EXPLAIN
pour vous en assurer.
En ce qui concerne votre commentaire, vous pouvez ignorer le test pour date_diffusion
, car id_exécution
est tout aussi utile pour établir l'ordre chronologique des publications, et je suppose que cela est garanti. unique, cela simplifie donc la requête:
SELECT a.name, r1.release_id, r1.release_date
FROM apps a
INNER JOIN releases r1
LEFT OUTER JOIN releases r2 ON (r1.app_id = r2.app_id
AND r1.release_id < r2.release_id)
WHERE r2.release_id IS NULL;
C'est moche, mais je pense que ça va marcher
select apps.name, (select releases.release_id from releases where releases.app_id=apps.app_id order by releases.release_date, releases.release_id), (select releases.release_date from releases where releases.app_id=apps.app_id order by releases.release_date, releases.release_id) from apps order by apps.app_id
J'espère qu'il sera possible d'obtenir ces deux colonnes en une sélection intégrée, mais je ne le sais pas.
Essayez:
SELECT a.name,
t.max_release_id,
t.max_date
FROM APPS a
JOIN (SELECT t.app_id,
MAX(t.release_id) 'max_release_id',
t.max_date
FROM (SELECT r.app_id,
r.release_id,
MAX(r.release_date) 'max_date'
FROM RELEASES r
GROUP BY r.app_id, r.release_id)
GROUP BY t.app_id, t.max_date) t
Err deuxième tentative. En supposant que les identifiants augmentent de façon monotone et que les débordements ne soient pas probables, vous pouvez ignorer la date et simplement faire:
SELECT apps.name, releases.release_id, releases.release_date
FROM apps INNER JOIN releases on apps.app_id = releases.app_id
WHERE releases.release_id IN
(SELECT Max(release_id) FROM releases
GROUP BY app_id);