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

Откуда:
Сообщений: 188
Здравствуйте!
Сегодня впервые столкнулся с SQL. Задача такая(на пальцах):
есть таблица ("MyTable"). В ней 2 поля: Field1 и Field2.
так вот, необходимо при изменении, если поле Field1 == null, то и Field2 тоже обнулить.
Как я понял, делается это с помощью триггера. Написал что-то типа:

USE [MyDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER TRIGGER [dbo].[nuller_trigger]
ON [MyDB].[dbo].[MyTable]
AFTER UPDATE 
AS
   DECLARE @field1 int
   SELECT @field1 = Field1   
   FROM MyTable
IF(@field1 =null)
UPDATE MyTable
SET Field2 = null


не обнуляет. Почему то еще есть сомнения, что зациклится такое обновление, ведь всегда будет вызываться UPDATE )
Подскажите пожалуйста, как правильно? Времени читать учебники просто нет, так как это разовая задачка.
18 июн 12, 10:30    [12729133]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
tpg
Member

Откуда: Novosibirsk
Сообщений: 23902
Sky Dragon
...не обнуляет...
Не удивительно...
Триггер написан из предположения, что он вызывается для каждой измененной строки. Это не так.
18 июн 12, 10:36    [12729201]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
Sky Dragon
Member

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

подскажите пожалуйста тогда, как сделать, чтобы было "так" :)
18 июн 12, 10:40    [12729233]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
iap
Member

Откуда: Москва
Сообщений: 47142
Sky Dragon
tpg,

подскажите пожалуйста тогда, как сделать, чтобы было "так" :)
1. Сравнивать поля/переменные с NULL операторами =,<>,<,> и т.п. нельзя!
2. Старайтесь не использовать скалярные переменные в триггерах.
3. Используйте псевдотаблицы deleted и inserted
18 июн 12, 10:44    [12729277]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
iap
Member

Откуда: Москва
Сообщений: 47142
4. За UPDATE без WHERE надо расстреливать! (За редким исключением)
18 июн 12, 10:46    [12729291]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
tpg
Member

Откуда: Novosibirsk
Сообщений: 23902
... и ничего не сказано о наличии ПК или ещё какого уникального столбца.
18 июн 12, 10:47    [12729305]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
iap
Member

Откуда: Москва
Сообщений: 47142
iap
4. За UPDATE без WHERE надо расстреливать! (За редким исключением)
WHERE или INNER JOIN или CROSS APPLY, если точнее
18 июн 12, 10:48    [12729316]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
Winnipuh
Member [заблокирован]

Откуда: Київ
Сообщений: 10428
iap
4. За UPDATE без WHERE надо расстреливать! (За редким исключением)


всех не перестреляешь!
18 июн 12, 13:18    [12730663]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
RubinDm
Member

Откуда:
Сообщений: 461
alter trigger [dbo].[nuller_trigger]
  on [MyDB].[dbo].[MyTable] after update
as
begin

  -- Если UPDATE не задел поле [Field1], то ...
  if (not(update(Field1)))
    return; -- нет необходимости в сбросе значения поля [Field2] в NULL

  -- Если UPDATE не задел записи таблицы, то ...
  if (not(exists(select * from [inserted]))
    return; -- нет необходимости в сбросе значения поля [Field2] в NULL

  -- Проверяем наличие записей, в которых
  -- значение поля [Field1] было сброшено в NULL.
  if (not(exists(
    select [new].[ID]
    from [inserted] [new]
    join [deleted]  [old] on [old].[ID] = [new].[ID]
    where [old].Field1 is NOT null
      and [new].Field1 is null
  )))
    return; -- нет необходимости в сбросе значения поля [Field2] в NULL
  
  -- Сброс значения поля [Field2] в NULL
  update T set T.Field2 = null
  from MyTable T
  where T.[ID] in ( -- тут мы вычисляем ID записей, в которых
                    -- значение поля [Field1] было сброшено в NULL.
                    select [new].[ID]
                    from [inserted] [new]
                    join [deleted]  [old] on [old].[ID] = [new].[ID]
                    where [old].Field1 is NOT null
                      and [new].Field1 is null
                  )
  
  return;

end
go


зы: не проверял (ибо не на чем)
18 июн 12, 13:51    [12731060]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
iap
Member

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

применение функции UPDATE() - абсолютно бессмысленно
18 июн 12, 16:20    [12732782]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
Xordal
Member

Откуда: Таганрог
Сообщений: 536
Да и 2 раза выполнять:
    select [new].[ID]
    from [inserted] [new]
    join [deleted]  [old] on [old].[ID] = [new].[ID]
    where [old].Field1 is NOT null
      and [new].Field1 is null

думаю тоже ни к чему.
18 июн 12, 17:59    [12733877]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
invm
Member

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

В общем-то, даже и один раз ни к чему.
18 июн 12, 18:21    [12734003]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
RubinDm
Member

Откуда:
Сообщений: 461
iap
RubinDm, применение функции UPDATE() - абсолютно бессмысленно
Не могли бы Вы аргументировать Ваш комент?
18 июн 12, 19:09    [12734207]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
RubinDm
Member

Откуда:
Сообщений: 461
Xordal
Да и 2 раза выполнять:
    select [new].[ID]
    from [inserted] [new]
    join [deleted]  [old] on [old].[ID] = [new].[ID]
    where [old].Field1 is NOT null
      and [new].Field1 is null

думаю тоже ни к чему.
И Вы... не могли бы Вы аргументировать Ваш комент?
18 июн 12, 19:10    [12734208]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
RubinDm
Member

Откуда:
Сообщений: 461
invm
Xordal, В общем-то, даже и один раз ни к чему.
И Вы... не могли бы Вы аргументировать Ваш комент?
18 июн 12, 19:10    [12734212]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
iap
Member

Откуда: Москва
Сообщений: 47142
RubinDm
iap
RubinDm, применение функции UPDATE() - абсолютно бессмысленно
Не могли бы Вы аргументировать Ваш комент?
А можно сначала услышать Ваш комментарий по этому поводу?
18 июн 12, 20:03    [12734357]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
invm
Member

Откуда: Москва
Сообщений: 9833
RubinDm
invm
Xordal, В общем-то, даже и один раз ни к чему.
И Вы... не могли бы Вы аргументировать Ваш комент?
alter trigger [dbo].[nuller_trigger]
  on [MyDB].[dbo].[MyTable] after update
as
begin

  -- Если UPDATE не задел поле [Field1], то ...
  if (not(update(Field1)))
    return; -- нет необходимости в сбросе значения поля [Field2] в NULL

  -- Если UPDATE не задел записи таблицы, то ...
  if (not(exists(select * from [inserted]))
    return; -- нет необходимости в сбросе значения поля [Field2] в NULL

  -- Сброс значения поля [Field2] в NULL
  update T
   set
    T.Field2 = null
  from
   inserted i join
   deleted d on d.[ID] = i.[ID] join
   MyTable T on t.[ID] = i.[ID]
  where
   i.Field1 is null and d.Field1 is not null;

 return;
end
go
18 июн 12, 20:26    [12734433]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
RubinDm
Member

Откуда:
Сообщений: 461
iap
RubinDm
пропущено...Не могли бы Вы аргументировать Ваш комент?
А можно сначала услышать Ваш комментарий по этому поводу?
Можно. Я всегда рад помочь незрячим и слабовидящим:
-- Если UPDATE не задел поле [Field1], то ...
  if (not(update(Field1)))
    return; -- нет необходимости в сбросе значения поля [Field2] в NULL
Теперь не могли бы Вы аргументировать Ваш комент?
18 июн 12, 21:31    [12734661]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
iap
Member

Откуда: Москва
Сообщений: 47142
RubinDm
iap
пропущено...
А можно сначала услышать Ваш комментарий по этому поводу?
Можно. Я всегда рад помочь незрячим и слабовидящим:
-- Если UPDATE не задел поле [Field1], то ...
  if (not(update(Field1)))
    return; -- нет необходимости в сбросе значения поля [Field2] в NULL
Теперь не могли бы Вы аргументировать Ваш комент?
"Задел поле" - это как понимать?
18 июн 12, 21:38    [12734680]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
RubinDm
Member

Откуда:
Сообщений: 461
iap
RubinDm
пропущено...
Можно. Я всегда рад помочь незрячим и слабовидящим:
-- Если UPDATE не задел поле [Field1], то ...
  if (not(update(Field1)))
    return; -- нет необходимости в сбросе значения поля [Field2] в NULL
Теперь не могли бы Вы аргументировать Ваш комент?
"Задел поле" - это как понимать?

1) Этот запрос задевает (меняет) значение поля Field1 и не задевает (не меняет) значение поля Field2.
update T set Field1 = ... from MyTable T

2) Этот запрос задевает (меняет) значение поля Field2 и не задевает (не меняет) значение поля Field1.
update T set Field2 = ... from MyTable T

3) Этот запрос задевает (меняет) значения обоих полей.
update T set Field1 = ..., Field2 = ... from MyTable T

По условиям поставленной автором топика задачи интерес представляют только те случаи, в которых значение поля Field1 может быть изменено (примеры 1 и 3). В противном случае (пример 2) актуализация поля Field2 не требуется, т.к. Field1 не меняется и ловить там нечего в принципе.

Так я увижу Ваши аргументы или забудем об этом?
18 июн 12, 21:59    [12734767]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
Xordal
Member

Откуда: Таганрог
Сообщений: 536
RubinDm
Xordal
Да и 2 раза выполнять:
    select [new].[ID]
    from [inserted] [new]
    join [deleted]  [old] on [old].[ID] = [new].[ID]
    where [old].Field1 is NOT null
      and [new].Field1 is null

думаю тоже ни к чему.
И Вы... не могли бы Вы аргументировать Ваш комент?

Ну это условие как минимум один раз будет проверяться, а в случае если есть что апдейтить, то даже 2 раза. Зачем серверу лишнюю работу давать? И, как отметил invm, в update ... set ... подзапрос не нужен, можно сразу джойниться на таблицы inserted и deleted
19 июн 12, 09:44    [12735965]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
alexeyvg
Member

Откуда: Moscow
Сообщений: 31960
2 RubinDm

if (not(update(Field1)))
это полезный стейтмент, позволит уменьшить нагрузку на сервер

if (not(exists(select * from [inserted]))
не нужно, обновление и так не будет потреблять ресурсов, если в inserted ничего нет.

if (not(exists(
    select [new].[ID] ...
не нужно, запрос в update сделает то же самое.

А из обновления можно убрать лишний джойн:
-- Сброс значения поля [Field2] в NULL
  update T
  set T.Field2 = null
  from deleted d
   MyTable T on T.[ID] = d.[ID]
  where
    T.Field1 is null and d.Field1 is not null;
19 июн 12, 09:52    [12736015]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
Sky Dragon
Member

Откуда:
Сообщений: 188
Ого, сколько обсуждений вызвал такой маленький вопрос)
Из одних только комментариев уже можно выучить синтаксис SQL )

Всем спасибо, что откликнулись
19 июн 12, 10:03    [12736082]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
iap
Member

Откуда: Москва
Сообщений: 47142
RubinDm
iap
пропущено...
"Задел поле" - это как понимать?

1) Этот запрос задевает (меняет) значение поля Field1 и не задевает (не меняет) значение поля Field2.
update T set Field1 = ... from MyTable T

2) Этот запрос задевает (меняет) значение поля Field2 и не задевает (не меняет) значение поля Field1.
update T set Field2 = ... from MyTable T

3) Этот запрос задевает (меняет) значения обоих полей.
update T set Field1 = ..., Field2 = ... from MyTable T

По условиям поставленной автором топика задачи интерес представляют только те случаи, в которых значение поля Field1 может быть изменено (примеры 1 и 3). В противном случае (пример 2) актуализация поля Field2 не требуется, т.к. Field1 не меняется и ловить там нечего в принципе.

Так я увижу Ваши аргументы или забудем об этом?
Кто кого "задевает" в этом примере:
update T set Field1 = Field1, Field2 = Field2 from MyTable T
?
19 июн 12, 12:39    [12737388]     Ответить | Цитировать Сообщить модератору
 Re: Триггер  [new]
RubinDm
Member

Откуда:
Сообщений: 461
iap
Кто кого "задевает" в этом примере:
update T set Field1 = Field1, Field2 = Field2 from MyTable T
?
Задевает поля инструкция, в данном случае это update, который задевает два поля: Field1 и Field2. Тот факт, что значения полей фактически не должны изменится, вовсе не означает, что сервер не должен выполнять инструкцию update. Сервер понимает, что могут быть тригеры, которые также могут влиять на результат выполнения инструкции. Возможно, что сервер понимает еще много разного, о чем мы даже не догадываемся. При этом полей в таблице иногда бывает много, но не всегда все из них подвергаются воздействиям инструкции update. И в тригерах, как правило, надо отлавливать изменение только конкретного набора полей, как в случае задачи поставленной автором топика. Вызов функции update(Field1) отвечает на вопрос, было ли упомянуто поле Field1 в инструкции update в части set. Если не было, но нет никакой необходимости в актуализации Field2.
19 июн 12, 12:53    [12737534]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2 3 4   вперед  Ctrl      все
Все форумы / Microsoft SQL Server Ответить