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

Откуда: Israel
Сообщений: 5500
Есть таблица:
CREATE TABLE [Tst] (
[Latitude] [nvarchar(8)] NULL ,
[Longitude] [nvarchar(9)] NULL ,
[la] [float] NULL ,
[lo] [float] NULL
) ON [PRIMARY]
END
для хранения географических координат в разных форматах.
Есть функции:
CREATE FUNCTION dbo.GeoGradFormatLo (@Coord FLOAT)
RETURNS nvarchar(8)
CREATE FUNCTION dbo.GeoGradFormatLa (@Coord FLOAT)
RETURNS nvarchar(9)
CREATE FUNCTION dbo.LaGrad (@Coord nvarchar(8))
RETURNS FLOAT
CREATE FUNCTION dbo.LoGrad (@Coord nvarchar(9))
RETURNS FLOAT
Все поля таблицы должны оставаться редактируемыми.
Нужно при редактировании полей Latitude, Longitude (INSERT, UPDATE) изменять поля la = dbo.LaGrad (Latitude) и lo = dbo.LoGrad (Longitude)
и, наоборот, при редактировании полей la, lo изменять поля
Latitude = dbo.GeoGradFormatLa (la) и
Longitude = dbo.GeoGradFormatLo (lo)

Можно ли избежать рекурсии, при построении триггера(ов)?
21 июл 04, 10:28    [823015]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Шотов Вадим
Member

Откуда: Киев
Сообщений: 309
Можно
TRIGGER_NESTLEVEL(OBJECT_ID('triggername'))
21 июл 04, 10:30    [823025]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Алексей2003
Member

Откуда: Москва
Сообщений: 5645
можно... для этого нужно где-то хранить тот факт, что вы изменяете строку...
например завести поле UPD и там при вставке/изменении всталять как 1, а после срабатывания триггера как 0 и делать проверку на таблицу inserted на это поле, чтобы оно было 1.


для спящего время бодрствования равносильно сну
21 июл 04, 10:33    [823037]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Rivkin Dmitry
Member

Откуда: Israel
Сообщений: 5500
Шотов Вадим
Можно
TRIGGER_NESTLEVEL(OBJECT_ID('triggername'))


Не понимаю как этим можно воспользоваться.
21 июл 04, 10:42    [823076]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Rivkin Dmitry
Member

Откуда: Israel
Сообщений: 5500
Алексей2003
можно... для этого нужно где-то хранить тот факт, что вы изменяете строку...
например завести поле UPD и там при вставке/изменении всталять как 1, а после срабатывания триггера как 0 и делать проверку на таблицу inserted на это поле, чтобы оно было 1.


для спящего время бодрствования равносильно сну


Можно пример?
21 июл 04, 10:42    [823078]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Glory
Member

Откуда:
Сообщений: 104760
Не понимаю как этим можно воспользоваться.
Ну так пример есть в BOL
21 июл 04, 10:46    [823100]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Шотов Вадим
Member

Откуда: Киев
Сообщений: 309

Не понимаю как этим можно воспользоваться.

if TRIGGER_NESTLEVEL(OBJECT_ID('triggername'))>1
RETURN
21 июл 04, 10:52    [823119]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
KOLCHOZ_POSTEVENT
Guest
То,чо Вас пужает называется,на вражеском языке direct recursion.
Она в большинстве баз дефолтнם запрещена(теоретически.Не налетал я на эту праблу в MSSQL).
Проверьте-ка,если oна на OFF,то,теоритически,опять-же бояться нечего.
sp_dboption master,'recursive triggers'
21 июл 04, 11:03    [823160]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
KOLCHOZ_POSTEVENT
Guest
Ой-ой,master-это я,так,к примеру,там должно быть имя пользованной базы.
21 июл 04, 11:05    [823172]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Glory
Member

Откуда:
Сообщений: 104760
Но лучше все-таки имхо подстраховаться с помощью TRIGGER_NESTLEVEL. Потому что рекурсия отключается для всей базы.
21 июл 04, 11:07    [823185]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Rivkin Dmitry
Member

Откуда: Israel
Сообщений: 5500
Glory
Не понимаю как этим можно воспользоваться.
Ну так пример есть в BOL


В Израильских школах изучают предмет называющийся "Понимание прочитанного". Я приехал в Израиль не в школьном возрасте, поэтому, есть проблема...
Ну прежде всего что я сделал я прочитал BOL. И все равно не понял. Записать эту команду в текст триггера? Не кажется... Пользоваться ею каждый раз после команды редактирования - чушь... Потому и не понял
21 июл 04, 11:08    [823192]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Glory
Member

Откуда:
Сообщений: 104760
В BOL нужно читать каждое предложение. Ибо там нет бесполезной информации. Поэтому если кажется что топик BOLа не ответил мне на вопрос я лично перечитываю его еще раз. Разбирая каждое предложение. Сначала это трудно, но потом втягиваешься.

Записать эту команду в текст триггера? Не кажется...
2ая строка в топике BOL про TRIGGER_NESTLEVEL
"TRIGGER_NESTLEVEL is used in triggers to determine the current level of nesting."

Пользоваться ею каждый раз после команды редактирования - чушь...
1ая строка в этом же топике в разделе Remarks
"TRIGGER_NESTLEVEL returns 0 if it is executed outside of a trigger and object_id is not NULL."
21 июл 04, 11:17    [823224]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
KOLCHOZ_POSTEVENT
Guest
Только вначале трига,если он,сердешный,пулён из рекурсии код Шотова дальше второй строки его не пустит.
21 июл 04, 11:19    [823240]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Rivkin Dmitry
Member

Откуда: Israel
Сообщений: 5500
Glory
Понял! Исправлюсь!
Спасибо!
21 июл 04, 11:21    [823247]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Rivkin Dmitry
Member

Откуда: Israel
Сообщений: 5500
Ок! Создал триггер:
CREATE TRIGGER TRG_TST
ON Tst
FOR INSERT, UPDATE 
AS 
BEGIN

IF TRIGGER_NESTLEVEL(OBJECT_ID('TRG_TST'))>1
RETURN

IF UPDATE ( Latitude ) 
BEGIN
	UPDATE Tst SET Tst.la = dbo.LaGrad (Tst.Latitude)
	FROM Tst
	INNER JOIN INSERTED I 
	ON Tst.ID = I.ID

END

IF UPDATE ( Longitude ) 
BEGIN
	UPDATE Tst SET Tst.lo = dbo.LoGrad (Tst.Longitude)
	FROM Tst
	INNER JOIN INSERTED I 
	ON Tst.ID = I.ID

END

IF UPDATE ( La ) 
BEGIN
	UPDATE Tst SET Tst.Latitude = dbo.GeoGradFormatLa (Tst.la)
	FROM Tst
	INNER JOIN INSERTED I 
	ON Tst.ID = I.ID

END

IF UPDATE ( Lo ) 
BEGIN
	UPDATE Tst SET Tst.Longitude = dbo.GeoGradFormatLo (Tst.lo)
	FROM Tst
	INNER JOIN INSERTED I 
	ON Tst.ID = I.ID

END

IF @@ERROR <> 0
	RAISERROR (50009, 16, 10)

END
GO

Пробую:
insert into Tst (	[Latitude] ,	[Longitude] )
values ('N1052391','E04426656')

insert into Tst (	[La] ,	[Lo] )
values (30.2324, 0.55666)
Получаю:
ID          Latitude Longitude la                                                    lo                                                    
----------- -------- --------- ----------------------------------------------------- ----------------------------------------------------- 

1 1052390N 04427056E 10.877527777777777 44.451555555555558 2 NULL NULL NULL NULL

Что я сделал не правильно?
21 июл 04, 12:11    [823460]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Rivkin Dmitry
Member

Откуда: Israel
Сообщений: 5500
Не получается триггер. Что-то делаю не так!
21 июл 04, 13:04    [823701]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Тимур Рахимов
Member

Откуда: Москва, Россия
Сообщений: 128
Кстати, для отсечения прямой рекурсии я пользуюсь слегка модифицированным вариантом уже предложенного, не требущим явного указания имени триггера:

if trigger_nestlevel(object_id(object_name(@@procid)))>1
return

Немножко сложнее, но меньше вероятности ошибиться при размножении текста методом copy/paste с далеко идущими последствиями...
21 июл 04, 13:59    [823978]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
ChA
Member

Откуда: Москва
Сообщений: 11136
Тимур Рахимов
trigger_nestlevel(object_id(object_name(@@procid)))

А что, были случаи, когда @@procid <> object_id(object_name(@@procid)) ?
21 июл 04, 14:09    [824031]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Тимур Рахимов
Member

Откуда: Москва, Россия
Сообщений: 128
Да нет, конечно, только сейчас заметил, что двойное преобразование произвёл, тупо заменив литерал на выражение, которым этот литерал обычно получается :). Ляпнул, в общем. Прошу извинить :).

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

if trigger_nestlevel(@@procid)>1
return
21 июл 04, 14:17    [824072]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
KOLCHOZ_POSTEVENT
Guest
Пирчина более-менее понятна-она в программистской логике.
Не вали всё до кучи,леат-леат,как говорят в Париже.
Портит дело помесь инсерт и апдейт триггерра.
На фиг он тебе нужен-этот инсерт тригер?Дропни его ле арба рухот.
Вводи налевою строку и апдейт её на здоровье,увидишь,как всё развяжется.
21 июл 04, 14:30    [824137]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Glory
Member

Откуда:
Сообщений: 104760
У меня лично все работает

use tempdb
go
if object_id('dbo.tst') is not null drop table dbo.tst
go
CREATE TABLE [Tst] (id int identity,
[Latitude] nvarchar(8) NULL ,
[Longitude] nvarchar(9) NULL ,
[la] [float] NULL ,
[lo] [float] NULL 
) ON [PRIMARY]

go
CREATE TRIGGER TRG_TST
ON Tst
FOR INSERT, UPDATE 
AS 
BEGIN

IF TRIGGER_NESTLEVEL(OBJECT_ID('TRG_TST'))>1
RETURN

IF UPDATE ( Latitude ) 
BEGIN
	print 'Update Tst.la'
	UPDATE Tst SET Tst.la = rand()
	FROM Tst
	INNER JOIN INSERTED I 
	ON Tst.ID = I.ID

END

IF UPDATE ( Longitude ) 
BEGIN
	print 'Update Tst.lo'
	UPDATE Tst SET Tst.lo = rand()
	FROM Tst
	INNER JOIN INSERTED I 
	ON Tst.ID = I.ID

END

IF UPDATE ( La ) 
BEGIN
	print 'Update Tst.Latitude'
	UPDATE Tst SET Tst.Latitude = 'xxx'
	FROM Tst
	INNER JOIN INSERTED I 
	ON Tst.ID = I.ID

END

IF UPDATE ( Lo ) 
BEGIN
	print 'Update Tst.Longitude'
	UPDATE Tst SET Tst.Longitude = 'yyy'
	FROM Tst
	INNER JOIN INSERTED I 
	ON Tst.ID = I.ID

END

IF @@ERROR <> 0
	RAISERROR (50009, 16, 10)

END
GO
insert into Tst (	[Latitude] ,	[Longitude] )
values ('N1052391','E04426656')

insert into Tst (	[La] ,	[Lo] )
values (30.2324, 0.55666)

select * from tst
go
if object_id('dbo.tst') is not null drop table dbo.tst
go
21 июл 04, 15:14    [824397]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Rivkin Dmitry
Member

Откуда: Israel
Сообщений: 5500
Glory
Что-то не так! На один insert 4 Update. Т.е. отрабатывают все Update в триггере, хотя я ожидаю только 2. К тому же в моих функциях параметры - вводимые значения, а не подстановка констант.
21 июл 04, 15:29    [824462]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Rivkin Dmitry
Member

Откуда: Israel
Сообщений: 5500
KOLCHOZ_POSTEVENT
Пирчина более-менее понятна-она в программистской логике.
Не вали всё до кучи,леат-леат,как говорят в Париже.
Портит дело помесь инсерт и апдейт триггерра.
На фиг он тебе нужен-этот инсерт тригер?Дропни его ле арба рухот.
Вводи налевою строку и апдейт её на здоровье,увидишь,как всё развяжется.


Всегда дело в логике!
Есть мат. логика , формальная, неформальная, дурная, женская и т.п.
Мужская, например, когда Ванька Федьку спрашивает: "Тебе Манька дала?",
"Нет! А тебе?", "Тоже - нет!". Оба хором: "Вот б...!"

Дело в том, что данные я могу получить и при инсерт и при апдейт, причем, никогда вместе не получаю Latitude, Longitude и la, lo. Сейчас я их пересчитываю в программе, а хотелось бы в триггере.
Инсерт убрал, но ничего не изменилось.
21 июл 04, 15:37    [824502]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Glory
Member

Откуда:
Сообщений: 104760
К тому же в моих функциях параметры - вводимые значения, а не подстановка констант.
Вы мне предлагаете составить функции самому ? Или сойдемся на том что скалярная функция возвращает все таки какое-то. Это раз

Во-вторых при операции INSERT функция UPDATE () вернет True для всех столбцов. А не только для тех которы были указаны в INSERT
21 июл 04, 15:37    [824504]     Ответить | Цитировать Сообщить модератору
 Re: Создать триггер и избежать рекурсии  [new]
Rivkin Dmitry
Member

Откуда: Israel
Сообщений: 5500
1. Вот функции:
IF NOT EXISTS (SELECT * FROM systypes WHERE Name = 'Latitude')
	EXEC sp_addtype Latitude, 'NVARCHAR(8)', 'NULL'
GO

IF NOT EXISTS (SELECT * FROM systypes WHERE Name = 'Longitude')
	EXEC sp_addtype Longitude, 'NVARCHAR(9)', 'NULL'
GO

CREATE FUNCTION dbo.LoGrad (@Coord Longitude)        --transfer format La #ggnn[.]nn to format #ggnnss

RETURNS FLOAT AS BEGIN DECLARE @Lo FLOAT SET @Lo = CAST(SUBSTRING(@Coord, 1, 3) AS FLOAT) + (CAST(SUBSTRING(@Coord, 4, 2) AS FLOAT) * 600.0 + CAST(SUBSTRING(@Coord, 6, 2) AS FLOAT) * 10.0 + CAST(SUBSTRING(@Coord, 8, 1) AS FLOAT)) / 36000.0 IF RIGHT(@Coord, 1) = 'W' SET @Lo = -1*@Lo RETURN @Lo END GO CREATE FUNCTION LaGrad (@Coord Latitude) --transfer format La #ggnn[.]nn to format #ggnnss
RETURNS FLOAT AS BEGIN DECLARE @LA FLOAT SET @LA = CAST(SUBSTRING(@Coord, 1, 2) AS FLOAT) + (CAST(SUBSTRING(@Coord, 3, 2) AS FLOAT) * 600.0 + CAST(SUBSTRING(@Coord, 5, 2) AS FLOAT) * 10.0 + CAST(SUBSTRING(@Coord, 7, 1) AS FLOAT)) / 36000.0 IF RIGHT(@Coord, 1) = 'S' SET @LA = -1*@LA RETURN @LA END GO CREATE FUNCTION dbo.GeoGradFormatLa (@Coord FLOAT) --transfer format La #ggnn[.]nn to format #ggnnss
RETURNS Latitude AS BEGIN DECLARE @GeoGrad Latitude, @M NVARCHAR(1) DECLARE @T1 INT, @T2 INT, @T3 INT IF @Coord >= 90 SET @GeoGrad = '0000000N' ELSE BEGIN IF @Coord >= 0 SET @M = 'N' ELSE SET @M = 'S' SET @Coord = ABS(@Coord) SET @T1 = CAST(@Coord AS INT) SET @T2 = CAST((@Coord - @T1)*60 AS INT) SET @T3 = CAST(CAST(@Coord - CAST(@T1 AS FLOAT) - CAST(@T2 AS FLOAT)/60 AS FLOAT)*36000 AS INT) SET @GeoGrad = RIGHT('00' + CAST(@T1 AS NVARCHAR(2)), 2) + + RIGHT('00' + CAST(@T2 AS NVARCHAR(2)), 2) + RIGHT('000' + CAST(@T3 AS NVARCHAR(3)), 3) + @M END RETURN LTRIM(RTRIM(@GeoGrad)) END GO CREATE FUNCTION dbo.GeoGradFormatLo (@Coord FLOAT) --transfer format La #ggnn[.]nn to format #ggnnss
RETURNS Longitude AS BEGIN DECLARE @GeoGrad Longitude, @M NVARCHAR(1) DECLARE @T1 INT, @T2 INT, @T3 INT IF @Coord >= 180 SET @GeoGrad = '00000000E' ELSE BEGIN IF @Coord <= 0 SET @M = 'W' ELSE SET @M = 'E' SET @Coord = ABS(@Coord) SET @T1 = CAST(@Coord AS INT) SET @T2 = CAST((@Coord - @T1)*60 AS INT) SET @T3 = CAST(CAST(@Coord - CAST(@T1 AS FLOAT) - CAST(@T2 AS FLOAT)/60 AS FLOAT)*36000 AS INT) SET @GeoGrad = RIGHT('000' + CAST(@T1 AS NVARCHAR(3)), 3) + + RIGHT('00' + CAST(@T2 AS NVARCHAR(2)), 2) + RIGHT('000' + CAST(@T3 AS NVARCHAR(3)), 3) + @M END RETURN LTRIM(RTRIM(@GeoGrad)) END GO

2.
автор
Во-вторых при операции INSERT функция UPDATE () вернет True для всех столбцов. А не только для тех которы были указаны в INSERT

Значит, на INSERT нет смысла писать такой триггер, а поля редактировать в программе?
21 июл 04, 15:51    [824579]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / Microsoft SQL Server Ответить