In the following statement, will f1 always get the value that f2 used to have? Or will f2 sometimes get updated first and f1 winds up with NULL? I am under the impression that the new values are not available within the statement, that f2 has the old value while processing the record, but I can't find an authoritative place that says this.
UPDATE x
SET
x.f1 = x.f2,
x.f2 = NULL
Conceptually the operation happens "all at once" so it will use the "before" values
Indeed
UPDATE x
SET
x.f1 = x.f2,
x.f2 = x.f1
would also work fine to swap the two column values.
f1 will always get f2's previous value before the UPDATE.
Technically speaking the record is deleted, and reinserted. So SQL will work out what the new record should be, then delete the current record, and insert the new record afterwards.
This article regarding SQL Triggers may help explain:
The deleted table stores copies of the affected rows during DELETE and UPDATE statements. During the execution of a DELETE or UPDATE statement, rows are deleted from the trigger table and transferred to the deleted table. The deleted table and the trigger table ordinarily have no rows in common.
The inserted table stores copies of the affected rows during INSERT
and UPDATE statements. During an insert or update transaction, new
rows are added to both the inserted table and the trigger table. The
rows in the inserted table are copies of the new rows in the trigger
table.
http://msdn.microsoft.com/en-us/library/ms191300.aspx
Related
Please explain what the following statements mean. It's an assignment of local variables but I do not understand what inserted or deleted means?
select #ID = ID from inserted
select #co_ID = co_ID from deleted
Thank you
INSERTED and DELETED are temporary, memory resident tables created by SQL Server for use (or misuse) within a DML trigger.
Inserts and updates copy new rows into INSERTED,
Deletes and updates copy old rows into DELETED.
It looks like this code is attempting to audit a change to a row of data - but will fail unless there is something else in the code path guaranteeing that only a single row will be updated.
These statements mean that you have written a trigger in SQL Server that is not safe. The trigger assumes that only one row has been updated. This is not safe because SQL Server calls triggers based on groups of rows.
If there is one row, then the parameters #ID and #co_ID are assigned values from that row. If there are multiple rows being updated, then values from arbitrary -- and perhaps different -- rows are assigned to the parameters.
In SQL server, when does a trigger get fired?
The problem is, I have a table where 45,000 records are going to be inserted.
And I want to copy all 45k records to other tables.
But I don't want the trigger to run on every insert, i.e 45000 times trigger.
My trigger is basically copying record from TableA to TableB.
Trigger:
Create trigger tri_1
on TableA
after insert
as
Begin
Insert into TableB (ID,Name,Others)
select ID,Name,Others from TableA
inner join inserted
on inserted.ID = TableA.ID
End
The above is just the template of my trigger.
Also, I have a question, the trigger mentioned above, how is it working? like firing for each row or after all insert is done?
In SQL Server, the trigger is fired when the insert statement is completed.
In some databases, the trigger is executed for each row inserted (in those databases for each row is often part of the syntax). By contrast, SQL Server keeps track of the changed rows, which is why they are stored in table-like structures, inserted and deleted -- and it is a mistake to assume that these contain only one row.
I'm looking to implement a sort of Type 2 Slow Changing Dimension like behavior into my dimension table using PL/SQL's MERGE statement. It workd just fine for updating existing values and inserting new ones. I'm looking to extend this functionality by not only updating existing values but creating a different row with the updated values while preserving the row with the "outdated" values.
In short, is it possible to do this?
MERGE INTO A
USING B
ON (A.ID = B.ID)
WHEN MATCHED THEN
UPDATE END_DATE ON THE EXISTING ROW;
INSERT UPDATED VALUES IN A NEW ROW;
WHEN NOT MATCHED THEN
INSERT A NEW ROW WITH NEW VALUES;
Thanks you guys in advance.
If you are truly doing a Type 2 dimension and want to use a merge, yes it can be done but it's not terribly straightforward. Effectively, if you will need to compare your data using an inline view in the USING clause, that features a column that indicates whether its an insert or an update. This field gets joined to an slowly changing dimension table that drives whether an insert or an update occurs.
This blog post describes the technique in great detail and has worked for us, albeit we used a hash to determine unqiueness as opposed to a column by column compare.
Load Slowly Changing Dimension Type 2 using Oracle Merge Statement
You need to run two statements instead of 1 here.
(in your pseudo-code)
UPDATE END_DATE IN A WHERE A.ID = something_from_B
INSERT new VALUES IN A
The insert needs to happen no matter what. So no need of checking if the row exists or not (and only updating if the row exists). Just end_date all records that match B. This would end_date 0 to n number of rows. And then Insert everything together without worrying if that record has been end-dated already or not.
Put this inside a BEGIN...END; if you want transactional atomicity.
In short - no. A MERGE statement provides the option to INSERT if matching data is not found, and to UPDATE if matching data is found. It does NOT give the option to "update-and-insert" if matching data is found.
Best of luck.
As I understand, you wrote a pseudocode. So I can suggest just an idea also in pseudocode:
MERGE INTO A
USING (select * from B1 union all
select * from B2) B
ON (A.ID = B.ID)
WHEN MATCHED THEN
UPDATE END_DATE ON THE EXISTING ROW FROM B1;
WHEN NOT MATCHED THEN
INSERT A NEW ROW WITH NEW VALUES FROM B2;
If you want to insert updated rows as new, you can generate subquery based on B, which will contain "updated rows", but these rows have to be considered as new by SQL engine (it is impossible to say how to do it without details about your tables). In my query B1 is "rows to update", and B2 is "updated rows which have to look as new". If you do this, updated rows will be inserted.
Also, no guarantee that in your case it could be implemented.
I have a trigger that inserts a record into a diff table but I need to get that record that was inserted inside the trigger, how do I do it? There is no identity field, only account_nbr that is generated by a separate trigger on the insert table.
I don't know if there is sql statements to retrieve a row that was just inserted.
DB is Sql Server 2008.
The OUTPUT clause will give you back the records you have just inserted: http://msdn.microsoft.com/en-us/library/ms177564.aspx
If you mean the rows inserted before the trigger invoked, they are in the inserted pseudo-table.
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