Trigger update and handle null values - sql-server-2008

I Have a trigger that takes the old values from the orginal tabel and put these values in one cell on another table except the date and user, when any updates occurs, the orginal table has attributes which is null when the raw is inserted like (updatedate and updateuser), so when this raw is updated those two attributes will be update to the current date and user who did the update, so the trigger should get those two parameters when they are null should get the inserted values and when they are not null should get the new values,however the trigger's syntax is correct but the result is incorrect
ALTER Trigger [dbo].[OrdersUpdate] on [dbo].[Orders]
After Update
as
Declare #UpdateDate datetime, #UpdateUser uniqueidentifier
Set #UpdateDate=(Select UpdateDate from deleted)
Set #UpdateUser=(Select UpdateUser from deleted)
If #UpdateDate is null
Begin
Set #UpdateDate=(select UpdateDate from inserted)
End
Else
Set #UpdateDate=(select UpdateDate from deleted)
If #UpdateUser is null
Begin
Set #UpdateUser=(select UpdateUser from inserted)
End
Else
Set #UpdateUser=(select UpdateUser from deleted)
Insert Into UpdateRows
Select 'Orders', id,#UpdateDate,#UpdateUser,
convert(nvarchar,InvoiceID,1)+'_'+
convert(nvarchar,OrderID,1)+'_'+
MatterName+'_'+
convert(nvarchar,PrintID,1)+'_'+
OrderName+'_'+
CONVERT(nvarchar,lenght,1)+'_'+
CONVERT(nvarchar,Wide,1)+'_'+
CONVERT(nvarchar,Quantity,1)+'_'+
CONVERT(nvarchar,EnterDate,101)+'_'+
CONVERT(nvarchar,EndDate,101)
From deleted

thanks HLGEM
First, you never ever write a sql server trigger where you set the values of a scalar parameter to something from inserted or deleted. These tables can contain multiple rows. The trigger must account for that. โ€“ HLGEM

Related

Exclude some fields from updating a field with default value of CURRENT_TIMESTAMP

I have a table in MySQL with 4 fields as id, name, gender and timestamp.
Default value of timestamp is CURRENT_TIMESTAMP so if name or gender changes, timestamp updates to current date and time.
I want to exclude gender field from updating that timestamp.
Is that possible?
I would use a MySQL Trigger to accomplish this. You did not provide a table name so I'll call your table PERSON.
The trigger should look something like the following:
delimiter ;;
DROP TRIGGER IF EXISTS `TRIGGER_PERSON_AFTER_UPDATE`;;
CREATE TRIGGER `TRIGGER_PERSON_AFTER_UPDATE` AFTER UPDATE ON PERSON
FOR EACH ROW
BEGIN
IF (NEW.name != OLD.name) THEN
NEW.time_stamp = CURRENT_TIMESTAMP;
END IF;
END;;
delimiter ;
You have to specify the same value for timestamp when updating gender. I assume that if you change other fields together with gender, then timestamp should update.
UPDATE my_table SET gender = ?, `timestamp`=`timestamp`

MySQL trigger to insert date to row with unique matching ID

I need to write an SQL trigger to insert the date into a row of another table after an insert.
The data comes in as such:
A form is submitted to _OnsiteTable which includes a JobID, this JobID matches a current entry containing a JobID value in _JobTable. Upon insert of the data into _OnsiteTable I need a trigger that will insert the current date into a column called JobClosedDate in JobTable.
It needs to insert the date into the row that matches the JobID value entered in the form on _OnsiteTable.
I have this so far but I don't know how to compare the JobID values between tables and insert into the specific row.
CREATE TRIGGER UpdateClosedDate
AFTER INSERT on _OnsiteTable
BEGIN
Declare ClosedDate DATETIME;
SET ClosedDate = CURDATE();
CASE WHEN (ClosedDate) = ?
THEN INSERT INTO `_JobTable`( `JobClosedDate`)
VALUES
( ClosedDate);
END CASE;
END
Any help is greatly appreciated!
Complete query now working, thank you! As below:
CREATE TRIGGER UpdateClosedDate
AFTER INSERT on _OnsiteTable
BEGIN
Declare ClosedDate DATETIME;
SET ClosedDate = CURDATE();
UPDATE `_JobTable` SET `JobClosedDate` = ClosedDate WHERE `JobID` = new.JobID;
END
Since the problem was solved with the given comment I will add it here to reference for others with similar issues:
Since you want to Update a column ("insert a value into a column...") You just need to fix your trigger changing your current case statement to an update command as follow:
UPDATE `_JobTable`
SET `JobClosedDate` = ClosedDate
WHERE `JobID` = NEW.JobID;

Insert only when auto-increment id is not equal 6(for example)?

I have a table with 3 fields: Id(PK,AI), Name(varchar(36)), LName(varchar(36)).
I have to insert name and last name, Id inserts automatically because of it's constraints,
Is There a way to Jump id auto increment value when it reaches 6?
for instance do this 7 times:
Insert Into table(Name, LName) Values ('name1', 'lname1') "And jump id to 7 if it is going to be 6"
It may sound stupid to do this but I have the doubt.
Also Jump and do not record id 6.
record only, 1-5, 7,8,9 and so on
What I want to achieve starts from a Union:
Select * From TableNames
Union All
Select * From TableNames_general
In the TableNames_general I assign it's first value so that when the user sees the table for the first time it will be displayed the record I inserted.
The problem comes when the user inserts a new record, if the Id of the inserted record is the same as the one I have inserted it will be duplicated, that is why I want to achieve when the users inserts one record and if the last insert id already exists just jump that record. this is because I must have different ids due to its relationship among child tables.
Identity column generate values for you, And its best left this way, You have the ability to insert specific values in Identity column but its best left alone and let it generate values for you.
Imagine you have inserted a value explicitly in an identity column and then later on Identity column generates the same value for you, you will end up with duplicates.
If you want to have your input in that column then why bother with identity column anyway ??
Well this is not the best practice but you can jump to a specific number by doing as follows:
MS SQL SERVER 2005 and Later
-- Create test table
CREATE TABLE ID_TEST(ID INT IDENTITY(1,1), VALUE INT)
GO
-- Insert values
INSERT INTO ID_TEST (VALUE) VALUES
(1),(2),(3)
GO
-- Set idnentity insert on to insert values explicitly in identity column
SET IDENTITY_INSERT ID_TEST ON;
INSERT INTO ID_TEST (ID, VALUE) VALUES
(6, 6),(8,8),(9,9)
GO
-- Set identity insert off
SET IDENTITY_INSERT ID_TEST OFF;
GO
-- 1st reseed the value of identity column to any smallest value in your table
-- below I reseeded it to 0
DBCC CHECKIDENT ('ID_TEST', RESEED, 0);
-- execute the same commad without any seed value it will reset it to the
-- next highest idnetity value
DBCC CHECKIDENT ('ID_TEST', RESEED);
GO
-- final insert
INSERT INTO ID_TEST (VALUE) VALUES
(10)
GO
-- now select data from table and see the gap
SELECT * FROM ID_TEST
If you query the database to get the last inserted ID, then you can check if you need to increment it, by using a parameter in the query to set the correct ID.
If you use MSSQL, you can do the following:
Before you insert check for the current ID, if it's 5, then do the following:
Set IDENTITY_INSERT to ON
Insert your data with ID = 7
Set IDENTITY_INSERT to OFF
Also you might get away with the following scenario:
check for current ID
if it's 5, run DBCC CHECKIDENT (Table, reseed, 6), it will reseed the table and in this case your next identity will be 7
If you're checking for current identity just after INSERT, you can use SELECT ##IDENTITY or SELECT SCOPE_IDENTITY() for better results (as rcdmk pointed out in comments)
Otherwise you can just use select: SELECT MAX(Id) FROM Table
There's no direct way to influence the AUTO_INCREMENT to "skip" a particular value, or values on a particular condition.
I think you'd have to handle this in an AFTER INSERT trigger. An AFTER INSERT trigger can't update the values of the row that was just inserted, and I don't think it can make any modifications to the table affected by the statement that fired the trigger.
A BEFORE INSERT trigger won't work either, because the value assigned to an AUTO_INCREMENT column is not available in a BEFORE INSERT trigger.
I don't believe there's a way to get SQL Server IDENTITY to "skip" a particular value either.
UPDATE
If you need "unique" id values between two tables, there's a rather ugly workaround with MySQL: roll your own auto_increment behavior using triggers and a separate table. Rather than defining your tables with AUTO_INCREMENT attribute, use a BEFORE INSERT trigger to obtain a value.
If an id value is supplied, and it's larger than the current maximum value from the auto_increment column in the dummy auto_increment_seq table, we'd need to either update that row, or insert a new one.
As a rough outline:
CREATE TABLE auto_increment_seq
(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=MyISAM;
DELIMITER $$
CREATE TRIGGER TableNames_bi
BEFORE INSERT ON TableNames
FOR EACH ROW
BEGIN
DECLARE li_new_id INT UNSIGNED;
IF ( NEW.id = 0 OR NEW.id IS NULL ) THEN
INSERT INTO auto_increment_seq (id) VALUES (NULL);
SELECT LAST_INSERT_ID() INTO li_new_id;
SET NEW.id = li_new_id;
ELSE
SELECT MAX(id) INTO li_max_seq FROM auto_increment_seq;
IF ( NEW.id > li_max_seq ) THEN
INSERT INTO auto_increment_seq (id) VALUES (NEW.id);
END IF;
END IF;
END$$
CREATE TRIGGER TableNames_ai
AFTER INSERT ON TableNames
FOR EACH ROW BEGIN
DECLARE li_max_seq INT UNSIGNED;
SELECT MAX(id) INTO li_max_seq FROM auto_increment_seq;
IF ( NEW.id > li_max_seq ) THEN
INSERT INTO auto_increment_seq (id) VALUES (NEW.id);
END IF;
END;
DELIMITER ;
The id column in the table could be defined something like this:
TableNames
( id INT UNSIGNED NOT NULL DEFAULT 0 PRIMARY KEY
COMMENT 'populated from auto_increment_seq.id'
, ...
You could create an identical trigger for the other table as well, so the two tables are effectively sharing the same auto_increment sequence. (With less efficiency and concurrency than an Oracle SEQUENCE object would provide.)
IMPORTANT NOTES
This doesn't really insure that the id values between the tables are actually kept unique. That would really require a query of the other table to see if the id value exists or not; and if running with InnoDB engine, in the context of some transaction isolation levels, we might be querying a stale (as in, consistent from the point in time at the start of the transaction) version of the other table.
And absent some additional (concurrency killing) locking, the approach outline above is subject to a small window of opportunity for a "race" condition with concurrent inserts... the SELECT MAX() from the dummy seq table, followed by the INSERT, allows a small window for another transaction to also run a SELECT MAX(), and return the same value. The best we can hope for (I think) is for an error to be thrown due to a duplicate key exception.
This approach requires the dummy "seq" table to use the MyISAM engine, so we can get an Oracle-like AUTONOMOUS TRANSACTION behavior; if inserts to the real tables are performed in the context of a REPEATABLE READ or SERIALIZABLE transaction isolation level, reads of the MAX(id) from the seq table would be consistent from the snapshot at the beginning of the transaction, we wouldn't get the newly inserted (or updated) values.
We'd also really need to consider the edge case of an UPDATE of row changing the id value; to handle that case, we'd need BEFORE/AFTER UPDATE triggers as well.

MERGE leads to 'Subquery returned more than 1 value' error in trigger

Below is the trigger that is on the table and the query I am using for updating.
Trigger
This trigger will update a Type ID column based on the text inserted or updated into another column...This is to force typing on a badly designed table
CREATE TRIGGER [dbo].[TypeIDInsert]
ON [dbo].[Table1]
AFTER INSERT, UPDATE
AS
BEGIN
IF (SELECT TypeID FROM inserted) IS NULL
BEGIN
DECLARE #ID [int] = (SELECT ID FROM inserted);
UPDATE Table1 SET TypeID = (
CASE TypeName
WHEN 'Value 1' THEN 1
WHEN 'Value 2' THEN 2
WHEN 'Value 3' THEN 3
ELSE 0
END
)
WHERE ID = #ID
END
END
The ID column is an IDENTITY column.
Update Statement
MERGE INTO Table1
USING Table1TypeTable
ON Table1.TypeName= TypeTable.TypeName
WHEN MATCHED THEN
UPDATE SET TypeID = TypeTable.TypeID;
When the trigger is enabled I get the error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I found this post Update statement error: Subquery returned more than 1 value describing that the issue is the trigger on the table was designed for only one record at a time. I disabled the trigger and was able to successfully do the update. My question is how to write the trigger as to not cause an issue on a mass update like this.
A couple of things: your trigger assumed that all operations only affect one row. The way to do this is to join to the inserted table, instead of trying to stuff a single variable with the ID value from who knows what row. Also the check against inserted (which in your example is missing EXISTS) is not really necessary. If there are 0 rows in inserted the update won't be performed anyway.
ALTER TRIGGER [dbo].[TypeIDInsert]
ON [dbo].[Table1]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE t SET t.TypeID = CASE i.TypeName
WHEN 'Value 1' THEN 1
WHEN 'Value 2' THEN 2
WHEN 'Value 3' THEN 3
ELSE 0 END
FROM dbo.Table1 AS t
INNER JOIN inserted AS i
ON t.ID = i.ID;
END
GO

creating triggers and functions in postgresql

how can i create a trigger function before adding/updating,the function should check records that have the same id (i.e comparison by id with existing objects that have the same property as the temporary_object)If a record with the id is found, then that entry is set to the time_dead, and then it adds an entry containing the corresponding values โ€‹โ€‹of the attributes found in that record (except those that are set for a new record), when time_dead is empty then time_create of a new time is equal to that time at the current moment . Thus,a new record time_create is like the time_dead's ancestor.
If a record with that id is found then it is added to the database with the establishment as the time_create of the current time.
for example here is a simple explanation(just for explanation purposes)
id time_create time-dead student amount
1 06.12 07.12 henry 500
1 07.12 henry 1000
so if a student called henry with id 1 entered a room at 06.12 and left at 07.12 the next time he enters another room again time_dead will be equal to time_create(so time_dead of old entry and time_create of new entry - will be equal)
these are my tables below in sql format
CREATE TABLE temporary_object
(
id integer NOT NULL,
time_create timestamp without time zone NOT NULL,
time_dead timestamp without time zone,
CONSTRAINT temporary_object_pkey PRIMARY KEY (id, time_create)
)
CREATE TABLE persons
(
fname text,
fsurname text,
)
INHERITS (temporary_object)
CREATE TABLE rooms
(
roomnum integer,
course integer,
passport text,
students_number text
)
INHERITS (temporary_object)
this is what i am trying to do but im afraid i do not know how to finish it but im 100% not right may some help out
CREATE TRIGGER trigger2
BEFORE INSERT OR UPDATE
ON persons
FOR EACH ROW
EXECUTE PROCEDURE func1();
and this is the function
CREATE OR REPLACE FUNCTION func1() RETURNS TRIGGER AS $persons$
DECLARE
time_create integer;
time_dead timestamp;
id timestamp;
BEGIN
IF (TG_OP = 'INSERT') THEN
time_create=
I can't tell you what I'm missing from your question, but I try to answer what I think I understood.
Row level triggers can access the version of the row affected with the NEW and OLD variables (depending on TG_OP). In this case, you can use NEW:
CREATE OR REPLACE FUNCTION func1()
RETURNS TRIGGER AS
$persons$
DECLARE
i integer;
BEGIN
IF TG_OP = 'INSERT'
THEN
UPDATE persons
SET time_dead = NEW.time_create
WHERE
id = NEW.id -- we are looking for the same ID
AND time_dead IS NULL
;
ELSE -- UPDATE
-- do here something
END IF;
END;
$persons$
LANGUAGE plpgsql;
This is only a starter, modify it to your needs.