mssql cdc update_mask изменения фильтра сделаны только в столбце TS

Я хочу найти строки в моей таблице mssql cdc, в которых изменен только столбец TS.

Итак, я нашел некоторую логику, чтобы проверить, был ли изменен конкретный столбец (это работает), но мне нужно проверить, был ли изменен только столбец TS:

SET @colorder = sys.fn_cdc_get_column_ordinal('dbo_mytable', 'TS')

SELECT case when substring([__$update_mask],len([__$update_mask]) - ((@colorder-1)/8),1) & power(2,(@colorder-1)%8) > 0 then 1 else 0 end

FROM cdc.fn_cdc_get_all_changes_dbo_MYTABLE(@from_lsn, @to_lsn, 'all') PD

person speedone    schedule 16.07.2020    source источник
comment
Какой продукт СУБД вы используете? SQL — это просто язык запросов, используемый всеми реляционными базами данных, а не название конкретного продукта базы данных. Добавьте тег для используемой вами базы данных. Почему я должен помечать свою СУБД   -  person a_horse_with_no_name    schedule 16.07.2020
comment
вопрос больше о логике битовой маски   -  person speedone    schedule 16.07.2020
comment
Это делает используемую СУБД еще более важной   -  person a_horse_with_no_name    schedule 16.07.2020


Ответы (1)


Я давно собирался написать такие функции, спасибо, что дали мне повод сделать это.

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

-- inline tabular function because it's more versatile
-- cross applies used purely for ease of reading, 
-- could just make nested calls but hard to read. Up to you.
-- pass null to flip, otherwise pass explicit value you want the bit to be set to
create function dbo.setbit(@b varbinary(128), @bitpos tinyint, @value bit = null) 
returns table as
return
(
    select      result = cast(result.val as varbinary(128))
    from        (select len(@b) - ((@bitpos - 1) / 8)) bytepos(val)
    cross apply (select substring(@b, bytepos.val, 1)) byte(val)
    cross apply (select power(2, (@bitpos - 1) % 8)) mask(val)
    cross apply (
                   select  cast
                           (
                               case @value
                                   when 1 then byte.val | mask.val
                                   when 0 then byte.val & ~mask.val
                                   else byte.val ^ mask.val
                               end
                               as binary(1)
                           )
                ) newbyte(val)
    cross apply (select stuff(@b, bytepos.val, 1, newbyte.val)) result(val)
);

-- scalar wrapper for tabular function
create function dbo.setbitscalar(@b varbinary(128), @bitpos tinyint, @value bit = null) 
returns varbinary(128) as
begin
    return (select result from dbo.setbit(@b, @bitpos, @value));
end

-- how it works
declare @b varbinary(128) = 0x0101 -- 2 bytes!
select 
    dbo.setbitscalar(@b, 1, 1),        -- set bit 1 to 1
    dbo.setbitscalar(@b, 1, 0),        -- set bit 1 to 0
    dbo.setbitscalar(@b, 1, default)   -- flip bit 1
    
-- how to use it in your case:
-- set the @colorder bit in the mask to zero, 
-- then see if the resulting mask is zero
-- if it is, then only TS changed
SET @colorder = sys.fn_cdc_get_column_ordinal('dbo_mytable', 'TS')

select      only_TS_changed = iif(t.result = 0x, 1, 0) 
from        cdc.fn_cdc_get_all_changes_dbo_MYTABLE(@from_lsn, @to_lsn, 'all') PD
cross apply dbo.setbit(PD.[__$update_mask], @colorder, 0) t
person allmhuran    schedule 17.07.2020
comment
отличное решение моей проблемы, большое спасибо @allmhuran, мне пришлось изменить только одну вещь: only_TS_changed = iif(t.result = 0x00, 1, 0), вместо того, чтобы сравнивать с целым числом, я должен сравнивать с шестнадцатеричными причинами = 0dez каждый раз приводит к истине - person speedone; 17.07.2020
comment
Очень хороший момент. Если сравнивать с литералом 0, он усекает длинный двоичный файл до первых 4 байтов и продвигает его до int, поэтому проверяется только то, пусты ли первые 4 байта. Исправлено. - person allmhuran; 17.07.2020