SQL Server INSERT trigger does not work - sql

I have a trigger to copy over the data from Table A to table B when table A is changed
The trigger is like this :
ALTER TRIGGER ATrigger
ON A AFTER INSERT, DELETE, UPDATE
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM B WHERE id IN (SELECT id FROM deleted)
INSERT INTO B(Id, col1,col2) (SELECT i.Id, i.col1, i.col2 FROM inserted i)
END
But i see not all the data inserted in A are copied to B, the data copied seems very random
I was searching around, found it might caused by multi-insert, someone is suggesting using cusor, but i think for mine, it should be ok to insert or delete from the inserted, deleted table using this two sql.
Please advise, thanks!

I'm not certain this is your problem but your trigger has 2 "gotchas". First on an insert the deleted table will have no rows in it so no deletes will be done. Second is the reverse and potentially your problem. On a delete the inserted table has no rows. So all of the IDs are going to be deleted from table B but not re-inserted. On top of this if ID is not a unique key for table A then when you insert a second copy of it you will be deleting all of your history in table B and only adding the "new" history.
If you can provide more information on the structure of the 2 tables and the purpose of the trigger, not to mention any patterns on the rows being inserted or not being inserted as the case may be we can be of more help.

Related

SQL Server triggers deleted vs inserted

In a SQL Server after update trigger, how would this work if the DELETED is already in the INSERTED ? Or what exactly does the deleted pseudo table contain?
SELECT *
INTO #ModifiedData
FROM
(SELECT * FROM DELETED
EXCEPT
SELECT * FROM INSERTED) ModifiedData;
So the way that SQL works is that it creates two tables. The first table, the deleted table. The second table is the inserted table. You can create your temp table, but it will be drop right after the trigger is out of memory. So it would help if you moved it to a permanent table. As a dba, I would highly advise against this method as it can cause a lot of data on high traffic tables. And you may have an update that does not change any data.
the INSERTED table has the new updated values and the DELETED table has the old value (before update)
so this
SELECT * FROM DELETED EXCEPT SELECT * FROM INSERTED
will get only the actual updated rows.
example :
updating column with the same value
this row will be only in the INSERTED table.

How to run a SQL statement every time a row is created?

We are using an ERP system which uses SQL Server. There is a function which creates a row 'A' in a specific table and populates it with data from another row 'B' of another table. For some reason the programmer thought, one would need only certain values of 'B' in 'A'. So only the values of some columns in 'B' are copied.
Now I want more columns to be copied than the program copies. The columns are there but they don't get copied.
The program offers a way to run a script before the SQL statement, which creates the row, is executed. So the problem here is, I don't know the id of the row which will be created. And even if I would, the row isn't created yet to alter it.
Is there a way in SQL Server to run a SQL script every time after a row is created in a specific table?
Thanks for the help.
Yes - those are called triggers.
You can write triggers that get fired after INSERT, UPDATE or DELETE - or they can be INSTEAD OF triggers, too - if you need to completely take control of an operation.
In your case, I believe an AFTER INSERT trigger should be just fine:
CREATE TRIGGER TrgCopyAdditionalColumns
ON dbo.TableA
AFTER INSERT
AS
-- the newly inserted row (there could be **multiple!**)
-- will be stored in the `Inserted` pseudo table, which has the
-- exact same structure as your "TableB" table - just pick out
-- the columns you need to insert into "TableA" from here
INSERT INTO dbo.TableA (Col1, Col2, ..., ColN)
SELECT
b.Col1, b.Col2, ..., b.ColN
FROM
dbo.TableB AS b
INNER JOIN
-- somehow, you need to connect your Table B's rows to the
-- newly inserted rows for Table A that are present in the
-- "Inserted" pseudo table, to get only those rows of data from
-- Table B that are relevant to the newly inserted Table A rows
Inserted i ON b.A_ID = i.ID

Delete trigger not inserting record

Need a trigger which runs on deletion.
The code I wrote is not working: It is firing the trigger but is not inserting any records.
I want it to insert the to be deleted row in a audit table.
My code:
CREATE TRIGGER [EXERCISE].[delete_trigger] ON [EXERCISE].[Fact_Sales]
AFTER DELETE
AS
INSERT INTO [EXERCISE].[Fact_Sales_Audit](City_ID,Product_ID,Time_ID,Created_Date,Updated_Date)
SELECT F.City_ID,F.Product_ID,F.Time_ID,F.Created_Date,F.Updated_Date
FROM [EXERCISE].[Fact_Sales] F
JOIN deleted D
ON F.Fact_Sales_ID = D.Fact_Sales_ID
PRINT 'Deleted row entered.'
It is not working. Reason I suspect is- the row is being deleted from the table before the trigger is fired and thats why it is not able to join and hence not inserting any records. But when I debug and print messages I can see that trigger happens before deletion.
Can someone please explain me how exactly does this trigger work?
You are deleting rows from the [EXERCISE].[Fact_Sales] table, yet in your trigger you are joining to this table, expecting the records to still be there - they are not.
At that point, the Deleted pseudo-table has the same schema as the [EXERCISE].[Fact_Sales] table, but with the rows you have just deleted in it. Instead of trying to join back to a table for rows you know aren't there any more, just use the Deleted table directly for your insert:
CREATE TRIGGER [EXERCISE].[delete_trigger]
ON [EXERCISE].[Fact_Sales]
AFTER DELETE
AS
INSERT INTO [EXERCISE].[Fact_Sales_Audit](City_ID,Product_ID,Time_ID,Created_Date,Updated_Date)
SELECT D.City_ID,
D.Product_ID,
D.Time_ID,
D.Created_Date,
D.Updated_Date
FROM deleted D
The trigger does fire after the delete on the main table, as the AFTER DELETE syntax implies. It fires somewhere between when the delete statement is executed, and before the control is returned to the user. It takes place in the same transaction - e.g. if the delete was rolled back for whatever reason, the inserts performed by the trigger would also be rolled back.
You should simplify your trigger definition to fetch just deleted row(s) from virtual table DELETED. What will happen is your deleted rows will be temporarily stored in that table and then will be inserted into audit table.
NB! Be sure you have set primary key on your Fact_Sales table.
CREATE TRIGGER [delete_trigger] ON [Fact_Sales]
FOR DELETE
AS
INSERT INTO [Fact_Sales_Audit](Fact_Sales_Id,City_ID,Product_ID,Time_ID,Created_Date,Updated_Date)
SELECT Fact_Sales_Id,City_ID,Product_ID,Time_ID,Created_Date,Updated_Date
FROM DELETED

Row is not being deleted because of cascade trigger updates this row

I have problem with deleteing rows in table which has trigger which invokes trigger of second table, which updates row in first table. Here is the description:
Table A (id,b_table_count)
Table B (id,a_table_id_fk)
Table A has trigger BEFORE DELETE which has instructions:
BEGIN
DELETE FROM b where a_table_fk = OLD.id;
RETURN OLD;
END;
Table B has trigger AFTER DELETE with instruction:
UPDATE a SET b_table_count = b_table_count-1 WHERE OLD.a_table_id_fk = a.id;
When I delete row from table A, which has no connected rows in B, everything is correct.
But, when I delete row from table A, which has connected row(s) in table B, then DELETE statement returns "Query returned successfully: 0 rows affected" . I must execute DELETE statment second time, then row is finnaly deleted. After first DELTE only connected ROWS are deleted in TABLE B, but row being deleted in table A remains.
Do you have answer for that? I suspect that pgsql doesn't allow to update row being deleted in trigger, but I haven't found anything about it in pgsql documentation.
What is the solution?
I'm having a similar problem, but with one table only. Deleting rows from the given table fires a trigger (before delete), that searches for related rows int the same table (by a given condition), and if finds them, these rows get updated. Now, if an updated row is to be deleted by the same delete command, then it doesn't gets deleted.
So basically, if you create a trigger, that always updates the same row that is being deleted, you can't delete anything from now on.
I don't know if this is on purpose, or not. From one way, it seems logical, that's for sure. If you update a record, it's not the same record that was intended for deletion.
(sorry for bad english)
So basicly you have a A -> trigger -> B -> trigger -> A situation, which is actually incorrect thing to do by design and I think postgres locks the row that is executing trigger or A row is locked but i'm not sure about all inner behavior of postgres.
so you can try DELETE FROM B WHERE a_id = %Row_to_delete_id% first and then
DELETE FROM A WHERE a_id = %Row_to_delete_id%, in a transaction,
but i highly recommend you revise your trigger dependencies

Help Understanding the inserted and deleted Table SQL Server

If a run an INSERT INTO statement that triggers an INSERT AFTER trigger. What table/row(s) does the inserted and deleted objects represent in the trigger? For example,
INSERT INTO Person.person (personID, name) VALUES (1, 'robert')
CREATE TRIGGER myTrigger
ON Person.person
AFTER INSERT AS
BEGIN
insert into Person.PersonMovies (personID, movieID)
select inserted.personID from inserted _--i know the code is incomplete, but i'm curious only about the inserted part._
END
I'm confused as to what records are held by the inserted table. Would it be the records in the insert statement that triggered the trigger or the Person.PersonMovies table?
Yes, the inserted/deleted tables gives your trigger code access to the records changed by the statement that caused (triggered) the trigger. Also remember that your statements could have effected multiple rows, so your deleted or inserted tables could have multiple records.
The rows in the pseudotables inserted and deleted are based on the table defined in you ON clause.
The Inserted table holds the rows from the original Insert statement that caused the trigger to fire.
See also this MSDN article.
The inserted and deleted pseudotables hold one record for each row in the table that was created or deleted by the statement that fired the trigger. For an update, the deleted table holds the old version of the records, and the inserted table holds the new. The schema of the pseudotables matches the actual table.