Страницы

суббота, 22 августа 2015 г.

Поиск обновленных столбцов

Если в соединении выполняется операция update, то можно написать триггер, в теле которого можно воспользоваться функциями update или columns_updated для проверки того какие конкретно столбцы были обновлены. Наиболее удобной функций является columns_updated, так как она возвращает битовую маску, где каждый бит соответствует номеру обновленного столбца.
Что бы правильно пользоваться этой функцией, нужно знать два факта.
Во-первых, байты, образующие биты, идут справа налево. Во-вторых, функция возвращает строку varbinary. И в ней первый справа байт описывает биты для первых восьми столбцов, второй правый байт показывает биты для столбцов с 9-ого по 16-ый и так далее.
Число байт, которое вернет columns_updated, равно отношению числа столбцов к 8 (плюс 1, если число столбцов не кратно 8). Для определения списка номеров столбцов в самом общем случае, независимо от числа столбцов, можно использовать такой запрос:

declare @varByteCols varbinary ( max ) = columns_updated ()
;
with Cols
as
(
       select iCol
       from
       (
             select 1 as iCol
                    union all
             select 2
                    union all
             select 4
                    union all
             select 8
                    union all
             select 16
                    union all
             select 32
                    union all
             select 64
                    union all
             select 128
       ) Cols
), Bytes
as
(
       select 1 as iByteNum
             union all
       select iByteNum + 1
       from Bytes
       where iByteNum + 1 <= len ( @varByteCols )
)
select iColNum = ( Bytes.iByteNum - 1 ) * 8 + log ( Cols.iCol, 2 ) + 1
from
       Bytes
             inner join
       Cols on substring ( @varByteCols, Bytes.iByteNum, 1 ) & Cols.iCol <> 0

Для удобства можно добавить соединение на sys.columns чтобы сразу определить имена обновленных столбцов. Также инкапсулируем все в табличную функцию:

if object_id ( N'dbo.GetUpdCols', N'IF' ) is null
       exec ( N'create function dbo.GetUpdCols ( @i int ) returns table as return ( select 1 as i )' )
go

alter function dbo.GetUpdCols ( @varByteCols varbinary ( max ), @vcTabName varchar ( 400 ) )
       returns table
as
return
(
       with Cols
       as
       (
             select iCol
             from
             (
                    select 1 as iCol
                           union all
                    select 2
                           union all
                    select 4
                           union all
                    select 8
                           union all
                    select 16
                           union all
                    select 32
                           union all
                    select 64
                           union all
                    select 128
             ) Cols
       ), Bytes
       as
       (
             select 1 as iByteNum
                    union all
             select iByteNum + 1
             from Bytes
             where iByteNum + 1 <= len ( @varByteCols )
       )
       select ColNums.name
       from
             Bytes
                    inner join
             Cols on substring ( @varByteCols, Bytes.iByteNum, 1 ) & Cols.iCol <> 0
                    inner join
             (
                    select name, iColNum = row_number () over ( order by column_id asc )
                    from sys.columns
                    where object_id = object_id ( @vcTabName, N'U' )
             ) ColNums on ColNums.iColNum = ( Bytes.iByteNum - 1 ) * 8 + log ( Cols.iCol, 2 ) + 1
)
go

В последнем подзапросе используется row_number для получения номеров столбцов. Это важно, так как column_id могут не идти последовательно (например, если ранее был создан столбец, затем удален, а затем появились еще новые столбцы). При использовании данной функции следует лишь удостовериться что пользователь, выполняющий update, сможет получить доступ к sys.columns. Если такого доступа нет, то можно создавать функцию с опцией exec as, чтобы она выполнялась от имени пользователя, имеющего соответствующие права на просмотр метаданных.

Комментариев нет:

Отправить комментарий