2008-08-24 5 views
4

J'ai une base de données postgres avec une table d'utilisateur (userid, prénom, nom) et une table usermetadata (userid, code, contenu, datetime créé). Je stocke diverses informations sur chaque utilisateur dans la table usermetadata par code et conserve un historique complet. Ainsi, par exemple, un utilisateur (userid 15) a les métadonnées suivantes:joignant le dernier des différents tags usermetadata aux lignes utilisateur

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 chercher une liste de tous mes utilisateurs et la valeur la plus récente de chacun des différents codes usermetadata. Je l'ai fait par programme et c'était, bien sûr, lenteur. Le mieux que je pouvais trouver pour le faire en SQL était de rejoindre les sous-sélections, qui étaient aussi lentes et je devais en faire une pour chaque code.

Répondre

1

Je suppose que vous n'êtes pas prêt à modifier votre schéma, donc je crains que mon answe pourrait ne pas être d'un grand secours, mais voilà ...

Une solution possible serait d'avoir le champ de temps 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 la table avec une colonne 'active', mais cela introduirait une certaine redondance.

La solution classique serait d'avoir à la fois des champs 'Valide-De' et 'Valide-À' où les champs 'Valide-À' sont vides jusqu'à ce qu'une autre entrée devienne valide. Cela peut être facilement géré en utilisant des déclencheurs ou similaires. L'utilisation de contraintes pour s'assurer qu'un seul élément de chaque type est valide garantira l'intégrité des données. En commun, il existe un moyen unique de déterminer l'ensemble des champs actuels. Vous devez simplement sélectionner toutes les entrées avec l'utilisateur actif et une valeur "Valid-To" ou "date d'obsolescence" NULL ou une valeur "active".

Vous pourriez être intéressé à jeter un oeil à l'entrée de Wikipedia sur temporal databases et l'article A consensus glossary of temporal database concepts.

6

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

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

qui limitera les résultats retournés au premier résultat par code unique, et si vous trier les résultats par la création du temps descendant, vous obtiendrez la plus récente de chacun.

0

Une sous-sélection est la manière standard de faire ce genre de chose. Vous avez juste besoin d'une contrainte unique sur UserId, Code et 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 
Questions connexes