Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 триггер создает дубликаты записей  [new]
raven_2008
Member

Откуда:
Сообщений: 115
доброе время суток коллеги!

в таблицу базы данных Sql server`a поступает информация.
на событие after insert включается триггер
ALTER TRIGGER [Counters].[InsertCommerc] ON [Counters].[CounterIndicatesCommerc]
After insert
AS
declare @id bigint, @owner bigint
declare @day int, @mont int, @year int, @dwork datetime, @dateindy datetime
declare @idcomm bigint, @counttype varchar(250), @idcount bigint, @idhouse bigint,
@measur varchar(250), @indy float, @cityname varchar(250), @adress varchar(500),
@service varchar(250), @iduk bigint

select @idcomm =MAX(id) from Counters.CounterIndicatesCommerc
--получить последнюю запись

select 
	@idcount=ID_Counter,
	@dwork=DateIndicates,
	@indy=Indicates,
	@mont=MONTH(DateIndicates),
	@year=year(DateIndicates)

from Counters.CounterIndicatesCommerc where ID=@idcomm

set @id= null
--проверить,  запись на соответствие началу периода
select @id=ID_commerc from  SelectionCommerc where ID_counter=@idcount 
and w_month=@mont and w_year=@year and direct=0 --начало месяца

if @id is null begin
	insert into selectionCommerc 
		select ci.id,
			ct.name,
			c.ID,
			h.id,
			ci.DateIndicates,
			mu.Name,
			ci.Indicates,
			css.Name,
			 isnull( cs.Name,'')+' '+isnull('д. '+convert(varchar(15), h.Number),'')+' '+ISNULL('кв. '+CONVERT(varchar(10), cf.Number),''),
			rs.Name,
			DAY(ci.DateIndicates),
			month(ci.DateIndicates),
			year(ci.DateIndicates),
			c.iduk,
			0
			from 
			counters.counter c
			join common.houses h on h.id=c.ID_house
			join Common.Buildings cb on cb.ID=h.OWNER
			join Common.Streets cs on cs.ID=cb.OWNER
			join Common.Settlements css on css.ID=cs.OWNER
			left join Common.Flats cf on cf.ID=c.ID_flat
			join [References].[Services] rs on rs.Code=c.Service
			join counters.counterindicatescommerc ci on ci.ID_Counter=c.ID
			join [References].CounterTypes ct on ct.Code=c.Type
			join  Counters.MeasureUnits mu on mu.Code=c.Measure
			where ci.ID=@idcomm
end

else begin
	select @dateindy=date_indicates from SelectionCommerc where ID_commerc=@id
	if @dateindy>@dwork begin
		update SelectionCommerc set Date_Indicates=@dwork, id_commerc=@idcomm,
			Indicates=@indy, w_day=DAY(@dwork), w_month=MONTH(@dwork), w_year=YEAR(@dwork)
		where ID_commerc=@id
	end
end

set @id=null
--проверить запись на соответствие окончанию периода
select @id=ID_commerc from  SelectionCommerc where ID_counter=@idcount 
and w_month=@mont and w_year=@year and direct=1 --конец месяца

if @id is null begin
	insert into selectionCommerc 
		select ci.id,
			ct.name,
			c.ID,
			h.id,
			ci.DateIndicates,
			mu.Name,
			ci.Indicates,
			css.Name,
			 isnull( cs.Name,'')+' '+isnull('д. '+convert(varchar(15), h.Number),'')+' '+ISNULL('кв. '+CONVERT(varchar(10), cf.Number),''),
			rs.Name,
			DAY(ci.DateIndicates),
			month(ci.DateIndicates),
			year(ci.DateIndicates),
			c.iduk,
			1
			from 
			counters.counter c
			join common.houses h on h.id=c.ID_house
			join Common.Buildings cb on cb.ID=h.OWNER
			join Common.Streets cs on cs.ID=cb.OWNER
			join Common.Settlements css on css.ID=cs.OWNER
			left join Common.Flats cf on cf.ID=c.ID_flat
			join [References].[Services] rs on rs.Code=c.Service
			join counters.counterindicatescommerc ci on ci.ID_Counter=c.ID
			join [References].CounterTypes ct on ct.Code=c.Type
			join  Counters.MeasureUnits mu on mu.Code=c.Measure
			where ci.ID=@idcomm
end

else begin
	select @dateindy=date_indicates from SelectionCommerc where ID_commerc=@id
	if @dateindy<@dwork begin
		update SelectionCommerc set Date_Indicates=@dwork, id_commerc=@idcomm,
			Indicates=@indy, w_day=DAY(@dwork), w_month=MONTH(@dwork), w_year=YEAR(@dwork)
		where ID_commerc=@id
	end
end


проблема в том, что в процессе работы данные - дублируются. и на конец месяца получается несколько абсолютно одинаковых записей для каждой записи.
от чего это происходит?
27 фев 13, 15:00    [13988976]     Ответить | Цитировать Сообщить модератору
 Re: триггер создает дубликаты записей  [new]
iap
Member

Откуда: Москва
Сообщений: 47001
raven_2008,

я как только переменные в триггере вижу, так думаю: ну, всё, дальше - говнокод.
Как же можно быстро в этом месиве разобраться?
Без каких-либо пояснений с Вашей стороны.
Например, какую цель преследует этот триггер?
27 фев 13, 15:07    [13989050]     Ответить | Цитировать Сообщить модератору
 Re: триггер создает дубликаты записей  [new]
PaulYoung
Member

Откуда: Москва
Сообщений: 2551
raven_2008,

в подобных триггерах обычно используют таблицы INSERTED и DELETED, а у Вас они даже не упоминаются. Зачем тогда триггер?
27 фев 13, 15:15    [13989130]     Ответить | Цитировать Сообщить модератору
 Re: триггер создает дубликаты записей  [new]
Гость333
Member

Откуда:
Сообщений: 3683
iap,

Да что там разбираться :-)
ALTER TRIGGER [Counters].[InsertCommerc] ON [Counters].[CounterIndicatesCommerc]
After insert

...

-- Здесь автор почему-то думает, что таким образом получает id вставляемой записи.
-- Это верно только для случая вставки одной записи и работы с БД из одной коннекции.
-- Ну либо уровень изоляции выставлен serializable, в чём я сомневаюсь.
select @idcomm =MAX(id) from Counters.CounterIndicatesCommerc

...

-- Здесь проверяется, существует ли в SelectionCommerc запись, соответствующая @idcomm
select @id=ID_commerc from  SelectionCommerc where ...

-- Если записи не существует, она вставляется. Если существует, то обновляется.
-- Либо у автора версия SQL < 2008, либо он не знает про merge.
-- Проблема в том, что от момента выборки @id до момента проверки сюда может влезть
-- другая сессия и вставить запись с такими же параметрами -- получится дубликат.
if @id is null
    insert into selectionCommerc ...
else
   update SelectionCommerc ...


Итак, 1) триггер неправильно работает для случая вставки более чем одной записи; 2) триггер неправильно работает в многопользовательском окружении; 3) для борьбы с дубликатами не используется unique constraint; 4) не факт, что сам по себе запрос "insert into selectionCommerc select ci.id, ct.name ..." не генерит дубликатов. Вон там сколько джойнов. Из какой-нибудь таблицы дубликаты вполне могут вылезть.
27 фев 13, 15:27    [13989234]     Ответить | Цитировать Сообщить модератору
 Re: триггер создает дубликаты записей  [new]
aleks2
Guest
iap
так думаю: ну, всё, дальше - говнокод.


Это не просто говнокод - это ЭПИЧЕСКИЙ говнокод

ALTER TRIGGER [Counters].[InsertCommerc] ON [Counters].[CounterIndicatesCommerc]
After insert
AS
...
select @idcomm =MAX(id) from Counters.CounterIndicatesCommerc
--получить последнюю запись


А отсутствие дублей должно обеспечиваться констрейнтом.
27 фев 13, 15:29    [13989252]     Ответить | Цитировать Сообщить модератору
 Re: триггер создает дубликаты записей  [new]
Александр Бердышев
Member

Откуда: Санкт-Петербург
Сообщений: 349
select @idcomm =MAX(id) from Counters.CounterIndicatesCommerc
--получить последнюю запись

select 
	@idcount=ID_Counter,
	@dwork=DateIndicates,
	@indy=Indicates,
	@mont=MONTH(DateIndicates),
	@year=year(DateIndicates)

from Counters.CounterIndicatesCommerc where ID=@idcomm

Сделайте мне развидеть это.

По существу, как можно помочь этому триггеру и автору:

1. Автору срочно прочитать про таблицы Inserted и Deleted.

2. Прочитать, что такое триггер After Insert, какие вообще виды триггеров бывают. Понять их различия. Понять, что в данном случае будет находиться в таблице после выполнения INSEERT, но до запуска триггера. Что при этом будет в таблице Inserted.

3. Прочитать про конструкцию CASE WHEN ... THEN ... ELSE END - возможно позволит в два раза уменьшить количество строк кода.

4. ПОЛНОСТЬЮ переписать этот триггер, используя вместо переменных таблицу Inserted.

5. Дубли в самом деле могут получаться из-за JOIN-ов - надо отладить этот момент. Скопировать значения из Inserted в глобальную временную таблицу (##TableName), после чего посмотреть что вернёт JOIN с этими данными.

Успехов!
27 фев 13, 16:59    [13989988]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить