Стандарт SQL выбирает текущие записи из вопроса журнала аудита

Моя память меня подводит. У меня есть простая таблица журнала аудита на основе триггера:

IDint (identity, PK)
CustomerID < /kbd>int
Имяvarchar(255)
Адресvarchar (255)
AuditDateTimedatetime
AuditCodechar(1)


Он содержит следующие данные:

IDCustomerIDИмя АдресAuditDateTimeAuditCode 1123Боб123 Internet Way2009-07-17 13:18:06.353I 2123< kbd>Боб
123 Internet Way2009-07-17 13:19:02.117D 3 123Джерри123 Internet Way2009-07-17 13:36:03.517Я 4123Боб123 Мой отредактированный путь 2009-07-17 13:36:08.050U 5100Арнольд100 SkyNet Way2009-07-17 13:36:18.607Я 6100 Ники100 Star Way2009-07-17 13:36:25.920U 7 < /kbd>110Блонди110 Другой путь2009-07-17 13:36:42.313Я 8113Салли113 Еще один способ2009-07-17 13:36 :57.627I


Каким должен быть эффективный оператор select для получения всех самых последних записей между временем начала и временем окончания? К вашему сведению: I для вставки, D для удаления и U для обновления.

Я что-то упустил в аудите? Таблица? Мой следующий шаг — создать таблицу аудита, которая записывает только изменения, но вы можете извлечь самые последние записи за заданный период времени. На всю жизнь я не могу легко найти его в любой поисковой системе. Ссылки тоже будут работать. Спасибо за помощь.


person Zachary Scott    schedule 17.07.2009    source источник
comment
Мне нравится этот стиль таблицы — хотелось бы, чтобы у нас была такая функция в редакторе.   -  person Jeff Meatball Yang    schedule 18.07.2009
comment
Да, это лучшее, что я видел. Слава доктору З.   -  person mechanical_meat    schedule 18.07.2009
comment
Да, расскажи нам о своей магии...   -  person polyglot    schedule 18.07.2009
comment
Отредактируйте вопрос, чтобы увидеть HTML-разметку за таблицей;)   -  person shahkalpeshp    schedule 18.07.2009
comment
Одна потенциальная проблема заключается в том, что если/когда у вас есть несколько операторов, попадающих в одну и ту же запись так близко по времени, что они имеют одинаковые значения даты и времени. Тип данных SQL Server DATETIME имеет разрешение 1/300 секунды. Таким образом, проблема может возникнуть, если два или более операторов выполняются в течение 3 миллисекунд. Поле идентификатора таблицы аудита всегда увеличивается?   -  person Shannon Severance    schedule 18.07.2009
comment
Неважно, теперь я вижу атрибут идентичности выше. Приведенные ниже решения запросов на основе AuditDateTime будут работать только в том случае, если операторы разделены интервалом › 3,33... миллисекунды. Использование ID может работать лучше.   -  person Shannon Severance    schedule 18.07.2009
comment
Я должен написать файл XSL(t) для вывода этого syle. :)   -  person Zachary Scott    schedule 20.07.2009


Ответы (4)


Другой (лучший?) способ сохранить историю аудита — использовать столбцы «startDate» и «endDate», а не столбцы «auditDateTime» и «AuditCode». Это часто используется при отслеживании изменений типа 2 (новых версий строки) в хранилищах данных.

Это позволяет более непосредственно выбирать текущие строки (WHERE endDate равно NULL), и вам не нужно будет обрабатывать обновления иначе, чем вставки или удаления. У вас просто есть три случая:

  • Вставка: скопируйте всю строку вместе с датой начала и датой окончания NULL.
  • Удалить: установить дату окончания существующей текущей строки (endDate равно NULL)
  • Обновление: выполните Удалить, затем Вставьте

Ваш выбор будет просто:

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

Ссылки

шпаргалка для хранилищ данных , но есть хороший раздел об изменениях типа 2 (что вы хотите отслеживать)

Страница MSDN, посвященная хранилищу данных

person Jeff Meatball Yang    schedule 17.07.2009
comment
Если вы смотрите на версионные данные в целом, вы на 100% правы. При выборе самой последней строки, в которой срок действия самой верхней строки не истек, лучше всего, чтобы все строки с версиями имели значение fromdate/todate, и для такого индекса возможен запрос с возможностью SARG. В противном случае обычно требуется как минимум самообъединение и двойное сканирование (и часто сканирование таблицы с половиной, если требуется получить все максимальные версии строк сущности из таблицы). - person polyglot; 18.07.2009
comment
Первая ссылка на использование столбцов «startDate» и «endDate», а не столбцов «auditDateTime» и «AuditCode», где я могу прочитать об этом? - person Zachary Scott; 20.07.2009
comment
Это техника, заимствованная из хранилищ данных. См. ссылки и найдите Тип 2 в тексте. - person Jeff Meatball Yang; 20.07.2009
comment
ТЫВМ. Я прочитал статью. Я был снежинкой и не знал этого. РЖУ НЕ МОГУ. Во всяком случае, я думаю, что наконец понял ваши комментарии «Вставить», «Обновить» и «Удалить» выше, чтобы применить их к таблице аудита (да, это заняло некоторое время). Очень хорошая идея, потому что я мог бы запустить три транзакции: удалить из того, что существует, и вставить, где не существует, и присоединиться к обновлению. (надеюсь правильно понял). Спасибо еще раз. - person Zachary Scott; 21.07.2009

Хорошо, пара вещей для таблиц журнала аудита.

Для большинства приложений мы хотим, чтобы таблицы аудита вставлялись очень быстро.

Если журнал аудита действительно предназначен для диагностики или по очень нерегулярным причинам аудита, то самый быстрый критерий вставки — сделать таблицу физически упорядоченной во время вставки.

И это означает, что время аудита должно быть указано в первом столбце кластеризованного индекса, например.

create unique clustered index idx_mytable on mytable(AuditDateTime, ID)

Это позволит выполнять чрезвычайно эффективные запросы на выборку при вставках AuditDateTime O(log n) и O(1).

Если вы хотите просмотреть свою таблицу аудита на основе идентификатора клиента, вам придется пойти на компромисс.

Вы можете добавить некластеризованный индекс по (CustomerID, AuditDateTime), что позволит O (log n) искать историю аудита для каждого клиента, однако стоимость обслуживания этого некластеризованного индекса после вставки — это обслуживание будет O ( log n) наоборот.

Однако этот штраф за время вставки может быть предпочтительнее сканирования таблицы (то есть O (n) времени сложности), которое вам придется заплатить, если у вас нет индекса для CustomerID, и это обычный запрос, который выполняется. Поиск O(n), который блокирует таблицу для процесса записи для нерегулярного запроса, может блокировать писатели, поэтому иногда в интересах писателей быть немного медленнее, если это гарантирует, что читатели не будут блокировать свои коммиты, потому что читателям нужно сканировать таблицу из-за отсутствия хорошего индекса для их поддержки....


Дополнение: если вы хотите ограничиться заданным таймфреймом, самое главное, прежде всего, это индекс по AuditDateTime. И сделайте его кластеризованным, когда вы вставляете в порядке AuditDateTime. Это самое большое, что вы можете сделать, чтобы сделать ваш запрос эффективным с самого начала.

Затем, если вы ищете самое последнее обновление для всех CustomerID в течение заданного промежутка времени, после этого требуется полное сканирование данных, ограниченное датой вставки.

Вам нужно будет выполнить подзапрос к вашей таблице аудита между диапазоном,

select CustomerID, max(AuditDateTime) MaxAuditDateTime 
from AuditTrail 
where AuditDateTime >= @begin and Audit DateTime <= @end

а затем включите это в свой собственный запрос выбора, например.

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
person polyglot    schedule 17.07.2009

Другой подход заключается в использовании подвыборки

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;
person nos    schedule 17.07.2009

время начала и окончания? например, между 1:00 и 3:00
или временем начала и окончания? например, с 17.07.2009 13:36 по 18.07.2009 13:36

person MakkyNZ    schedule 17.07.2009
comment
Полное начало даты и времени до конца даты и времени, а не повторение. Мне нужно выбрать все записи до того момента, когда я хочу их обработать, например, в час. Они постоянно меняются, и я хочу обрабатывать до определенного момента, не останавливая их процесс. - person Zachary Scott; 20.07.2009