Форум программистов, компьютерный форум, киберфорум
Microsoft SQL Server
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.96/25: Рейтинг темы: голосов - 25, средняя оценка - 4.96
 Аватар для Pavel Rem
11 / 11 / 2
Регистрация: 15.07.2016
Сообщений: 132

Универсальный логирующий триггер

17.10.2019, 16:06. Показов 4807. Ответов 5

Студворк — интернет-сервис помощи студентам
Написал триггер для логирования изменений в таблице.
Триггер записывает изменения в виде xml строки.

Для Insert и Delete записываются все поля.

В случае Update логируются только измененные поля.
Для этого нужно выделить только те ячейки в строке, которые изменились.
Я это делаю с помощью курсора и подготовленного запроса.
T-SQL
1
2
declare ColumnsCursor cursor lockal for
select column_name from iformation_schema.columns where table_name = 'tablename'
T-SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while @@fetch_status=0
        begin 
            set @sql= 
                'select i.itemid as [itemid]
                        ,''' + @columnname + ''' as [сolumn]
                        ,cast(d.' + @columnname + ' as varchar(max)) as [oldvalue]
                        ,cast(i.' + @columnname + ' as varchar(max)) as [newvalue]
                from #inserted i
                    left join #deleted d on i.itemid = d.inemid
                where cast(d.' + @columnname + ' as varchar(max)) != cast(i.' + @columnname + ' as varchar(max))'
 
            insert into @changes exec(@sql);
 
            fetch next from columnscursor into @columnname
        end

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



Весь код триггера:
Кликните здесь для просмотра всего текста

T-SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
alter trigger [dbo].[trigername] on [dbo].[tablename]
after insert,update, delete
as
 
--@@@@@@ insert @@@@@@@
if exists (select top 1 * from inserted) and not exists (select top 1 * from deleted)
begin
insert into [logs].[tablename] ([changes], [type], changedate, userlogin)
  values 
  (
    (select * from inserted for xml path('template')),
    'insert',
    getdate(),
    (select top 1 modifylogin from inserted)
  )
end
 
--@@@@@@ update @@@@@@@
if exists (select top 1 * from inserted)
    begin
        declare @columnname nvarchar(255)
        declare @sql nvarchar(500)
        declare @result xml
        declare @login varchar(50)
        declare @changes table 
        (
            [itemid] int,
            [сolumn] varchar(max), 
            [oldvalue] varchar(max), 
            [newvalue] varchar(max)
        )
 
         declare columnscursor cursor local for
            select column_name from information_schema.columns where table_name = 'tablename'
 
        open columnscursor
        fetch next from columnscursor into @columnname
 
        if object_id('tempdb..#inserted') is not null drop table #inserted
        if object_id('tempdb..#deleted') is not null drop table #deleted
    
        select * into #inserted from inserted
        select * into #deleted from deleted
 
        select top 1 @login = modifylogin from #inserted;
 
        while @@fetch_status=0
        begin 
            set @sql= 
                'select i.itemid as [itemid]
                        ,''' + @columnname + ''' as [сolumn]
                        ,cast(d.' + @columnname + ' as varchar(max)) as [oldvalue]
                        ,cast(i.' + @columnname + ' as varchar(max)) as [newvalue]
                from #inserted i
                    left join #deleted d on i.itemid = d.inemid
                where cast(d.' + @columnname + ' as varchar(max)) != cast(i.' + @columnname + ' as varchar(max))'
 
            insert into @changes exec(@sql);
 
            fetch next from columnscursor into @columnname
        end
 
        if object_id('tempdb..#inserted') is not null drop table #inserted
        if object_id('tempdb..#deleted') is not null drop table #deleted
 
        close columnscursor
        deallocate columnscursor
 
        set @result = (select * from @changes for xml path('row'));
        if @result is not null
        begin
            insert into [logs].[tablename] ([changes], [type], changedate, userlogin)
                        values  (@result, 'update', getdate(), @login)
        end
    end
    --@@@@@@ delete @@@@@@@
else
    begin
     insert into [logs].[tablename] ([changes], [type], changedate, userlogin)
      values 
      (
        (select * from deleted for xml path('template')),
        'delete',
        getdate(),
        (select top 1 modifylogin from deleted)
      )
    end

Пример XML:
Кликните здесь для просмотра всего текста
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<row>
    <ID>1623</ID>
    <Сolumn>Name</Сolumn>
    <OldValue>Петро</OldValue>
    <NewValue>Василий</NewValue>
</row>
<row>
    <ID>1623</ID>
    <Сolumn>Age</Сolumn>
    <OldValue>15</OldValue>
    <NewValue>187</NewValue>
</row>
<row>
    <ID>1623</ID>
    <Сolumn>Sex</Сolumn>
    <OldValue>Male</OldValue>
    <NewValue>Female</NewValue>
</row>
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
17.10.2019, 16:06
Ответы с готовыми решениями:

Php логирующий весь POST
Нужен PHP файл который логгировал все POST запросы, любые POST запросы, рандомные с именами и параметрами, в отдельный файл. Заранее...

Универсальный указатель или универсальный скалярный тип
Здравствуйте! Помогите, пожалуйста написать программу: требуется написать универсальный скалярный тип данных без использования шаблонов....

Триггер для добавления к строке название таблицы, в которой содержится триггер
Есть таблицы База_Практик. В неё добавляю предприятие, и автоматически нужно добавить это предприятие с именем таблицы База практик в...

5
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
17.10.2019, 17:10
T-SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
use tempdb;
go
 
create table dbo.t (id int primary key, a int, b int, c int);
create table dbo.t_log (id int identity primary key, data_changes xml, operator sysname, dt datetime2);
go
 
create trigger dbo.t_logging
on dbo.t
after insert, update, delete
as
begin
 set nocount on;
 
 with t(x) as
 (
  select
   isnull(i.id, d.id) as id,
   (select 'a' as [@name], i.a as NewValue, d.a as OldValue where not exists(select i.a intersect select d.a) for xml path('Column'), type) as[*],
   (select 'b' as [@name], i.b as NewValue, d.b as OldValue where not exists(select i.b intersect select d.b) for xml path('Column'), type) as[*],
   (select 'c' as [@name], i.c as NewValue, d.c as OldValue where not exists(select i.c intersect select d.c) for xml path('Column'), type) as[*]
  from
   inserted i full join
   deleted d on d.id = i.id
  where
   not exists(select i.id, i.a, i.b, i.c intersect select d.id, d.a, d.b, d.c)
  for xml path('row'), type
 )
 insert into dbo.t_log
  (data_changes, operator, dt)
 select
  x, system_user, sysdatetime()
 from
  t;
  
end;
go
 
insert into dbo.t
values
 (1, 100, 200, 300);
 
update dbo.t set a = -100 where id = 1;
 
update dbo.t set id = 2 where id = 1;
 
delete from dbo.t where id = 2;
 
select * from t_log order by id;
go
 
drop table dbo.t, dbo.t_log;
go
0
 Аватар для Pavel Rem
11 / 11 / 2
Регистрация: 15.07.2016
Сообщений: 132
17.10.2019, 18:45  [ТС]
Если честно я не совсем уловил в чем отличие, кроме того что нужно всё поля вручную вписывать.
Я хочу добиться универсальности.
В идеале, что б была возможность просто скопировать триггер, без изменений, на все таблицы которые я хочу логировать.
А если придется вручную, для каждой таблицы, для всех полей (которых может быть 30+), корректировать код, то зачем это нужно.

Можно ли внутри триггера получить имя таблицы для которой он выполняется?
Кроме этого, есть ли ещё какой-нибудь хитрый, эффективный способ получить из inserted только те поля которые изменились, а не строки целиком?
0
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
17.10.2019, 18:57
Лучший ответ Сообщение было отмечено pincet как решение

Решение

Pavel Rem, что мешает написать генератор триггеров с вменяемым содержимым, а не сочинять универсального монстра с перекладыванием inserted+deleted во временные таблицы и анализом метаданных?
0
1116 / 761 / 183
Регистрация: 27.11.2009
Сообщений: 2,272
17.10.2019, 21:29
Лучший ответ Сообщение было отмечено pincet как решение

Решение

Изобретения вечного двигателя не применяются к рассмотрению.
Почему бы и нам изобретателям универсального триггера не говорить сразу "нет!!!"?
0
 Аватар для pincet
1655 / 1154 / 173
Регистрация: 23.07.2010
Сообщений: 6,910
19.10.2019, 17:04
Цитата Сообщение от Pavel Rem Посмотреть сообщение
Я хочу добиться универсальности.
задай себе простой вопрос "зарадидля?". много думай
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
19.10.2019, 17:04
Помогаю со студенческими работами здесь

Триггер для добавления к строке название таблицы, в которой содержится триггер
Есть таблицы База_Практик. В неё добавляю предприятие, и автоматически нужно добавить это предприятие с именем таблицы База практик в...

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

Триггер, вызывающий процедуру, в которой выполняется select к этой таблице (откуда был вызван триггер)
есть тригерр create or replace trigger quickstart.quickstart.ACRIONS_COMMIT_TR after update on quickstart.actions for each...

Т-Триггер на ИЛИ-НЕ элементах и что это за триггер?
Здравствуйте. Нужно построить схему Т-Триггера через ИЛИ-НЕ элементы. Вот сам триггер ...

Взаимодействие коллайдеров (триггер и не триггер)
подскажите почему не срабатывает коллайдер с тригером на коллайдере без тригера? и как выйти из этой ситуации private void...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Новые блоги и статьи
Подстановка значения реквизита справочника в табличную часть документа
Maks 10.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеПерсонала", разработанного в конфигурации КА2. Задача: при выборе сотрудника (справочник Сотрудники) в ТЧ документа. . .
Очистка реквизитов документа при копировании
Maks 09.04.2026
Алгоритм из решения ниже применим как для типовых, так и для нетиповых документов на самых различных конфигурациях. Задача: при копировании документа очищать определенные реквизиты и табличную. . .
модель ЗдравоСохранения 8. Подготовка к разному выполнению заданий
anaschu 08.04.2026
https:/ / github. com/ shumilovas/ med2. git main ветка * содержимое блока дэлэй из старой модели теперь внутри зайца новой модели 8ATzM_2aurI
Блокировка документа от изменений, если он открыт у другого пользователя
Maks 08.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа, разработанного в конфигурации КА2. Задача: запретить редактирование документа, если он открыт у другого пользователя. / / . . .
Система безопасности+живучести для сервера-слоя интернета (сети). Двойная привязка.
Hrethgir 08.04.2026
Далее были размышления о системе безопасности. Сообщения с наклонным текстом - мои. А как нам будет можно проверить, что ссылка наша, а не подделана хулиганами, которая выбросит на другую ветку и. . .
Модель ЗдрввоСохранения 7: больше работников, больше ресурсов.
anaschu 08.04.2026
работников и заданий может быть сколько угодно, но настроено всё так, что используется пока что только 20% kYBz3eJf3jQ
Дальние перспективы сервера - слоя сети с космологическим дизайном интефейса карты и логики.
Hrethgir 07.04.2026
Дальнейшее ближайшее планирование вывело к размышлениям над дальними перспективами. И вот тут может быть даже будут нужны оценки специалистов, так как в дальних перспективах всё может очень сильно. . .
Горе от ума
kumehtar 07.04.2026
Эта мне ментальная установка, что вот прямо сейчас, мол, мне для полного счастья не хватает (нужное вписать), и когда я этого достигну - тогда и полный кайф. Одна из самых сильных ловушек на пути. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru