2014-02-10 4 views
0

Недавно я создал триггер SQL для замены очень дорогого запроса, который я использовал для уменьшения количества обновлений, которые моя база данных делает каждый день.SQL-триггер для таблицы аудита, выходящей из синхронизации

Перед тем, как преформ обновления я проверяю, чтобы узнать, сколько обновлений уже произошли в течение дня, это используется, чтобы сделать с помощью запроса:

SELECT COUNT(*) FROM Movies WHERE DateAdded = Date.Now 

Ну моя база данных более 1 миллиона записей и этот запрос прогоните 1-2k в минуту, чтобы вы могли понять, почему я хотел использовать для этого новый подход.

Итак, я создал таблицу аудита и установил триггер SQL, чтобы обновить эту таблицу, если в таблице Movie произойдут какие-либо INSERT или UPDATE. Однако я замечаю, что таблица аудита выходит из синхронизации на несколько сотен каждый день (количество таблиц аудита выше фактических обновлений в таблице фильмов). Поскольку это не представляет огромной проблемы, мне просто интересно, что может быть причиной этого или как его отладить?

SQL Trigger:

ALTER TRIGGER [dbo].[trg_Audit] 
ON [dbo].[Movies] 
AFTER UPDATE, INSERT 
AS 
BEGIN 
    UPDATE Audit SET [count] = [count] + 1 WHERE [date] = CONVERT (date, GETDATE()) 
    IF @@ROWCOUNT=0 
    INSERT INTO audit ([date], [count]) VALUES (GETDATE(), 1) 
END 

выше триггер происходит только после UPDATE или INSERT на столе Movie и пытается обновить счетчик + 1 в таблице аудита, и если он не существует (IF @ @ ROWCOUNT = 0), то он создает его. Любая помощь приветствуется! Благодарю.

+1

Вы понимаете, что если вы обновите 10 записей сразу, этот триггер только увеличит счет на 1? – OGHaza

+0

@OGHaza, который прекрасен, поскольку я никогда не обновляю или не вставляю больше одной записи за раз, также подсчет таблицы аудита (проблема синхронизации) выше, чем я возвращаюсь, когда я запрашиваю мою таблицу фильмов с WHERE DateAdded = Date.Now ... Это то, что я пытаюсь выяснить. – bfritz

+0

Что означает, что триггер запускается больше, чем фактическое количество обновлений/вставок в базу данных. Я также тестировал, чтобы убедиться, что при создании и #tempTables это не срабатывает, и это было не так. – bfritz

ответ

2

Что-то, как это должно работать:

create table dbo.Movies (
    A int not null, 
    B int not null, 
    DateAdded datetime not null 
) 
go 
create view dbo.audit 
with schemabinding 
as 
    select CONVERT(date,DateAdded) as dt,COUNT_BIG(*) as cnt 
    from dbo.Movies 
    group by CONVERT(date,DateAdded) 
go 
create unique clustered index IX_MovieCounts on dbo.audit (dt) 

Это называется indexed view. Преимущество заключается в том, что SQL Server берет на себя ответственность за сохранение данных, хранящихся в этом представлении, и это всегда правильно.

Если вы не на Enterprise/Developer Edition, вы бы запросить audit вид, используя NOEXPAND подсказку:

SELECT * from audit with (noexpand) 

Это имеет преимущества, которые

а) Вы надеваете» t нужно написать триггеры самостоятельно (у SQL Server действительно есть что-то очень похожее на триггеры за кулисами),

b) Он не может w справиться с многострочными вставками, обновлениями и удалениями и

c) Вам не нужно писать логику, чтобы справиться с обновлением, которое изменяет значение DateAdded.

+0

Эй, Дэмиен, я просто протестировал это в своей среде Dev, и, похоже, он работает и обновляется должным образом, однако я хотел знать, будет ли это больше накладных расходов, чем функция триггера? Как вы видите, мне пришлось запускать некоторые трюки в триггере, чтобы избежать выражения select, чтобы проверить, существовала ли строка до попытки обновления. Поскольку я получаю более 15 миллионов запросов в день, мне нужно убедиться, что это не приведет к резкому увеличению нагрузки на сервер. Когда обновляются эти представления? по требованию? или как триггер после каждой вставки/обновления? – bfritz

+0

@bfritz - они почти точно такие, как если бы вы написали триггер, а правила вокруг того, что разрешено в индексированном представлении, - обеспечить их эффективность - то есть они разработаны, явно, так что сохранение индекса может быть достигнуто на основе * чисто * на строках, на которые влияет конкретный оператор - им не нужно сканировать всю таблицу. –

+0

Просто хотел добавить, что я реализовал это в производство и обнаружил, когда не использовал «WITH (NOEXPAND)», это было очень медленным запросом! Так что еще раз спасибо за это! – bfritz

1

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

UPDATE Audit 
SET [count] = [count] + (SELECT COUNT(*) FROM INSERTED) 
WHERE [date] = CONVERT (date, GETDATE()) 
IF @@ROWCOUNT=0 
INSERT INTO audit ([date], [count]) 
VALUES (GETDATE(), (SELECT COUNT(*) FROM INSERTED)) 
+0

Я не вижу ничего плохого, так что пока вы не попробовали, я могу только предположить, что это так. Этот триггер, похоже, отлично работает на SQLFiddle. – OGHaza