2009-03-09 4 views
3

J'ai une table parent avec des entrées pour les documents et j'ai une table d'historique qui enregistre une entrée d'audit chaque fois qu'un utilisateur accède à l'un des documents.Sous-requête pour retourner la dernière entrée pour chaque ID parent

J'écris une requête de recherche pour retourner une liste de documents (filtrés par divers critères) avec le dernier identifiant d'utilisateur pour accéder à chaque document retourné dans le jeu de résultats.

Ainsi, pour


    DOCUMENTS 
    ID | NAME 
    1 | Document 1 
    2 | Document 2 
    3 | Document 3 
    4 | Document 4 
    5 | Document 5 

    HISTORY 
    DOC_ID | USER_ID | TIMESTAMP 
    1  | 12345 | TODAY 
    1  | 11111 | IN THE PAST 
    1  | 11111 | IN THE PAST 
    1  | 12345 | IN THE PAST 
    2  | 11111 | TODAY 
    2  | 12345 | IN THE PAST 
    3  | 12345 | IN THE PAST 

Je serais à la recherche d'obtenir un retour de ma recherche comme


    ID | NAME  | LAST_USER_ID 
    1 | Document 1 | 12345 
    2 | Document 2 | 11111 
    3 | Document 3 | 12345 
    4 | Document 4 | 
    5 | Document 5 | 

Puis-je faire facilement cela avec une requête SQL et une jointure entre les deux tables?

Répondre

4

Révise ce que Andy White a produit, et le remplacement des crochets (notation MS SQL Server) avec DB2 (et SQL standard ISO) "identificateurs délimités":

SELECT d.id, d.name, h.last_user_id 
    FROM Documents d LEFT JOIN 
     (SELECT r.doc_id AS id, user_id AS last_user_id 
       FROM History r JOIN 
        (SELECT doc_id, MAX("timestamp") AS "timestamp" 
         FROM History 
         GROUP BY doc_id 
        ) AS l 
        ON r."timestamp" = l."timestamp" 
        AND r.doc_id  = l.doc_id 
     ) AS h 
     ON d.id = h.id 

Je ne suis pas absolument sûr que "timestamp" ou "TIMESTAMP" est correct - probablement le dernier. L'avantage de ceci est qu'il remplace la sous-requête corrélée interne dans la version d'Andy par une sous-requête plus simple non corrélée, qui a le potentiel d'être (radicalement?) Plus efficace.

1

je pense que ce devrait être quelque chose comme ceci:

SELECT ID, Name, b.USER_ID as LAST_USER_ID 
FROM DOCUMENTS a LEFT JOIN 
    (SELECT DOC_ID, USER_ID 
      FROM HISTORY 
       GROUP BY DOC_ID, USER_ID 
       HAVING MAX(TIMESTAMP)) as b 
    ON a.ID = b.DOC_ID 

cela pourrait fonctionner aussi:

SELECT ID, Name, b.USER_ID as LAST_USER_ID 
FROM DOCUMENTS a 
    LEFT JOIN HISTORY b ON a.ID = b.DOC_ID 
GROUP BY DOC_ID, USER_ID 
HAVING MAX(TIMESTAMP) 
+0

Vous auriez besoin d'une LEFT OUTER JOIN pour obtenir les trois dernières lignes de la sortie requise. –

+0

Je ne pouvais pas avoir max (timestamp) pour travailler sur le serveur SQL - cela fonctionne-t-il sur d'autres DB? –

+0

Et vous auriez probablement besoin de: GROUP BY DOC_ID, USER_ID, et vous auriez besoin de réparer la clause HAVING ... –

4

Je ne pouvais pas obtenir le "MAX AVOIR (timestamp)" pour exécuter dans SQL Server - Je suppose avoir besoin d'une expression booléenne comme "avoir max (TIMESTAMP)> 2009-03-05" ou quelque chose, ce qui ne s'applique pas dans ce cas.

select 
    d.ID, 
    d.NAME, 
    h."USER_ID" as "LAST_USER_ID" 
from Documents d 
left join History h 
    on d.ID = h.DOC_ID 
    and h."TIMESTAMP" = 
    (
     select max("TIMESTAMP") 
     from "HISTORY" 
     where "DOC_ID" = d.ID 
    ) 
+0

Je suppose que j'aurais pu utiliser max (TIMESTAMP) dans la requête interne, plutôt que de sélectionner top 1 ... ordre par [TIMESTAMP] desc –

+0

... Correction dans ma réponse –

+0

Et vous devriez perdre les barbarismes MS SQL Server avec carré entre parenthèses - ou remplacez-les par une notation DB2 équivalente "TIMESTAMP". –

3

Ce n »: - (je pourrais faire quelque chose de mal ...)

Voici quelque chose qui semble fonctionner le joint a constate 2 conditions (pas sûr si cela est bon ou pas) J'utilise une jointure, mais pour certaines requêtes comme celle-ci, j'aime bien sélectionner le champ. Si vous voulez attraper la situation quand aucun utilisateur n'a accédé, vous pouvez l'emballer avec une NVL().

select a.ID, a.NAME, 
(select x.user_id 
from HISTORY x 
where x.doc_id = a.id 
    and x.timestamp = (select max(x1.timestamp) 
         from HISTORY x1 
         where x1.doc_id = x.doc_id)) as LAST_USER_ID 
from DOCUMENTS a 
where <your criteria here> 
0
Select ID, Name, User_ID 
From Documents Left Outer Join 
History a on ID = DOC_ID 
Where (TimeStamp = (Select Max(TimeStamp) 
         From History b 
         Where a.DOC_ID = b.DOC_ID) OR 
     TimeStamp Is NULL) /* this accomodates the Left */