confused on the logic SQL (log Triggers) - sql

I am confused on the logic of this question, my prof did not teach us anything regarding this.... can someone explain it to me and this is my horrible example of what i did> i definitely need to fixed the
Inserted information and the stored values.
Create A trigger called TR_5 to log changes made to the CostPerHour of the services. When change is made to the CostPerHour add a record to the CostPerHourLog table (shown below). However, DO NOT record a change if the value of the CostPerHour did not change! Show the code to create the trigger and to create the table. All the table attributes are required.
Create Table CostPerHourLog
(
LogID [int]Identity(1,1) NOT NULL,
ChangeDateTime smalldatetime,
ServiceCode varchar(15),
Description varchar(100),
OldCostPerHour smallmoney,
NewCostPerHour smallmoney
)
Drop trigger TR_5
go
Create trigger TR_5
on CostPerHour
for update
as
if ##rowcount<0
begin
if not exists (select * from costperhourlog)
insert into CostperHourLog
(LogID,ChangeDateTime,ServiceCode,Description,OldCostPerHour,NewCostPerHour)
Values
(LogID,ChangeDateTime,ServiceCode,Description,OldCostPerHour,NewCostPerHour)
end
return

ALTER TRIGGER TR_5
ON CostPerHour
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
if exists(select * from inserted except select * from deleted)
begin
insert into CostPerHourLog
end
END
GO

Related

MSSQL Trigger for update is working only once

I'm going to create a trigger for update. Purpose of this trigger is that If muadurum column is changed , take the old value of mua_tarih in table fkayitlar and insert to another table mua_tarihleri.
My code block like;
ALTER TRIGGER [dbo].[trgr_fkayit_update]
ON [dbo].[fkayitlar]
AFTER UPDATE
AS
DECLARE #mua_durum_once NVARCHAR(10)
DECLARE #mua_durum_sonra NVARCHAR(10)
DECLARE #mua_tarih_once DATE
DECLARE #mua_yapan_once NVARCHAR(25)
DECLARE #kisi_id INT
Take the old value;
SELECT
#kisi_id=kayitid,
#mua_durum_once=muayenedurum,
#mua_tarih_once=muayenetarih,
#mua_yapan_once=mua_yapan
FROM deleted
Take the new value;
SELECT #mua_durum_sonra=muayenedurum FROM inserted
Check if value is changed ; if changed, Insert #mua_tarih to table mua_tarihleri with #kisi_id and #mua_yapan_once
IF #mua_durum_once='OLDU'
AND #mua_durum_sonra='OLMADI'
AND #mua_tarih_once IS NOT NULL
BEGIN
INSERT INTO mua_tarihleri(kayitid,mua_tarihi,mua_yapan)
VALUES(#kisi_id,#mua_tarih_once,#mua_yapan_once)
END
My problem is When I update more than one row in table fkayitlar,Trigger is working, but I see only one inserted row in table mua_tarihleri (only working once). I need to see more than one.(should be working more than once) Are not Triggers working on more than one process? or How can I solve this my problem?
The trigger only occurs once when the table is updated, no matter how many rows are updated. Therefore, you have to write your trigger body to operate on a set of rows, not a single row as you have done.
Should be something like:
ALTER TRIGGER [dbo].[trgr_fkayit_update]
ON [dbo].[fkayitlar]
AFTER UPDATE
AS
INSERT INTO mua_tarihleri(kayitid,mua_tarihi,mua_yapan)
SELECT deleted.kayitid, deleted.muayenedurum, deleted.muayenetarih, deleted.mua_yapan
FROM deleted
JOIN inserted ON deleted.kayitid = inserted.kayitid
WHERE deleted.muayenedurum='OLDU'
AND inserted.muayenedurum='OLMADI'
AND muayenetarih IS NOT NULL

Creating a trigger instead of UPDATE on a view

I have a view and a trigger that overwrites UPDATE. I try to understand the code, however I am having some troubles with line from deleted and from inserted - Are these automatically created by original update? Does it mean that if I call this trigger, first the update gets called and then the trigger?
create trigger updateView on View
instead of UPDATE as
begin
declare #nameK VARCHAR(100), #addresK VARCHAR(100), #nameZ VARCHAR(100), #number INT, #date DATE;
declare #nameKo VARCHAR(100), #addresKo VARCHAR(100), #nameZo VARCHAR(100), #numbero INT, #dateo DATE; -- 'o'ld
declare insCur cursor for select name, addres, bandName, number, date from inserted;
declare delCur cursor for select name, addres, bandName, number, date from deleted;
open insCur;
open delCur;
...
This trigger will fire instead of performing the update. The inserted and deleted tables are made available so that you can determine:
The new values for each updated row in inserted
The previous (in this case current, since the normal update operation has not been performed) values from each updated row in deleted
The convention of inserted and deleted is used regardless of the type of trigger. So, for example, in an after update trigger, which simply follows the execution of a normal update, the deleted table would be the primary way to know the previous values.
The deleted contains old values, the inserted - new values.
There are options: AFTER UPDATE or BEFORE UPDATE which you can set to see behavior which you want.
About Deleted and Inserted:
UPDATE dbo.Client SET Name = 'John' WHERE Name = 'Steve'
you will have: deleted value 'Steve', inserted value 'John'
Also I would recommend to avoid using cursors inside triggers, i.e. it can hit performance a lot.

INSERTED magic table in case of in place Update

Today I saw a nice post in which the author has proven that if you are updating a column of a table without any indexes defined, an "in place update" will occur and not traditional delete\insert.
Considering this I run a small test where I have created an Update trigger on table and try to access the INSERTED magic table and here is the catch.
I am able to access the INSERTED magic table, can someone explain me if in place update is not using traditional Delete\Insert? How come one can access the magic tables?
These are my SQL statement to prove this thing.
Main table :
CREATE TABLE TestingUpdate1 (
ID INT IDENTITY,
SomeString CHAR(50)
)
INSERT INTO TestingUpdate1 (SomeString)
VALUES
('One'),('Two'),('Three'),('Four'),('Five'),('Six'),('Seven'),('Eight'),('Nine')
CHECKPOINT -- truncate the log, DB is in simple recovery.
UPDATE TestingUpdate1
SET SomeString = 'NotFour'
WHERE ID = 4 -- one row
SELECT Operation, Context, AllocUnitName, [Transaction Name], Description FROM fn_dblog(NULL, NULL) AS TranLog
Second table :
CREATE TABLE TestingUpdate4 (
ID INT IDENTITY,
SomeString CHAR(50)
)
INSERT INTO TestingUpdate4 (SomeString)
VALUES
('One'),('Two'),('Three'),('Four'),('Five'),('Six'),('Seven'),('Eight'),('Nine')
Trigger :
CREATE TRIGGER ViewCustomerTrigger ON TestingUpdate1
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE TestingUpdate4
SET SomeString = i.SomeString
FROM INSERTED i
END
GO
select * from TestingUpdate4
select * from TestingUpdate1
Thanks in advance
The INSERTED and DELETED tables in an update trigger always show the logical before and after versions of the updated rows.
Whether that is physically implemented as an in place update or as an insert/delete is execution plan dependant and irrelevant to the contents of these tables.

Stored Procedure - Knowing the next ID

I am creating a stored procedure to create a new customer so for instance,
CREATE PROCEDURE Customer_Create
#customer_arg
#type_arg
AS
BEGIN
INSERT INTO Customer (Customer_id, Type_id)
VALUES (#Customer_arg,#type_arg)
End;
If I have several foreign keys in my statement and they are all ID's is there a way for me to pull the NEXT ID number automatically without having to know what it would be off the top of my head when I run the execute statement? I would like to just have it pull the fact that the ID will be 2 because the previous record was 1
EXECUTE Customer_Create 16,2
Is it something wnith output? If so how does this work code wise
I suspect that what you want to do is return the new id after the record is inserted. For that:
CREATE PROCEDURE Customer_Create (
#customer_arg,
#type_arg,
#NewCustomerId int output
) AS
BEGIN
INSERT INTO Customer(Customer_id, Type_id)
VALUES (#Customer_arg, #type_arg);
#NewCustomerId = scope_identity();
End;
There are several other choices for getting the identity, which are explained here.
To get to the last inserted IDENTITY value you should use the OUTPUT clause like this:
DECLARE #IdentValues TABLE(v INT);
INSERT INTO dbo.IdentityTest
OUTPUT INSERTED.id INTO #IdentValues(v)
DEFAULT VALUES;
SELECT v AS IdentityValues FROM #IdentValues;
There are several other mechanisms like ##IDENTITY but they all have significant problems. See my Identity Crisis article for details.
In your case you can also experiment with #IDENTITY like this
DECLARE #NextID int
--insert statement goes here
SET #NextID = ##Identity`
Here are couple good resources for getting familiar with this
http://blog.sqlauthority.com/2007/03/25/sql-server-identity-vs-scope_identity-vs-ident_current-retrieve-last-inserted-identity-of-record/
http://blog.sqlauthority.com/2013/03/26/sql-server-identity-fields-review-sql-queries-2012-joes-2-pros-volume-2-the-sql-query-techniques-tutorial-for-sql-server-2012/

SQL Server - Get Inserted Record Identity Value when Using a View's Instead Of Trigger

For several tables that have identity fields, we are implementing a Row Level Security scheme using Views and Instead Of triggers on those views. Here is a simplified example structure:
-- Table
CREATE TABLE tblItem (
ItemId int identity(1,1) primary key,
Name varchar(20)
)
go
-- View
CREATE VIEW vwItem
AS
SELECT *
FROM tblItem
-- RLS Filtering Condition
go
-- Instead Of Insert Trigger
CREATE TRIGGER IO_vwItem_Insert ON vwItem
INSTEAD OF INSERT
AS BEGIN
-- RLS Security Checks on inserted Table
-- Insert Records Into Table
INSERT INTO tblItem (Name)
SELECT Name
FROM inserted;
END
go
If I want to insert a record and get its identity, before implementing the RLS Instead Of trigger, I used:
DECLARE #ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT #ItemId = SCOPE_IDENTITY();
With the trigger, SCOPE_IDENTITY() no longer works - it returns NULL. I've seen suggestions for using the OUTPUT clause to get the identity back, but I can't seem to get it to work the way I need it to. If I put the OUTPUT clause on the view insert, nothing is ever entered into it.
-- Nothing is added to #ItemIds
DECLARE #ItemIds TABLE (ItemId int);
INSERT INTO vwItem (Name)
OUTPUT INSERTED.ItemId INTO #ItemIds
VALUES ('MyName');
If I put the OUTPUT clause in the trigger on the INSERT statement, the trigger returns the table (I can view it from SQL Management Studio). I can't seem to capture it in the calling code; either by using an OUTPUT clause on that call or using a SELECT * FROM ().
-- Modified Instead Of Insert Trigger w/ Output
CREATE TRIGGER IO_vwItem_Insert ON vwItem
INSTEAD OF INSERT
AS BEGIN
-- RLS Security Checks on inserted Table
-- Insert Records Into Table
INSERT INTO tblItem (Name)
OUTPUT INSERTED.ItemId
SELECT Name
FROM inserted;
END
go
-- Calling Code
INSERT INTO vwItem (Name)
VALUES ('MyName');
The only thing I can think of is to use the IDENT_CURRENT() function. Since that doesn't operate in the current scope, there's an issue of concurrent users inserting at the same time and messing it up. If the entire operation is wrapped in a transaction, would that prevent the concurrency issue?
BEGIN TRANSACTION
DECLARE #ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT #ItemId = IDENT_CURRENT('tblItem');
COMMIT TRANSACTION
Does anyone have any suggestions on how to do this better?
I know people out there who will read this and say "Triggers are EVIL, don't use them!" While I appreciate your convictions, please don't offer that "suggestion".
You could try SET CONTEXT_INFO from the trigger to be read by CONTEXT_INFO() in the client.
We use it the other way to pass info into the trigger but would work in reverse.
Have you in this case tried ##identity? You mentioned both scope_Identity() and identity_current() but not ##identity.