I have an activity from my SQL Server class and I'm missing only this trigger to complete it.
My teacher has asked in this activity to do the following trigger operation on a column sequential on telefone table.
After an insert statement, always keep the values in sequence like the first record is 0, the next should be 1 and there it goes.
And for the delete statement if someone removes a record the sequential value should be reordered.
I've got the insert statement working fine creating a sequence and adding it's next value to the column.
But no lucky with the delete statement.
Here's what I have so far for the trigger:
CREATE TRIGGER tg_sequencial
ON Telefone
AFTER INSERT, UPDATE
AS
BEGIN
DECLARE #vSequencial_Ins INT, #vSequencial_Del INT
SELECT #vSequencial_Ins = inserted.Sequencial FROM inserted
SELECT #vSequencial_Del = deleted.Sequencial FROM deleted
-- Check if it is an insert operation
if(#vSequencial_Ins IS NOT NULL AND #vSequencial_Del IS NULL)
begin
update Telefone set Sequencial = NEXT VALUE FOR dbo.Sequencial
from Telefone tel
join inserted i on i.IdPessoa = tel.IdPessoa
end
-- Check if it is an delete operation
else if(#vSequencial_Ins IS NULL AND #vSequencial_Del IS NOT NULL)
begin
alter sequence dbo.Sequencial
restart with 0
end
end
And this is the sequence code:
CREATE SEQUENCE dbo.Sequencial
START WITH 0
INCREMENT BY 1
NO CACHE
;
GO
My telefone table has the following values:
Sequencial smallint not null [PK]
IdPessoa int not null[PFK],
IdTipoTelefone tinyint not null [FK],
NumeroTelefone int not null,
DDD tinyint not null
EDIT: Got insert trigger working fine after changing the code from:
update Telefone set Sequencial = NEXT VALUE FOR dbo.Sequencial
To:
update Telefone set Sequencial = NEXT VALUE FOR dbo.Sequencial
from Telefone tel
join inserted i on i.IdPessoa = tel.IdPessoa
It now affects just the selected row instead of increasing all the fields.
Related
I have this trigger
ALTER TRIGGER [dbo].[tInsertTaskFromOpportunityReassignment]
ON [dbo].[OpportunityBase]
FOR UPDATE
AS
BEGIN
IF UPDATE(owninguser)
BEGIN
-- do the task
END
END
I only want to do the task if owninguser has actually changed. How can I determine that?
Thanks
ALTER TRIGGER [dbo].[tInsertTaskFromOpportunityReassignment]
ON [dbo].[OpportunityBase]
FOR UPDATE
AS
BEGIN
DECLARE HasChanged int = 0;
SELECT #HasChanged = 1
FROM Inserted AS I
INNER JOIN Deleted AS D
ON I.PK = D.PK
AND IsNull(I.owninguser,'~') <> IsNull(D.owninguser,'~')
IF #HasChanged = 1
BEGIN
-- do the task
END
END
Compare the values of the field between Inserted and Deleted, joining the two tables on the primary key.
ALTER TRIGGER [dbo].[tInsertTaskFromOpportunityReassignment]
ON [dbo].[OpportunityBase]
FOR UPDATE
AS
BEGIN
/*
I will assume that your [dbo].[OpportunityBase] table has a PRIMARY KEY
or UNIQUE column that is immutable to join the inserted and deleted
tables. In this example, [OpportunityBaseId] is that column.
The SELECT query returns a set of all records that had the value of [owninguser]
changed. What you would do from that point is up to you.
*/
SELECT
i.[OpportunityBaseId], i.owninguser New_owninguser, d.owninguser Old_owninguser
FROM inserted i
JOIN deleted d
ON i.[OpportunityBaseId] = d.[OpportunityBaseId]
AND
(
--owninguser value was changed.
i.[owninguser] <> d.[owninguser] OR
--owninguser changed from non-NULL to NULL.
(i.[owninguser] IS NULL AND d.[owninguser] IS NOT NULL) OR
--owninguser changed from NULL to non-NULL.
(i.[owninguser] IS NOT NULL AND d.[owninguser] IS NULL)
)
END
GO
I like to use concat although it may potentially cause you problems with NULL and '':
CONCAT('', inserted.owninguser) <> CONCAT('', deleted.owninguser)
Here is a trigger
CREATE TRIGGER [dbo].[CheckApplyId]
ON [dbo].[AppliedStudent_event] INSTEAD OF INSERT
AS
DECLARE #studentId INT
DECLARE #compReq_Id INT
BEGIN
SELECT #studentId = studentId
FROM INSERTED
SELECT #compReq_Id = compReq_Id
FROM INSERTED
IF EXISTS(SELECT StudentId,
compreq_id
FROM AppliedStudent_event
WHERE StudentId = #studentId
AND compreq_id = #compReq_Id)
BEGIN
ROLLBACK
PRINT 'User Already Applied'
END
END
When in insert a data into a table using command:
INSERT INTO AppliedStudent_event (StudentId, compreq_id)
VALUES (3026, 1)
Message is:
(1 row(s) affected)
But when I execute a sql command no data is inserted in the table.
Can you please tell why are you using trigger because you use only assign the variable #studentId and #compReq_Id from inserted table.
That's a broken trigger because inserted can contain multiple (or no) rows - so a statement like SELECT #ScalarVariable = column from inserted is always wrong.
And it's unnecessary since you can just place a UNIQUE constraint on the StudentId and compreq_id columns:
ALTER TABLE AppliedStudent_event
ADD CONSTRAINT UQ_Student_Events
UNIQUE (StudentId,compreq_id)
And it's further broken because you've specified it as an instead of trigger - that says that your code is going to be responsible for the actual insert - but your code doesn't actually do that. That's why no data ends up in the table.
If you insist on doing it as a trigger, it's actually tricky to get everything correct (that's why I'd really recommend the UNIQUE constraint). It'll end up being something like this:
CREATE TRIGGER [dbo].[CheckApplyId]
ON [dbo].[AppliedStudent_event] INSTEAD OF INSERT
AS
IF EXISTS(select
StudentId,compreq_id,COUNT(*)
from inserted
group by StudentId,compreq_id
HAVING COUNT(*) > 1)
OR EXISTS (select *
from inserted i
inner join
AppliedStudent_event e
on
i.StudentId = e.StudentId and
i.compreq_id = e.compreq_id)
BEGIN
ROLLBACK
PRINT 'User Already Applied'
END
ELSE
BEGIN
INSERT INTO AppliedStudent_event(StudentId,compreq_id /* Other columns? */)
SELECT StudentId,compreq_id /* And again, other columns */
FROM inserted
END
I have a scenario where one column of the target table needs to be auto incremented . I do not have identity enabled on this column. So i need to pick the last number and add 1 to it , each time an insert is done.
http://sqlfiddle.com/#!6/61eb4/5
A similar scenario is given in the fiddle link. I do not want the productid of ProductChanges table to be inserted. Instead, i need the last id to be picked and i need it to be incremented and inserted for each new row
Code to get this working
DECLARE #intctr int
SELECT #intctr = MAX(productid)+1 from products
DECLARE #strQry varchar(200)
SET #strQry =
'CREATE SEQUENCE dbo.seq_key_prd
START WITH ' +convert( varchar(12),#intctr) +' INCREMENT BY 1 ;'
print #strQry
exec( #strQry)
alter table Products
add default next value for seq_key_prd
for ProductId;
GO
--Merge statement for data sync
MERGE Products USING ProductChanges ON (Products.Productid = ProductChanges.Productid)
WHEN MATCHED AND Products.VendorlD =0 THEN DELETE
WHEN NOT MATCHED by target then insert (productid,Productname,VendorlD)
values(default,productname,VendorlD)
WHEN MATCHED THEN UPDATE SET
Products.ProductName = ProductChanges.ProductName ,
Products.VendorlD = ProductChanges.VendorlD;
1)create sequence and set to target table.
example
CREATE SEQUENCE table_seq
MINVALUE 1
START WITH 1
INCREMENT BY 1
CACHE 20;
2)create trigger for that sequence,to set the table
CREATE OR REPLACE TRIGGER my_trigger
BEFORE INSERT
ON myTable
FOR EACH ROW
WHEN (new.id is null)
DECLARE
v_id qname.qname_id%TYPE;
BEGIN
SELECT table_seq.nextval INTO v_id FROM DUAL;
:new.qname_id := v_id;
END my_trigger;
I have a table called Employee. The EmpId column serves as the primary key. In my scenario, I cannot make it AutoNumber.
What would be the best way of generating the the next EmpId for the new row that I want to insert in the table?
I am using SQL Server 2008 with C#.
Here is the code that i am currently getting, but to enter Id's in key value pair tables or link tables (m*n relations)
Create PROCEDURE [dbo].[mSP_GetNEXTID]
#NEXTID int out,
#TABLENAME varchar(100),
#UPDATE CHAR(1) = NULL
AS
BEGIN
DECLARE #QUERY VARCHAR(500)
BEGIN
IF EXISTS (SELECT LASTID FROM LASTIDS WHERE TABLENAME = #TABLENAME and active=1)
BEGIN
SELECT #NEXTID = LASTID FROM LASTIDS WHERE TABLENAME = #TABLENAME and active=1
IF(#UPDATE IS NULL OR #UPDATE = '')
BEGIN
UPDATE LASTIDS
SET LASTID = LASTID + 1
WHERE TABLENAME = #TABLENAME
and active=1
END
END
ELSE
BEGIN
SET #NEXTID = 1
INSERT INTO LASTIDS(LASTID,TABLENAME, ACTIVE)
VALUES(#NEXTID+1,#TABLENAME, 1)
END
END
END
Using MAX(id) + 1 is a bad idea both performance and concurrency wise.
Instead you should resort to sequences which were design specifically for this kind of problem.
CREATE SEQUENCE EmpIdSeq AS bigint
START WITH 1
INCREMENT BY 1;
And to generate the next id use:
SELECT NEXT VALUE FOR EmpIdSeq;
You can use the generated value in a insert statement:
INSERT Emp (EmpId, X, Y)
VALUES (NEXT VALUE FOR EmpIdSeq, 'x', 'y');
And even use it as default for your column:
CREATE TABLE Emp
(
EmpId bigint PRIMARY KEY CLUSTERED
DEFAULT (NEXT VALUE FOR EmpIdSeq),
X nvarchar(255) NULL,
Y nvarchar(255) NULL
);
Update: The above solution is only applicable to SQL Server 2012+. For older versions you can simulate the sequence behavior using dummy tables with identity fields:
CREATE TABLE EmpIdSeq (
SeqID bigint IDENTITY PRIMARY KEY CLUSTERED
);
And procedures that emulates NEXT VALUE:
CREATE PROCEDURE GetNewSeqVal_Emp
#NewSeqVal bigint OUTPUT
AS
BEGIN
SET NOCOUNT ON
INSERT EmpIdSeq DEFAULT VALUES
SET #NewSeqVal = scope_identity()
DELETE FROM EmpIdSeq WITH (READPAST)
END;
Usage exemple:
DECLARE #NewSeqVal bigint
EXEC GetNewSeqVal_Emp #NewSeqVal OUTPUT
The performance overhead of deleting the last inserted element will be minimal; still, as pointed out by the original author, you can optionally remove the delete statement and schedule a maintenance job to delete the table contents off-hour (trading space for performance).
Adapted from SQL Server Customer Advisory Team Blog.
Working SQL Fiddle
The above
select max(empid) + 1 from employee
is the way to get the next number, but if there are multiple user inserting into the database, then context switching might cause two users to get the same value for empid and then add 1 to each and then end up with repeat ids. If you do have multiple users, you may have to lock the table while inserting. This is not the best practice and that is why the auto increment exists for database tables.
I hope this works for you. Considering that your ID field is an integer
INSERT INTO Table WITH (TABLOCK)
(SELECT CASE WHEN MAX(ID) IS NULL
THEN 1 ELSE MAX(ID)+1 END FROM Table), VALUE_1, VALUE_2....
Try following query
INSERT INTO Table VALUES
((SELECT isnull(MAX(ID),0)+1 FROM Table), VALUE_1, VALUE_2....)
you have to check isnull in on max values otherwise it will return null in final result when table contain no rows .
I have a table called tbl_gallery which has a column of datatype bit called isActive.
When the user updates the IsActive value, other rows with IsActive = true will be automatically turned to false.
How can do it with updated trigger?
Please help
I think you want something like:
CREATE TRIGGER trgGalleryActive
ON dbo.tbl_gallery
FOR UPDATE
AS
BEGIN
UPDATE g
-- Update all other gallery rows for this same user to false
SET g.IsActive = 0
FROM tbl_gallery g
INNER JOIN inserted i
on g.UserPK = i.UserPK
WHERE
-- However, we don't want current inserted records to be updated
g.TablePK <> i.TablePK
-- As per Marc's comment - don't update existing inactive rows unnecessarily
AND g.IsActive = 1
-- Only if this record is active should any of this happen
AND i.IsActive = 1
END
Trigger for update second table after updated first table :
CREATE TRIGGER update_table_cityUpdated_afterTable_cityUpdate
ON Table_city
AFTER UPDATE AS
BEGIN
DECLARE #cityId AS BIGINT
DECLARE #stateId AS BIGINT
DECLARE #CityName AS NVARCHAR(200)
SELECT #cityId=cityId FROM INSERTED
SELECT #stateId= stateId FROM INSERTED
SELECT #CityName= CityName FROM INSERTED
UPDATE table_cityUpdated
SET
[dbo].[table_cityUpdated].stateId=#stateId,
[dbo].[table_cityUpdated].CityName=#CityName
WHERE [dbo].[table_cityUpdated].cityId=#cityId
END
;