Database trigger - sql

I have no experience in writing database trigger but I need one in my current project.
My use case is the following. I have two tables - Table 1 and Table 2.
These tables have a 1 : m relation.
My usecase is, if all records in Table1 have "VALUE2" than value in Table2 should updated to VALUE2.
So if record-value with ID 3 of table1 is updated to VALUE2 than Value of table2 also should be updated to value2.
It would be great if someone could help me - Thanks a lo for than!
TABLE1:
ID FK_Table2 VALUE
-----------------------------
1 77 VALUE2
2 77 VALUE2
3 77 VALUE1
4 54 OTHERVALUE
TABLE2:
ID VALUE
---------------
77 VALUE1

So you need to learn and try basic trigger first.
CREATE OR REPLACE TRIGGER trigger_name
AFTER UPDATE ON TABLE1
FOR EACH ROW
BEGIN
/* trigger code goes here...*/
/* for this particular case you need to update value of table2 */
UPDATE TABLE2 SET VALUE = new.VALUE WHERE TABLE2.ID = new.FK_Table2 ;
END
Try and write some code. IF stucked... come back and let us know...

No matter which system, there are some basic rules or best practices you should know. One is that it is bad form (and outright prohibited in many systems) for a trigger to reach back out and query the very table the trigger is written for. Your use case requires the trigger on Table1 to go back out and read from Table1 during the Update operation. Not good.
One available option is to use a stored procedure to handle all the updates to this table. They are more awkward to work with (for example: if a parameter is NULL, does that mean put a NULL in the corresponding field or leave it unmodified?). For that reason, and with the understanding that this is based on the limited amount of information in the question, I would recommend one of two alternatives.
One is to have a stored procedure that is used only to change the VALUE field. That field is not changed in a vacuum, but as part of a larger process. The step in the process that actually ends up changing the field could then call the SP.
Another is to front the table with a view with an "instead of" trigger and perform all DML through the view. This is the method I prefer, at least on those systems that allow triggers on views. The view trigger may query the underlying table as needed.
As for the logic (SP or trigger) here is some pseudo code:
-- Make the update
update table1 set value = #somevalue
where id = #someid;
-- Get the group that id is in
select FK_Table2 into #somegroupid
from Table1
where id = #someid;
-- Are all the values in that group the same?
select count(*) into #OtherValues
from Table1
where FK_Table2 = #somegroupid
and value <> #somevalue;
-- If so, notify the other table.
if #OtherValues = 0 then
update table2 set value = #somevalue
where id = #somegroupid;
I hope this answers your immediate question. However, based on what you have shown us here, the major cause of the problem would seem to be poor design. Let us know the higher level requirement you are trying to fill here and I'll bet we could come up with some modeling changes that would make this a whole lot easier without having to get really clever with SPs or triggers.

Related

Interbase SQL trigger

Interbase, Sql, trigger. Can't really understand how to write a trigger of this kind: I have several tables. Each one has a document type, status and an unique number. One table in which i wish to create a trigger is a table that holds a file i post, a status of posting, a doctype corresponding to a table and a unique number linking to a record in the corresponding table. I want to change document status in the corresponding table based on the unique number to a certain status depending on the post result (status) that i change after posting. How can I do it?
Trigger T1 will be executed after update on TABLE1.
Trigger checks if TABLE1.StatusOfPosting changed to some value and depending on result updates TABLE2.DocumentStatus.
Depending on bussienes logic, maybe you will need and BEFORE DELETE trigger.
CREATE TRIGGER T1 FOR TABLE1 AFTER UPDATE POSITION 0
AS
BEGIN
IF (NEW.StatusOfPosting <> OLD.StatusOfPosting and NEW.StatusOfPosting=1) THEN
UPDATE TABLE2
SET TABLE2.DocumentStatus=1
WHERE TABLE2.UniqueNumber = TABLE1.UniqueNumber;
END

multithreading with the trigger

I have written a Trigger which is transferring a record from a table members_new to members_old. The Function of trigger is to insert a record into members_old on after insert in members_new. So suppose a record is getting inserted into a members_new like
nMmbID nMmbName nMmbAdd
1 Abhi Bangalore
This record will get inserted into members_old with the same data structure of the table
My trigger is like :
create trigger add_new_record
after
insert on members_new
for each row
INSERT INTO `test`.`members_old`
(
`nMmbID`,
`nMmbName`,
`nMmbAdd`
)
(
SELECT
`members_new`.`nMmbID`,
`members_new`.`nMmbName`,
`members_new`.`nMmbAdd`
FROM `test`.`members_new`
where nMmbID = (select max(nMmbID) from `test`.`members_new` // written to read the last record from the members_new and stop duplication on the members_old , also this will reduce the chances of any error . )
)
This trigger is working for now , but my confusion is that what will happen if a multiple insertion is happening at one instance of time.
Will it reduce the performance?
Will I face deadlock condition ever in any case as my members_old have FKs?
If any better solution for this situation is there, please give limelight on that
From the manual:
You can refer to columns in the subject table (the table associated with the trigger) by using the aliases OLD and NEW. OLD.col_name refers to a column of an existing row before it is updated or deleted. NEW.col_name refers to the column of a new row to be inserted or an existing row after it is updated.
create trigger add_new_record
after
insert on members_new
for each row
INSERT INTO `test`.`members_old`
SET
`nMmbID` = NEW.nMmbID,
`nMmbName` = NEW.nMmbName,
`nMmbAdd` = NEW.nMmbAdd;
And you will have no problem with deadlocks or whatever. Also it should be much faster, because you don't have to read the max value before (which is also unsecure and might lead to compromised data). Read about isolation levels and transactions if you're interested why...

Update if different/changed

Is it possible to perform an update statement in sql, but only update if the updates are different?
for example
if in the database, col1 = "hello"
update table1 set col1 = 'hello'
should not perform any kind of update
however, if
update table1 set col1 = "bye"
this should perform an update.
During query compilation and execution, SQL Server does not take the time to figure out whether an UPDATE statement will actually change any values or not. It just performs the writes as expected, even if unnecessary.
In the scenario like
update table1 set col1 = 'hello'
you might think SQL won’t do anything, but it will – it will perform all of the writes necessary as if you’d actually changed the value. This occurs for both the physical table (or clustered index) as well as any non-clustered indexes defined on that column. This causes writes to the physical tables/indexes, recalculating of indexes and transaction log writes. When working with large data sets, there is huge performance benefits to only updating rows that will receive a change.
If we want to avoid the overhead of these writes when not necessary we have to devise a way to check for the need to be updated. One way to check for the need to update would be to add something like “where col <> 'hello'.
update table1 set col1 = 'hello' where col1 <> 'hello'
But this would not perform well in some cases, for example if you were updating multiple columns in a table with many rows and only a small subset of those rows would actually have their values changed. This is because of the need to then filter on all of those columns, and non-equality predicates are generally not able to use index seeks, and the overhead of table & index writes and transaction log entries as mentioned above.
But there is a much better alternative using a combination of an EXISTS clause with an EXCEPT clause. The idea is to compare the values in the destination row to the values in the matching source row to determine if an update is actually needed. Look at the modified query below and examine the additional query filter starting with EXISTS. Note how inside the EXISTS clause the SELECT statements have no FROM clause. That part is particularly important because this only adds on an additional constant scan and a filter operation in the query plan (the cost of both is trivial). So what you end up with is a very lightweight method for determining if an UPDATE is even needed in the first place, avoiding unnecessary write overhead.
update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists
(
/* DESTINATION */
select table1.col1
except
/* SOURCE */
select col1 = 'hello'
)
This looks overly complicated vs checking for updates in a simple WHERE clause for the simple scenerio in the original question when you are updating one value for all rows in a table with a literal value. However, this technique works very well if you are updating multiple columns in a table, and the source of your update is another query and you want to minimize writes and transaction logs entries. It also performs better than testing every field with <>.
A more complete example might be
update table1
set col1 = 'hello',
col2 = 'hello',
col3 = 'hello'
/* Only update rows from CustomerId 100, 101, 102 & 103 */
where table1.CustomerId IN (100, 101, 102, 103)
/* AVOID NET ZERO CHANGES */
and exists
(
/* DESTINATION */
select table1.col1
table1.col2
table1.col3
except
/* SOURCE */
select z.col1,
z.col2,
z.col3
from #anytemptableorsubquery z
where z.CustomerId = table1.CustomerId
)
The idea is to not perform any update if a new value is the same as in DB right now
WHERE col1 != #newValue
(obviously there is also should be some Id field to identify a row)
WHERE Id = #Id AND col1 != #newValue
PS: Originally you want to do update only if value is 'bye' so just add AND col1 = 'bye', but I feel that this is redundant, I just suppose
PS 2: (From a comment) Also note, this won't update the value if col1 is NULL, so if NULL is a possibility, make it WHERE Id = #Id AND (col1 != #newValue OR col1 IS NULL).
If you want to change the field to 'hello' only if it is 'bye', use this:
UPDATE table1
SET col1 = 'hello'
WHERE col1 = 'bye'
If you want to update only if it is different that 'hello', use:
UPDATE table1
SET col1 = 'hello'
WHERE col1 <> 'hello'
Is there a reason for this strange approach? As Daniel commented, there is no special gain - except perhaps if you have thousands of rows with col1='hello'. Is that the case?
This is possible with a before-update trigger.
In this trigger you can compare the old with the new values and cancel the update if they don't differ. But this will then lead to an error on the caller's site.
I don't know, why you want to do this, but here are several possibilities:
Performance: There is no performance gain here, because the update would not only need to find the correct row but additionally compare the data.
Trigger: If you want the trigger only to be fired if there was a real change, you need to implement your trigger like so, that it compares all old values to the new values before doing anything.
CREATE OR REPLACE PROCEDURE stackoverflow([your_value] IN TYPE) AS
BEGIN
UPDATE [your_table] t
SET t.[your_collumn] = [your_value]
WHERE t.[your_collumn] != [your_value];
COMMIT;
EXCEPTION
[YOUR_EXCEPTION];
END stackoverflow;
You need an unique key id in your table, (let's suppose it's value is 1) to do something like:
UPDATE table1 SET col1="hello" WHERE id=1 AND col1!="hello"
Old question but none of the answers correctly address null values.
Using <> or != will get you into trouble when comparing values for differences if there are is potential null in the new or old value to safely update only when changed use the is distinct from operator in Postgres. Read more about it here
I think this should do the trick for ya...
create trigger [trigger_name] on [table_name]
for insert
AS declare #new_val datatype,#id int;
select #new_val = i.column_name from inserted i;
select #id = i.Id from inserted i;
update table_name set column_name = #new_val
where table_name.Id = #id and column_name != #new_val;

select the rows affected by an update

If I have a table with this fields:
int:id_account
int:session
string:password
Now for a login statement I run this sql UPDATE command:
UPDATE tbl_name
SET session = session + 1
WHERE id_account = 17 AND password = 'apple'
Then I check if a row was affected, and if one indeed was affected I know that the password was correct.
Next what I want to do is retrieve all the info of this affected row so I'll have the rest of the fields info.
I can use a simple SELECT statement but I'm sure I'm missing something here, there must be a neater way you gurus know, and going to tell me about (:
Besides it bothered me since the first login sql statement I ever written.
Is there any performance-wise way to combine a SELECT into an UPDATE if the UPDATE did update a row?
Or am I better leaving it simple with two statements? Atomicity isn't needed, so I might better stay away from table locks for example, no?
You should use the same WHERE statement for SELECT. It will return the modified rows, because your UPDATE did not change any columns used for lookup:
UPDATE tbl_name
SET session = session + 1
WHERE id_account = 17 AND password = 'apple';
SELECT *
FROM tbl_name
WHERE id_account = 17 AND password = 'apple';
An advice: never store passwords as plain text! Use a hash function, like this:
MD5('apple')
There is ROW_COUNT() (do read about details in the docs).
Following up by SQL is ok and simple (which is always good), but it might unnecessary stress the system.
This won't work for statements such as...
Update Table
Set Value = 'Something Else'
Where Value is Null
Select Value From Table
Where Value is Null
You would have changed the value with the update and would be unable to recover the affected records unless you stored them beforehand.
Select * Into #TempTable
From Table
Where Value is Null
Update Table
Set Value = 'Something Else'
Where Value is Null
Select Value, UniqueValue
From #TempTable TT
Join Table T
TT.UniqueValue = T.UniqueValue
If you're lucky, you may be able to join the temp table's records to a unique field within Table to verify the update. This is just one small example of why it is important to enumerate records.
You can get the effected rows by just using ##RowCount..
select top (Select ##RowCount) * from YourTable order by 1 desc

How to set a default value for one column in SQL based on another column

I'm working with an old SQL 2000 database and I don't have a whole lot of SQL experience under my belt. When a new row is added to one of my tables I need to assign a default time value based off of a column for work category.
For example, work category A would assign a time value of 1 hour, category B would be 2 hours, etc...
It should only set the value if the user does not manually enter the time it took them to do the work. I thought about doing this with a default constraint but I don't think that will work if the default value has a dependency.
What would be the best way to do this?
I would use a trigger on Insert.
Just check to see if a value has been assigned, and if not, go grab the correct one and use it.
Use a trigger as suggested by Stephen Wrighton:
CREATE TRIGGER [myTable_TriggerName] ON dbo.myTable FOR INSERT
AS
SET NOCOUNT ON
UPDATE myTable
SET
timeValue = '2 hours' -- assuming string values
where ID in (
select ID
from INSERTED
where
timeValue = ''
AND workCategory = 'A'
)
Be sure to write the trigger so it will handle multi-row inserts. Do not process one row at a time in a trigger or assume only one row will be in the inserted table.
If what you are looking for is to define a column definition based on another column you can do something like this:
create table testable
(
c1 int,
c2 datetime default getdate(),
c3 as year(c2)
);
insert into testable (c1) select 1
select * from testable;
Your result set should look like this :
c1 | c2 | c3
1 | 2013-04-03 17:18:43.897 | 2013
As you can see AS (in the column definition) does the trick ;) Hope it helped.
Yeah, trigger.
Naturally, instead of hard-coding the defaults, you'll look them up from a table.
Expanding on this, your new table then becomes the work_category table (id, name, default_hours), and you original table maintains a foreign key to it, transforming fom
(id, work_category, hours) to (id, work_category_id, hours).
So, for example, in a TAG table (where tags are applied to posts) if you want to count one tag as another...but default to counting new tags as themselves, you would have a trigger like this:
CREATE TRIGGER [dbo].[TR_Tag_Insert]
ON [dbo].[Tag]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Tag
SET [CountAs] = I.[ID]
FROM INSERTED AS I
WHERE I.[CountAs] IS NULL
AND dbo.Tag.ID = I.ID
END
I can think of two ways:
triggers
default value or binding (this should work with a dependency)
Triggers seem well explained here, so I won't elaborate. But generally I try and stay away from triggers for this sort of stuff, as they are more appropriate for other tasks
"default value or binding" can be achieved by creating a function e.g.
CREATE FUNCTION [dbo].[ComponentContractor_SortOrder] ()
RETURNS float
AS
BEGIN
RETURN (SELECT MAX(SortOrder) + 5 FROM [dbo].[tblTender_ComponentContractor])
END
And then setting the "default value or binding" for that column to ([dbo].ComponentContractor_SortOrder)
Generally I steer away from triggers. Almost all dbms have some sort of support for constraints.
I find them easier to understand , debug and maintain.