joindre la dernière des diverses balises usermetadata aux lignes utilisateur

StackOverflow https://stackoverflow.com/questions/25224

  •  09-06-2019
  •  | 
  •  

Question

J'ai une base de données Postgres avec une table utilisateur (ID utilisateur, prénom, nom) et une table de métadonnées utilisateur (ID utilisateur, code, contenu, date/heure de création).Je stocke diverses informations sur chaque utilisateur dans la table usermetadata par code et conserve un historique complet.Ainsi, par exemple, un utilisateur (ID utilisateur 15) possède les métadonnées suivantes :

15, 'QHS', '20', '2008-08-24 13:36:33.465567-04'  
15, 'QHE', '8', '2008-08-24 12:07:08.660519-04'  
15, 'QHS', '21', '2008-08-24 09:44:44.39354-04'  
15, 'QHE', '10', '2008-08-24 08:47:57.672058-04'  

Je dois récupérer une liste de tous mes utilisateurs et la valeur la plus récente de chacun des différents codes de métadonnées utilisateur.Je l'ai fait par programme et c'était, bien sûr, terriblement lent.Le mieux que j'ai pu trouver pour le faire en SQL était de joindre des sous-sélections, qui étaient également lentes et je devais en faire une pour chaque code.

Était-ce utile?

La solution

Je suppose que vous n'êtes pas disposé à modifier votre schéma, donc j'ai peur que ma réponse ne soit pas d'une grande aide, mais voilà...

Une solution possible serait de laisser le champ d'heure vide jusqu'à ce qu'il soit remplacé par une valeur plus récente, lorsque vous insérez la « date de dépréciation » à la place.Une autre façon consiste à étendre le tableau avec une colonne « active », mais cela introduirait une certaine redondance.

La solution classique serait d'avoir à la fois des champs « Valid-From » et « Valid-To » où les champs « Valid-To » sont vides jusqu'à ce qu'une autre entrée devienne valide.Cela peut être géré facilement en utilisant des déclencheurs ou similaires.L'utilisation de contraintes pour s'assurer qu'il n'y a qu'un seul élément de chaque type valide garantira l'intégrité des données.

Leur point commun est qu’il existe une manière unique de déterminer l’ensemble des champs actuels.Vous sélectionneriez simplement toutes les entrées avec l'utilisateur actif et un « Valid-To » ou une « date de dépréciation » NULL ou un vrai « actif ».

Vous pourriez être intéressé à jeter un œil à l'entrée Wikipédia sur bases de données temporelles et l'article Un glossaire consensuel des concepts de bases de données temporelles.

Autres conseils

Ce n'est en fait pas si difficile à faire dans PostgreSQL car il a le "DISTINCT SUR" clause dans sa syntaxe SELECT (DISTINCT ON n'est pas du SQL standard).

SELECT DISTINCT ON (code) code, content, createtime
FROM metatable
WHERE userid = 15
ORDER BY code, createtime DESC;

Cela limitera les résultats renvoyés au premier résultat par code unique, et si vous triez les résultats par heure de création décroissante, vous obtiendrez le plus récent de chacun.

Une sous-sélection est la manière standard de faire ce genre de chose.Vous avez juste besoin d'une contrainte unique sur l'ID utilisateur, le code et la date - et vous pouvez ensuite exécuter ce qui suit :

SELECT * 
FROM Table
JOIN (
   SELECT UserId, Code, MAX(Date) as LastDate
   FROM Table
   GROUP BY UserId, Code
) as Latest ON
   Table.UserId = Latest.UserId
   AND Table.Code = Latest.Code
   AND Table.Date = Latest.Date
WHERE
   UserId = @userId
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top