Subconsulta para retornar o mais recente entrada para cada ID pai
Pergunta
Eu tenho uma tabela pai com entradas para documentos e eu tenho uma tabela de histórico que registra uma entrada de auditoria cada vez que um usuário acessa um dos documentos.
Eu estou escrevendo uma consulta de pesquisa para retornar uma lista de documentos (filtrado por vários critérios) com a mais recente identificação de usuário para acessar cada documento retornado no conjunto de resultados.
Assim, para
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
eu estaria olhando para obter um retorno de minha pesquisa como
ID | NAME | LAST_USER_ID
1 | Document 1 | 12345
2 | Document 2 | 11111
3 | Document 3 | 12345
4 | Document 4 |
5 | Document 5 |
Posso facilmente fazer isso com uma consulta SQL e uma junção entre as duas tabelas?
Solução
Revisando o que Andy White produziu, e substituição de colchetes (notação MS SQL Server) com o DB2 (e padrão ISO SQL) "identificadores delimitados":
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
Eu não estou absolutamente certo se "timestamp" ou "TIMESTAMP" está correta -., Provavelmente, a última
A vantagem disso é que ele substitui a sub-consulta correlacionada interna na versão de Andy com uma sub-consulta simples não-correlacionados, que tem o potencial para ser (radicalmente?) Mais eficiente.
Outras dicas
Eu não poderia obter o "TENDO MAX (timestamp)" para executar no SQL Server - Eu acho que ter requer uma expressão booleana como "tendo max (timestamp)> 2009-03-05" ou algo assim, o que não faz aplicável neste caso. (Eu poderia estar fazendo algo errado ...)
Aqui está algo que parece funcionar - nota a junção tem 2 condições (não sei se isso é bom ou não):
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
)
Esta não usa uma junção, mas para algumas consultas como este gosto para inline a selecção para o campo. Se você quiser pegar a situação quando nenhum usuário acessou você pode envolvê-lo com um 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>
Eu acho que deveria ser algo como isto:
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
Isso pode funcionar também:
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 )
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 */