Подзапрос, чтобы вернуть последнюю запись для каждого родительского идентификатора
Вопрос
У меня есть родительская таблица с записями для документов, и у меня есть таблица истории, которая регистрирует запись аудита каждый раз, когда пользователь обращается к одному из документов.
Я пишу поисковый запрос, чтобы получить список документов (отфильтрованных по различным критериям) с последним идентификатором пользователя для доступа к каждому документу, возвращенному в наборе результатов.
Таким образом, для
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
Я бы хотел получить ответ от моего поиска, например
ID | NAME | LAST_USER_ID
1 | Document 1 | 12345
2 | Document 2 | 11111
3 | Document 3 | 12345
4 | Document 4 |
5 | Document 5 |
Могу ли я легко сделать это с помощью одного SQL-запроса и объединения двух таблиц?
Решение
Пересмотр произведений Энди Уайта и замена квадратных скобок (нотация MS SQL Server) на DB2 (и SQL стандарта ISO) «идентификаторы с разделителями»:
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
Я не совсем уверен, является ли " временная метка " или "TIMESTAMP" правильно - вероятно, последнее.
Преимущество этого состоит в том, что он заменяет внутренний коррелированный подзапрос в версии Энди более простым некоррелированным подзапросом, который потенциально может быть (радикально?) более эффективным.
Другие советы
Я не смог получить " HAVING MAX (TIMESTAMP) " для запуска в SQL Server - я полагаю, что требуется логическое выражение, такое как "max (TIMESTAMP)" > 2009-03-05 & Quot; или что-то, что не применимо в этом случае. (Возможно, я что-то не так делаю ...)
Вот кое-что, что, кажется, работает - обратите внимание, что объединение имеет 2 условия (не уверен, хорошо это или нет):
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
)
Это не использует объединение, но для некоторых запросов, подобных этому, я хотел бы встроить выбор для поля. Если вы хотите уловить ситуацию, когда ни один пользователь не получил доступ, вы можете обернуть его с помощью 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>
Я думаю, что это должно быть примерно так:
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
это также может работать:
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 */