Стандарт SQL выберите текущие записи из вопроса журнала аудита
Вопрос
Моя память подводит меня.У меня есть простая таблица журнала аудита, основанная на триггере:
ID int (идентификатор, PK)
Идентификатор клиента инт
Имя переменный код (255)
Адрес переменный код (255)
Дата проверки: Время дата - время
Код проверки символ (1)
В нем есть такие данные, как это:
ID Идентификатор клиента Имя Адрес Дата проверки: Время Код проверки
1 123 Боб 123 Интернет-способ 2009-07-17 13:18:06.353Я
2 123 Боб 123 Интернет-способ 2009-07-17 13:19:02.117D
3 123 Джерри 123 Интернет-способ 2009-07-17 13:36:03.517Я
4 123 Боб 123 Мой Отредактированный Способ 2009-07-17 13:36:08.050U
5 100 Арнольд 100 Способ Скайнета 2009-07-17 13:36:18.607Я
6 100 Ники 100 Звездный Путь 2009-07-17 13:36:25.920U
7 110 Блондиночка 110 Другой Способ 2009-07-17 13:36:42.313Я
8 113 Салли 113 Еще один Способ 2009-07-17 13:36:57.627Я
Каким был бы эффективный оператор select для получения всех самых последних записей между временем начала и окончания? К ТВОЕМУ СВЕДЕНИЮ:I для вставки, D для удаления и U для обновления.
Я что-нибудь упускаю в таблице аудита?Мой следующий шаг - создать таблицу аудита, в которую записываются только изменения, но вы можете извлечь самые последние записи за данный период времени.Хоть убейте, я не могу легко найти его ни в одной поисковой системе.Ссылки тоже будут работать.Спасибо за помощь.
Решение
Другой (лучший?) способ ведения истории аудита - использовать столбцы 'StartDate' и 'EndDate', а не столбцы auditDateTime и AuditCode.Такой подход часто используется при отслеживании изменений типа 2 (новых версий строки) в хранилищах данных.
Это позволяет вам более непосредственно выбирать текущие строки (ГДЕ конечная дата равна НУЛЮ), и вам не нужно будет обрабатывать обновления иначе, чем вставки или удаления.У вас просто есть три случая:
- Вставить:скопируйте полную строку вместе с начальной датой и НУЛЕВОЙ конечной датой
- Удалить:установите конечную дату существующей текущей строки (конечная дата равна НУЛЮ).
- Обновить:выполните Удаление, затем Вставьте
Ваш выбор был бы просто:
select * from AuditTable where endDate is NULL
В любом случае, вот мой запрос к вашей существующей схеме:
declare @from datetime
declare @to datetime
select b.* from (
select
customerId
max(auditdatetime) 'auditDateTime'
from
AuditTable
where
auditcode in ('I', 'U')
and auditdatetime between @from and @to
group by customerId
having
/* rely on "current" being defined as INSERTS > DELETES */
sum(case when auditcode = 'I' then 1 else 0 end) >
sum(case when auditcode = 'D' then 1 else 0 end)
) a
cross apply(
select top 1 customerId, name, address, auditdateTime
from AuditTable
where auditdatetime = a.auditdatetime and customerId = a.customerId
) b
Ссылки
A сводная таблица для хранилищ данных, но имеет хороший раздел об изменениях типа 2 (то, что вы хотите отслеживать)
Страница MSDN на хранилище данных
Другие советы
Хорошо, пара вещей для таблиц журнала аудита.
Для большинства приложений мы хотим, чтобы таблицы аудита были чрезвычайно быстрыми при вставке.
Если журнал аудита действительно предназначен для диагностики или по очень нерегулярным причинам аудита, то самый быстрый критерий вставки - сделать таблицу физически упорядоченной во время вставки.
И это означает поместить время аудита в качестве первого столбца кластеризованного индекса, например
create unique clustered index idx_mytable on mytable(AuditDateTime, ID)
Это позволит выполнять чрезвычайно эффективные запросы select при вставках AuditDateTime O(log n) и O(1).
Если вы хотите просмотреть свою таблицу аудита для каждого идентификатора клиента, то вам нужно будет пойти на компромисс.
Вы можете добавить некластеризованный индекс при (CustomerID, AuditDateTime), что позволит выполнять O (log n) просмотр истории аудита каждого клиента, однако стоимость будет заключаться в обслуживании этого некластеризованного индекса при вставке - это обслуживание будет O (log n) наоборот.
Однако это ограничение по времени вставки может быть предпочтительнее сканирования таблицы (то есть затрат на O (n) трудоемкость), которые вам нужно будет оплатить, если у вас нет индекса по CustomerID и выполняется обычный запрос.Поиск O (n), который блокирует таблицу в процессе записи для нерегулярного запроса, может заблокировать авторов, поэтому иногда в интересах авторов работать немного медленнее, если это гарантирует, что читатели не будут блокировать свои коммиты, потому что читателям необходимо сканировать таблицу из-за отсутствия хорошего индекса для их поддержки....
Дополнение:если вы хотите ограничить себя заданным таймфреймом, то в первую очередь самое важное - это индекс при AuditDateTime.И сделайте его кластеризованным при вставке в порядке AuditDateTime.Это самое большое, что вы можете сделать, чтобы сделать ваш запрос эффективным с самого начала.
Далее, если вы ищете самое последнее обновление для всех идентификаторов клиентов в течение заданного промежутка времени, то после этого требуется полное сканирование данных, ограниченное датой вставки.
Вам нужно будет выполнить подзапрос к вашей таблице аудита, между диапазоном,
select CustomerID, max(AuditDateTime) MaxAuditDateTime
from AuditTrail
where AuditDateTime >= @begin and Audit DateTime <= @end
а затем включите это в свой запрос select, например.
select AuditTrail.* from AuditTrail
inner join
(select CustomerID, max(AuditDateTime) MaxAuditDateTime
from AuditTrail
where AuditDateTime >= @begin and Audit DateTime <= @end
) filtration
on filtration.CustomerID = AuditTrail.CustomerID and
filtration.AuditDateTime = AuditTrail.AuditDateTime
Другой подход заключается в использовании вложенного выбора
select a.ID
, a.CustomerID
, a.Name
, a.Address
, a.AuditDateTime
, a.AuditCode
from myauditlogtable a,
(select s.id as maxid,max(s.AuditDateTime)
from myauditlogtable as s
group by maxid)
as subq
where subq.maxid=a.id;
время начала и окончания?например, между 1 и 3 часами ночи
или дата начала и время окончания?например, с 2009-07-17 13:36 по 2009-07-18 13:36