Pregunta

Tengo una tabla principal con entradas para documentos y tengo una tabla de historial que registra una entrada de auditoría cada vez que un usuario accede a uno de los documentos.

Estoy escribiendo una consulta de búsqueda para devolver una lista de documentos (filtrada por varios criterios) con la última identificación de usuario para acceder a cada documento devuelto en el conjunto de resultados.

Así 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

Estaría buscando una devolución de mi búsqueda como


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

¿Puedo hacer esto fácilmente con una consulta SQL y una unión entre las dos tablas?

¿Fue útil?

Solución

Revisando lo que Andy White produjo y reemplazando los corchetes (notación MS SQL Server) con DB2 (y el estándar ISO) 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

No estoy absolutamente seguro de si " marca de tiempo " o " TIMESTAMP " es correcto, probablemente este último.

La ventaja de esto es que reemplaza la subconsulta correlacionada interna en la versión de Andy por una subconsulta no correlacionada más simple, que tiene el potencial de ser (¿radicalmente?) más eficiente.

Otros consejos

No pude obtener el " HAVING MAX (TIMESTAMP) " para ejecutarse en SQL Server: supongo que tener una expresión booleana como " tener max (TIMESTAMP) > 2009-03-05 " O algo así, que no se aplica en este caso. (Podría estar haciendo algo mal ...)

Aquí hay algo que parece funcionar. Tenga en cuenta que la unión tiene 2 condiciones (no estoy seguro de si esto es bueno o no):

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
    )

Esto no usa una combinación, pero para algunas consultas como esta me gusta alinear la selección para el campo. Si desea detectar la situación cuando ningún usuario ha accedido, puede envolverla con una 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>

Creo que debería ser algo como esto:

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

esto podría funcionar también:

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 */
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top