#c# #sql #sql-server #sql-server-2014
Вопрос:
Эй,
Я пытаюсь обновить некоторые столбцы в своей таблице, SQL Server 2014
и я написал некоторый код для trigger
и, следовательно, вставить новые значения в новую таблицу, используя следующую процедуру кода:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Atrin Noori
-- =============================================
ALTER TRIGGER [dbo].[UsersOnUPDATE]
ON [dbo].[Users]
After UPDATE
AS
BEGIN
SET NOCOUNT ON;
--
DECLARE @user_key int
SELECT @user_key = i.User_key FROM inserted i;
--
IF UPDATE (User_fullname)
BEGIN
DECLARE @newfullname nvarchar(MAX);
DECLARE @oldfullname nvarchar(MAX);
--
SELECT @oldfullname = i.User_fullname FROM deleted i;
SELECT @newfullname = i.User_fullname FROM inserted i;
--
INSERT INTO UsersLogs (UL_user_key_r, UL_date, UL_oldname, UL_newname, UL_IsDeleted)
VALUES (@user_key, GETDATE(), @oldfullname, @newfullname, 0)
END
--
ELSE IF UPDATE (User_password)
BEGIN
DECLARE @newpassword nvarchar(10);
DECLARE @oldpassword nvarchar(10);
--
SELECT @oldpassword = i.User_password FROM deleted i;
SELECT @newpassword = i.User_password FROM inserted i;
--
INSERT INTO UsersLogs (UL_user_key_r,UL_date,UL_oldpass,UL_newpass,UL_IsDeleted)
VALUES (@user_key, GETDATE(), @oldpassword, @newpassword, 0)
END
--
ELSE IF UPDATE (User_username)
BEGIN
DECLARE @newusername nvarchar(10);
DECLARE @oldusername nvarchar(10);
--
SELECT @oldusername = i.User_username FROM deleted i;
SELECT @newusername = i.User_username FROM inserted i;
--
INSERT INTO UsersLogs (UL_user_key_r, UL_date, UL_oldusername, UL_newusername, UL_IsDeleted)
VALUES (@user_key, GETDATE(), @oldusername, @newusername, 0)
END
--
ELSE IF UPDATE (User_position_id_r)
BEGIN
DECLARE @newposid tinyint;
DECLARE @oldposid tinyint;
--
SELECT @oldposid = i.User_position_id_r FROM deleted i;
SELECT @newposid = i.User_position_id_r FROM inserted i;
--
INSERT INTO UsersLogs (UL_user_key_r, UL_date, UL_oldPosid, UL_newPosid, UL_IsDeleted)
VALUES (@user_key, GETDATE(), @oldposid, @newposid, 0)
END
END
Этот триггер отлично работает, когда я вручную изменяю значения в каждом столбце и вставляю их в новую вызываемую таблицу UsersLogs
.
Однако это не работает нормально, когда я использую приложение c#, которое я разрабатываю, для обновления значений…
рассматривать:
Я пытаюсь изменить пароль пользователя через свое приложение и использую
Stored Procedure
.
Он Stored Procedure
работает нормально и обновляет значения, НО триггер UserOnUpdate
вставляет значение old
и new
User_Fullnme
(не oldpassword и newpassword) в новую таблицу (UsersLgos)
и устанавливает для других значение null (что нормально, если оно равно нулю).
Я имею в виду, что будет выполняться только эта часть кода:
IF UPDATE (User_fullname)
BEGIN
DECLARE @newfullname nvarchar(MAX);
DECLARE @oldfullname nvarchar(MAX);
--
SELECT @oldfullname = i.User_fullname FROM deleted i;
SELECT @newfullname = i.User_fullname FROM inserted i;
--
INSERT INTO UsersLogs (UL_user_key_r, UL_date, UL_oldname, UL_newname, UL_IsDeleted)
VALUES (@user_key, GETDATE(), @oldfullname, @newfullname, 0)
END
И судя по тому, что я только что сказал… Я меняю пароль и УСТАНАВЛИВАЮ условие для проверки обновленной колонки.
Но вопрос в том, почему триггер не может понять, какой столбец был обновлен через приложение ?
записка:
Я повторяю это еще раз: ОН ОТЛИЧНО РАБОТАЕТ С РУЧНЫМИ ИЗМЕНЕНИЯМИ И ОБНОВЛЕНИЯМИ
Комментарии:
1. В целом
deleted
иinserted
содержат набор записей, а не только одну. Так что вашиSELECT
s в скалярную переменную из них в целом не имеют смысла. Сначала вам нужно исправить этот фундаментальный недостаток, прежде чем что-либо еще.2. но он отлично работает при обновлении вручную
Ответ №1:
UPDATE()
Функция сообщает вам только о том, присутствовал ли столбец в UPDATE
инструкции, а не о том, действительно ли значение изменилось. Кроме того, te inserted
и deleted
таблицы могут содержать несколько строк или ни одной.
Итак, ваш триггер действительно должен выглядеть так:
ALTER TRIGGER [dbo].[UsersOnUPDATE]
ON [dbo].[Users]
After UPDATE
AS
SET NOCOUNT ON;
IF NOT (UPDATE(User_fullname) OR UPDATE(User_password) OR UPDATE(User_username) OR UPDATE(User_position_id_r))
RETURN; -- this only tells you if the column was present
INSERT INTO UsersLogs
(UL_user_key_r, UL_date, UL_IsDeleted,
UL_oldname, UL_newname,
UL_oldpass, UL_newpass,
UL_oldusername, UL_newusername,
UL_oldPosid, UL_newPosid)
SELECT
i.user_key, GETDATE(), 0,
d.fullname, i.fullname,
d.User_password, i.User_password,
d.User_username, i.User_username,
d.User_position_id_r, i.User_position_id_r
FROM inserted i
JOIN deleted d ON i.user_key = d.user_key
WHERE EXISTS ( -- this checks for any differences
SELECT i.fullname, i.User_password, i.User_username, i.User_position_id_r
EXCEPT -- this will deal with nulls correctly
SELECT d.fullname, d.User_password, d.User_username, d.User_position_id_r
);
В случае обновления только некоторых столбцов это оставляет вас с другими столбцами, имеющими одинаковые значения до и после. Но некоторые CASE
выражения должны это уладить.
ALTER TRIGGER [dbo].[UsersOnUPDATE]
ON [dbo].[Users]
After UPDATE
AS
SET NOCOUNT ON;
IF NOT (UPDATE(User_fullname) OR UPDATE(User_password) OR UPDATE(User_username) OR UPDATE(User_position_id_r))
RETURN; -- this only tells you if the column was present
INSERT INTO UsersLogs
(UL_user_key_r, UL_date, UL_IsDeleted,
UL_oldname, UL_newname,
UL_oldpass, UL_newpass,
UL_oldusername, UL_newusername,
UL_oldPosid, UL_newPosid)
SELECT
i.user_key, GETDATE(), 0,
CASE WHEN d.fullname <> i.fullname THEN d.fullname END, CASE WHEN d.fullname <> i.fullname THEN i.fullname END
CASE WHEN d.User_password <> i.User_password THEN d.User_password END, CASE WHEN d.User_password <> i.User_password THEN i.User_password END,
CASE WHEN d.User_username <> i.User_username THEN d.User_username END, CASE WHEN d.User_username <> i.User_username THEN i.User_username END,
CASE WHEN d.User_position_id_r <> i.User_position_id_r THEN d.User_position_id_r END, CASE WHEN d.User_position_id_r <> i.User_position_id_r THEN i.User_position_id_r END
FROM inserted i
JOIN deleted d ON i.user_key = d.user_key
WHERE EXISTS ( -- this checks for any differences
SELECT i.fullname, i.User_password, i.User_username, i.User_position_id_r
EXCEPT -- this will deal with nulls correctly
SELECT d.fullname, d.User_password, d.User_username, d.User_position_id_r
);
Если вы хотите, чтобы для каждого измененного значения были отдельные строки, вы можете отменить вывод с помощью CROSS APPLY
Комментарии:
1. С этим есть проблема, и она заключается в том, что она заполняет все столбцы и не устанавливает для неизмененных значение null
2. Хорошо, я дал вам
CASE
решения для этого3. 🙂 tnQ И Да, это работает просто отлично, так что дело
UPDATE()
в том, чтобы следить за текущими столбцами… хорошо
Ответ №2:
update()
функция триггера возвращает значение true , если столбец обновляется, даже если он имеет то же значение. таким образом, похоже, что при обновлении пароля вы обновляете столбец User_fullname и, вероятно, другие столбцы (с тем же значением, что и раньше, конечно) . поэтому UPDATE (User_fullname) возвращает значение true .
Но также и то, как вы написали свой код , триггер работает в ситуации , когда обновляется только один столбец , и в этом порядке в вашем коде , например, если UPDATE (User_fullname)
верно, ваш код не проверяет другие условия из-за ELSE IF
. возможно , вы захотите удалить else
и проверить каждый столбец или полностью изменить свою стратегию, чтобы регистрировать данные внутри триггера.
затем , основываясь на вашем комментарии, избавьтесь от всего, если еще, и сделайте одно подобное заявление insert для всех столбцов:
INSERT INTO UsersLogs (UL_user_key_r,UL_date,UL_oldpass,UL_newpass,<all columns>)
select (@user_key, GETDATE(), case when deleted.password <> new.password then deleted.password else null end , case when deleted.password <> new.password then inserted.password else null end , ....)
from inserted i
join deleted d on i.userkey = d.userkey
Комментарии:
1. Да, у меня возникли подозрения, и я удалил
ELSE
часть»», и угадайте, что произошло? он вставил 8 строк в таблицу журнала, что было нормально в том смысле, что он мог регистрировать все изменения, но… Я хочу сохранить свою базу данных в максимально уменьшенном виде… таким образом, для других будет установлено значение null, но я не хочу, чтобы в моей таблице было 8 строк2. Я думаю , что ошибка связана с хранимой процедурой, потому что, когда я заменяю значение его собственным значением, оно не воспринимается как обновление или изменение, но когда хранимая процедура обновляет столбец своим собственным значением, она рассматривает его как обновление…. Поэтому я изменяю некоторые части кода.. Я удалил
else
часть и установил другое условие, чтобы проверитьold value
, равно лиnew value
значение, и если это не было, вставьте журнал, он уменьшает 8 строк до 3 или даже меньше или других в зависимости от измененных столбцов, но если я найду способ вставить все изменения в одну строку, это было бы здорово 🙂3. Хорошо, я только что видел вашу обновленную версию, дайте мне проверить ofc, я думаю, что это работает нормально
4. @Charlieface да, но у пользователей в моей базе данных есть по одной строке, и меня это устраивает
5. @AtrinNoori Я все равно обновил свой ответ, чтобы отразить это в любом случае,