Je travaille avec une table Postgres (appelée "lives") qui contient des enregistrements avec des colonnes pour time_stamp, usr_id, transaction_id, et lives_remaining. J'ai besoin d'une requête qui me donne le total de lives_remaining le plus récent pour chaque usr_id.
-
Il y a plusieurs utilisateurs (usr_id's distincts)
-
time_stamp n'est pas un identifiant unique : il arrive que des événements utilisateurs (un par ligne dans la table) se produisent avec le même time_stamp.
-
trans_id n'est unique que pour de très petites périodes : au fil du temps, il se répète.
-
remaining_lives (pour un utilisateur donné) peut à la fois augmenter et diminuer dans le temps.
exemple :
time\_stamp|lives\_remaining|usr\_id|trans\_id
-----------------------------------------
07:00 | 1 | 1 | 1
09:00 | 4 | 2 | 2
10:00 | 2 | 3 | 3
10:00 | 1 | 2 | 4
11:00 | 4 | 1 | 5
11:00 | 3 | 1 | 6
13:00 | 3 | 3 | 1
Comme j'aurai besoin d'accéder à d'autres colonnes de la ligne contenant les dernières données pour chaque usr_id donné, j'ai besoin d'une requête qui donne un résultat comme celui-ci :
time\_stamp|lives\_remaining|usr\_id|trans\_id
-----------------------------------------
11:00 | 3 | 1 | 6
10:00 | 1 | 2 | 4
13:00 | 3 | 3 | 1
Comme mentionné, chaque usr_id peut gagner ou perdre des vies, et parfois ces événements horodatés sont si proches qu'ils ont le même horodatage ! Par conséquent, cette requête ne fonctionnera pas :
SELECT b.time_stamp,b.lives_remaining,b.usr_id,b.trans_id FROM
(SELECT usr_id, max(time_stamp) AS max_timestamp
FROM lives GROUP BY usr_id ORDER BY usr_id) a
JOIN lives b ON a.max_timestamp = b.time_stamp
Au lieu de cela, je dois utiliser à la fois le time_stamp (premier) et le trans_id (deuxième) pour identifier la bonne ligne. Je dois également transmettre ces informations de la sous-requête à la requête principale qui fournira les données pour les autres colonnes des lignes appropriées. Voici la requête modifiée que j'ai réussi à faire fonctionner :
SELECT b.time_stamp,b.lives_remaining,b.usr_id,b.trans_id FROM
(SELECT usr_id, max(time_stamp || '*' || trans_id)
AS max_timestamp_transid
FROM lives GROUP BY usr_id ORDER BY usr_id) a
JOIN lives b ON a.max_timestamp_transid = b.time_stamp || '*' || b.trans_id
ORDER BY b.usr_id
Bon, ça marche, mais je n'aime pas ça. Elle nécessite une requête dans une requête, une auto-jonction, et il me semble que cela pourrait être beaucoup plus simple en saisissant la ligne que MAX trouve avoir le plus grand timestamp et trans_id. La table "lives" contient des dizaines de millions de lignes à analyser, j'aimerais donc que cette requête soit aussi rapide et efficace que possible. Je suis nouveau dans le RDBM et Postgres en particulier, je sais donc que je dois utiliser efficacement les index appropriés. Je suis un peu perdu sur la façon d'optimiser.
J'ai trouvé une discussion similaire aquí . Puis-je exécuter un type d'équivalent Postgres d'une fonction analytique Oracle ?
Tout conseil sur l'accès aux informations des colonnes connexes utilisées par une fonction d'agrégation (comme MAX), la création d'index et la création de meilleures requêtes serait très apprécié !
P.S. Vous pouvez utiliser les éléments suivants pour créer mon cas d'exemple :
create TABLE lives (time_stamp timestamp, lives_remaining integer,
usr_id integer, trans_id integer);
insert into lives values ('2000-01-01 07:00', 1, 1, 1);
insert into lives values ('2000-01-01 09:00', 4, 2, 2);
insert into lives values ('2000-01-01 10:00', 2, 3, 3);
insert into lives values ('2000-01-01 10:00', 1, 2, 4);
insert into lives values ('2000-01-01 11:00', 4, 1, 5);
insert into lives values ('2000-01-01 11:00', 3, 1, 6);
insert into lives values ('2000-01-01 13:00', 3, 3, 1);
0 votes
Josh, vous n'aimez peut-être pas le fait que la requête s'auto-joint, etc., mais cela ne pose aucun problème au SGBDR.
1 votes
L'auto-jonction se traduira en fait par un simple mappage d'index, où le SELECT interne (celui avec MAX) parcourt l'index en éliminant les entrées non pertinentes, et où le SELECT externe se contente de saisir le reste des colonnes de la table correspondant à l'index réduit.
0 votes
Vlad, merci pour les conseils et les explications. Cela m'a ouvert les yeux sur la façon de commencer à comprendre le fonctionnement interne de la base de données et d'optimiser les requêtes. Quassnoi, merci pour l'excellente requête et le conseil sur la clé primaire ; Bill aussi. Bill aussi. Très utile.
0 votes
Merci de m'avoir montré comment obtenir un
MAX
BY
2 colonnes !0 votes
Duplicata possible de Récupérer la ligne qui a la valeur maximale pour une colonne.