2010-01-26 5 views
5

OK. Я делаю обновление в одной строке таблицы. Все поля будут перезаписаны новыми данными, кроме первичного ключа. Однако не все значения изменят b/c обновления. Например, если моя таблица выглядит следующим образом:T-SQL: какие COLUMNS изменились после обновления?

TABLE (id int ident, foo varchar(50), bar varchar(50)) 

Начальное значение:

id foo bar 
----------------- 
1 hi there 

Я тогда выполнить UPDATE tbl SET foo = 'hi', bar = 'something else' WHERE id = 1

То, что я хочу знать, какой столбец имеет свою ценность изменена и какова ее первоначальная ценность и какова ее новая ценность.

В приведенном выше примере я хотел бы видеть, что столбец «бар» был изменен с «там» на «что-то еще».

Возможно без сравнения столбцов по столбцам? Есть ли какой-то элегантный SQL-оператор, такой как EXCEPT, который будет более мелкозернистым, чем просто строка?

Спасибо.

ответ

9

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

DECLARE @Updates TABLE 
(
    OldFoo varchar(50), 
    NewFoo varchar(50), 
    OldBar varchar(50), 
    NewBar varchar(50) 
) 

UPDATE FooBars 
SET <some_columns> = <some_values> 
OUTPUT deleted.foo, inserted.foo, deleted.bar, inserted.bar INTO @Updates 
WHERE <some_conditions> 

SELECT * 
FROM @Updates 
WHERE OldFoo != NewFoo 
OR OldBar != NewBar 

Если вы пытаетесь на самом деле сделать что-то, как в результате этих изменений, то лучше написать триггер:

CREATE TRIGGER tr_FooBars_Update 
ON FooBars 
FOR UPDATE AS 
BEGIN 
    IF UPDATE(foo) OR UPDATE(bar) 
     INSERT FooBarChanges (OldFoo, NewFoo, OldBar, NewBar) 
      SELECT d.foo, i.foo, d.bar, i.bar 
      FROM inserted i 
      INNER JOIN deleted d 
       ON i.id = d.id 
      WHERE d.foo <> i.foo 
      OR d.bar <> i.bar 
END 

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

Вы можете использовать COLUMNS_UPDATED вместо UPDATE, но я считаю, что это будет боль, и она до сих пор не покажет вам, какие столбцы фактически изменили, только столбцы, которые были включены в UPDATE заявления. Так, например, вы можете написать UPDATE MyTable SET Col1 = Col1, и он все равно скажет вам, что Col1 был обновлен, хотя ни одно значение фактически не изменилось. При написании триггера вам нужно действительно проверить индивидуальные значения до и после, чтобы убедиться, что вы получаете реальные изменения (если это то, что вы хотите).

P.S. Вы также можете указать UNPIVOT, как говорит Роб, но вам все равно нужно явно указывать столбцы в предложении UNPIVOT, это не волшебство.

+0

Обычно принято оставлять заметку, когда вы используете downvote, ребята. Не будьте рывком, что именно не так с этим ответом? – Aaronaught

+0

Спасибо за информацию. Я искал специальное заявление, но, если его нет, я думаю, мне придется переделать его, как вы это делали выше. – Calvin

2

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

+0

Не могли бы вы привести пример, используя случай выше? – Calvin

+0

Да, но я нахожусь на своем iPhone прямо сейчас ... Немного позже, конечно. –

+1

/* К сожалению о форматировании, надеется, что он работает нормально */ создать триггер uBlah на dbo.blah после обновления как выбери д. *, I.value из ( выберите идентификатора, собр, значение из удаленных UNPIVOT (значение окры в ([Foo], [бар])) у ) д присоединиться к ( выберите идентификатор, столбец, значение от вставленной UNPIVOT (значения для седловины в ([Foo], [бар])) u ) i по d.id = i.id и d.col = i.col и d.value! = i.value ; –

0
OUTPUT deleted.bar AS [OLD VALUE], inserted.bar AS [NEW VALUE] 

@Calvin Я просто основывался на примере UPDATE. Я не говорю, что это полное решение. Я намекая, что вы могли бы сделать это где-то в вашем коде ;-)

Поскольку я уже получил -1 из приведенного выше ответа, позвольте мне передать это:

Если вы не знаете, который Колонок был обновлен, я бы сказал, что создать триггер и использовать COLUMNS_UPDATED функции() в теле этого триггера (см this)

Я создал в своем блоге Bitmask Reference для использования с этим COLUMNS_UPDATED(). Это сделает вашу жизнь проще, если вы решили идти по этому пути (Trigger + COLUMNS_UPDATED())

Если вы не знакомы с триггером, вот мой пример основного триггера http://dbalink.wordpress.com/2008/06/20/how-to-sql-server-trigger-101/

+0

Проблема с этим решением заключается в том, что я не знаю, что «бар» - это поле, которое изменилось. Спасибо хоть. – Calvin

+0

@Calvin См. Мои правки выше ;-) – MarlonRibunal

+1

Итак, я думаю, что единственный вопрос, который у меня есть для этого: Стоит ли COLUMNS_UPDATED() с соответствующей битовой маской силы (2, fieldnum-1) на самом деле сказать мне, изменилось ли значение или только если, как говорит Аароноут, было ли поле включено в оператор UPDATE? – Calvin

1

Вы можете обнаружить это в Trigger, или использовать CDC в SQL Server 2008.

Если вы create a triggerFOR AFTER UPDATE то inserted таблицы будет содержать строки с новыми значениями, и deleted таблицы будет содержать соответствующие строки со старыми значениями.

+0

Использование SQL Server 2005. Как именно я обнаружил бы это в триггере? – Calvin

+0

См. Мой ответ ;-) – MarlonRibunal

0

Если вы используете SQL Server 2008, вам следует, вероятно, взглянуть на новую функцию «Захват данных данных». Это сделает то, что вы хотите.

+0

CDC действительно не подходит для этого случая использования. – keithwarren7

+0

И я использую SQL Server 2005. – Calvin

0

Альтернативный вариант для отслеживания изменений данных - это запись данных в другую (возможную временную) таблицу, а затем анализ различий с использованием XML. Измененные данные записываются в таблицу аудита вместе с именами столбцов. Только одно: вам нужно знать поля таблицы для подготовки временной таблицы. Вы можете найти это решение здесь:

Смежные вопросы