Проверить наличие изменений в таблице SQL Server?

144

Как я могу отслеживать изменения в таблице в базе данных SQL Server, не используя триггеры или каким-либо образом изменяя структуру базы данных? Я предпочитаю среду программирования .NET и C #.

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

Под «изменениями в таблице» я подразумеваю изменения данных таблицы, а не изменения структуры таблицы.

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


С учетом моих требований (отсутствие триггеров или модификации схемы, SQL Server 2000 и 2005) лучшим вариантом действий, по-видимому, является использование BINARY_CHECKSUMфункции в T-SQL . Я планирую реализовать следующее:

Каждые X секунд запускайте следующий запрос:

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*))
FROM sample_table
WITH (NOLOCK);

И сравните это с сохраненным значением. Если значение изменилось, просмотрите таблицу строка за строкой, используя запрос:

SELECT row_id, BINARY_CHECKSUM(*)
FROM sample_table
WITH (NOLOCK);

И сравните возвращенные контрольные суммы с сохраненными значениями.

TimM
источник
3
Они ведь случайно не поместили в свои строки метку времени последнего изменения, не так ли?
zmbq 03
Для записи, если поддерживается версия SQL Server 2005 или новее. Я рассмотрю функцию Service Broker в SQL Server.
Марко Гиньяр

Ответы:

98

Взгляните на команду CHECKSUM:

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM sample_table WITH (NOLOCK);

Это будет возвращать одно и то же число при каждом запуске, пока содержимое таблицы не изменилось. См. Мой пост об этом для получения дополнительной информации:

КОНТРОЛЬНАЯ СУММА

Вот как я использовал его для восстановления зависимостей кеша при изменении таблиц:
Зависимость кеша базы данных ASP.NET 1.1 (без триггеров)

Джон Гэллоуэй
источник
2
Контрольные суммы могут потерпеть неудачу, и в конечном итоге не получится. Если ваша система допускает, что два разных набора данных приведут к одной и той же контрольной сумме, все в порядке. По этой причине мне пришлось отказаться от контрольных сумм в большинстве наших систем ...
LPains
@LPains, не могли бы вы подробнее рассказать о своем заявлении?
petrosmm
1
@petrosmm Я не уверен, что конкретно вы хотите, чтобы я уточнил, но я попробую. Представьте, что у вас есть таблица с несколькими сотнями записей, вы, по сути, генерируете целое число в качестве контрольной суммы, как часто это будет конфликтовать? В моем случае я делал это примерно с 10 таблицами, каждая из которых содержала сотни записей. У меня было хотя бы одно столкновение в день. Отметьте этот другой ответ stackoverflow.com/questions/14450415/…
LPains
31

К сожалению, CHECKSUM не всегда работает должным образом для обнаружения изменений .

Это только примитивная контрольная сумма и не вычисление циклического контроля избыточности (CRC).

Следовательно, вы не можете использовать его для обнаружения всех изменений, например, симметричные изменения приводят к одной и той же КОНТРОЛЬНОЙ СУММЕ!

E. g. решение с CHECKSUM_AGG(BINARY_CHECKSUM(*))всегда будет давать 0 для всех 3 таблиц с разным содержимым:


SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM 
(
  SELECT 1 as numA, 1 as numB
  UNION ALL
  SELECT 1 as numA, 1 as numB
)  q
-- delivers 0!

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM ( SELECT 1 as numA, 2 as numB UNION ALL SELECT 1 as numA, 2 as numB ) q -- delivers 0!

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM ( SELECT 0 as numA, 0 as numB UNION ALL SELECT 0 as numA, 0 as numB ) q -- delivers 0!

BitLauncher
источник
5
На самом деле это не ответ, это «ваше предложение не работает».
kristianp
1
Это можно исправить для дублирующихся данных, используя ключевое слово DISINCT перед BINARY_CHECKSUM. Есть обсудили несколько других подводных камней здесь , но не совсем общие сценарии.
pblack
26

Почему вы не хотите использовать триггеры? Они хороши, если их правильно использовать. Если вы используете их как способ обеспечения ссылочной целостности, тогда они переходят от хороших к плохим. Но если вы используете их для мониторинга, на самом деле они не считаются табу.

Ник Берарди
источник
21

Как часто вам нужно проверять наличие изменений и насколько велики (с точки зрения размера строки) таблицы в базе данных? Если вы воспользуетесь CHECKSUM_AGG(BINARY_CHECKSUM(*))методом, предложенным Джоном, он просканирует каждую строку указанной таблицы. NOLOCKПодсказка помогает, но на большой базе данных, вы все еще ударять каждую строку. Вам также нужно будет сохранить контрольную сумму для каждой строки, чтобы вы знали, что она изменилась.

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

Они могли бы реализовать API, который предоставляет механизм для уведомления дополнительных приложений об изменении данных. Это может быть так же просто, как запись в таблицу уведомлений, в которой указано, какая таблица и какая строка были изменены. Это может быть реализовано с помощью триггеров или кода приложения. С вашей стороны, это не имело бы значения, ваша единственная забота - это периодическое сканирование таблицы уведомлений. Падение производительности базы данных будет намного меньше, чем сканирование каждой строки на предмет изменений.

Сложнее всего убедить поставщика приложения реализовать эту функцию. Поскольку это может быть выполнено полностью через SQL с помощью триггеров, вы можете выполнить большую часть работы за них, написав и протестировав триггеры, а затем передав код поставщику приложения. Если поставщик поддерживает триггеры, это предотвращает ситуацию, когда добавляемый вами триггер непреднамеренно заменяет триггер, предоставленный поставщиком.

Крис Миллер
источник
19

К сожалению, я не думаю, что в SQL2000 есть простой способ сделать это. Если вы сузите свои требования до SQL Server 2005 (и более поздних версий), тогда вы в деле. Вы можете использовать SQLDependencyкласс в System.Data.SqlClient. См. Уведомления о запросах в SQL Server (ADO.NET) .

Кэйден
источник
17

Иметь задание DTS (или задание, запускаемое службой Windows), которое выполняется с заданным интервалом. Каждый раз при запуске он получает информацию о данной таблице с помощью системных таблиц INFORMATION_SCHEMA и записывает эти данные в репозиторий данных. Сравните возвращенные данные о структуре таблицы с данными, возвращенными в предыдущий раз. Если он другой, значит, вы знаете, что структура изменилась.

Пример запроса для возврата информации обо всех столбцах в таблице ABC (в идеале перечисление только столбцов из таблицы INFORMATION_SCHEMA, которые вы хотите, вместо использования * select **, как я здесь):

select * from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'ABC'

Вы будете отслеживать разные столбцы и представления INFORMATION_SCHEMA в зависимости от того, как именно вы определяете «изменения в таблице».

Яаков Эллис
источник
2
Речь идет об изменениях в данных таблицы, а информация_schema содержит схему (определения столбцов) таблицы.
too
14

Здесь дикая догадка: если вы не хотите изменять таблицы сторонних разработчиков, можете ли вы создать представление, а затем установить для него триггер?

Орион Эдвардс
источник
7

Проверьте дату последней фиксации. Каждая база данных имеет историю, когда была сделана каждая фиксация. Я считаю, что это стандарт соответствия ACID.

ЕЭК
источник
2
Предоставьте документированный способ переноса этой информации в таблицу в SQL Server
Мартин Смит,