How to trace SQL statements for a specific table? - sql

I'm currently working with DB2 v10.5 and I need to log all SQL Statements that occurr on a specific table (A). For instance, if an INSERT occur on table A, I need to "grab" that SQL Statement and log it into another table (A_LOGGER).
The solution I've reached was to create a TRIGGER (for each CRUD operation) over table A that looks into the table SYSIBMADM.SNAPDYN_SQL and try to save the last executed statement on table A.
Example for the INSERT Statement:
CREATE OR REPLACE TRIGGER OPERATIONS_INSERT_TRIGGER
AFTER INSERT ON REPLDEMO.OPERATIONS
REFERENCING NEW AS OBJ
FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
INSERT INTO REPLDEMO.OPERATIONS_LOGGER (LAST_SQL_STATEMENT)
SELECT STMT_TEXT FROM SYSIBMADM.SNAPDYN_SQL
WHERE 1=1
AND STMT_TEXT LIKE 'INSERT INTO REPLDEMO.OPERATIONS (%'
AND STMT_TEXT NOT LIKE '%?%';
END%
But looking at table SYSIBMADM.SNAPDYN_SQL is not the best solution because you cannot guarantee that you'll get the truly last SQL Statement executed on table A. Moreover, if there's a massive number of sql statements executed over table A in a very short period the TRIGGER will replicate many of the statements already saved on A_LOGGER.
So, my question is: Is there an effective and secure way to get the last SQL Statement executed over a table?
Thanks.

Related

SQL trigger to insert data from one table to another if a condition is met

I am miserably failing to build a sql trigger (in background) where I want to insert data from one table to another if a certain condition is met, something like this:
Create trigger on table Invoice
If inv_number starts with inv
Then Insert into Document (var1,var2,var3) values (inv_number, inv_date, inv_amount)
Thanks
If you're using SQL Server (as I said in comments - triggers are highly vendor-specific, so if you're using something else, you'll have to adapt as needed), you can use something like this:
CREATE TRIGGER trgInvoiceInsert
ON dbo.Invoice
AFTER INSERT -- adapt if you need to run this after UPDATE or DELETE, too
AS
BEGIN
/* In SQL Server, if you inserting a bunch of rows
at once using an `INSERT INTO .... SELECT ....`
approach, then this trigger will be called only *ONCE*,
with all the inserted rows in the "Inserted" pseudo table.
Handle it accordingly - in a set-based manner
*/
INSERT INTO dbo.Document (col1, col2, col3)
SELECT i.inv_number, i.inv_date, i.inv_amount)
FROM Inserted i
WHERE i.inv_number LIKE 'inv%'
END
For further details, check out the official Microsoft documentation on SQL Server triggers

DB2 SQL statement - is it possible to A) declare a temporary table B) populate it with data then C) run a select statement against it?

I have read only access to a DB2 database and i want to create an "in flight/on the fly" or temporary table which only exists within the SQL, then populate it with values, then compare the results against an existing table.
So far I am trying to validate the premise and have the following query compiling but failing to pick anything up with the select statement.
Can anyone assist me with what I am doing wrong or advise on what I am attempting to do is possible? (Or perhaps a better way of doing things)
Thanks
Justin
--Create a table that only exists within the query
DECLARE GLOBAL TEMPORARY TABLE SESSION.TEMPEVENT (EVENT_TYPE INTEGER);
--Insert a value into the temporary table
INSERT INTO SESSION.TEMPEVENT (EVENT_TYPE) VALUES ('1');
--Select all values from the temporary table
SELECT * FROM SESSION.TEMPEVENT;
--Drop the table so the query can be run again
DROP TABLE SESSION.TEMPEVENT;
If you look at the syntax diagram of the DECLARE GLOBAL TEMPORARY TABLE statement, you may note the following block:
.-ON COMMIT DELETE ROWS---.
--●--+-------------------------+--●----------------------------
'-ON COMMIT PRESERVE ROWS-'
This means that ON COMMIT DELETE ROWS is default behavior. If you issue your statements with the autocommit mode turned on, the commit statement issued automatically after each statement implicitly, which deletes all the rows in your DGTT.
If you want DB2 not to delete rows in DGTT upon commit, you have to explicitly specify the ON COMMIT PRESERVE ROWS clause in the DGTT declaration.

SQL insert into 2 tables in one query

I have the following query in SQLRPGLE for DB2:
INSERT INTO ITEMS2 (PROGRAM, VLDFILE, VLDFLD,
SELFILE, SELFLD) VALUES(:SCAPP , 'CSTMR', 'CYC',
'BYC', 'BYCC');
I would like this query to be run in 2 libraries as in FIRST/ITEMS2 and SECOND/ITEMS2
where FIRST and SECOND are the library names. Can this be achieved in one query?
For those who have no understanding of iSeries: The above insert statement would be similar to having a insert query for 2 tables.
The INSERT statement does not support inserting into multiple tables.
However you could create a trigger on FIRST/ITEMS2 to automatically insert/update/delete the record into SECOND/ITEMS2.
See the CREATE TRIGGER statement for more information.
If this will be run often, consider making the INSERT into a stored procedure, and then setting the target schema via SET SCHEMA:
set schema=first;
call my_insert_proc(:scapp);
set schema=second;
call my_insert_proc(:scapp);
You could create a QMQuery like this
INSERT INTO &LIB/ITEMS2
(PROGRAM, VLDFILE, VLDFLD, SELFILE, SELFLD)
VALUES (&SCAPP, 'CSTMR', 'CYC', 'BYC', 'BYCC');
Then
STRQMQRY myQmQry SETVAR(('LIB' 'FIRSTLIB')('SCAPP' &VAR))
STRQMQRY myQmQry SETVAR(('LIB' 'SECONDLIB')('SCAPP' &VAR))
From IBM's Syntax diagram of INSERT ( http://pic.dhe.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Fdb2%2Frbafzbackup.htm ), I'd say you have to go with two queries.
But after the first time of executing this query, you can try changing the current library ( http://publib.boulder.ibm.com/infocenter/iadthelp/v7r1/topic/com.ibm.etools.iseries.langref2.doc/chglibl.html ).

Trigger event is fired only once for Microsoft SQL Server 2005 DB if more than one rows updated?

I have a table MyTable with a trigger defined like this:
ALTER TRIGGER [MyTableInsertDeleteUpdate]
ON [dbo].[MyTable]
AFTER INSERT,DELETE,UPDATE
AS
DECLARE #id int;
BEGIN
SELECT #id = ins.id FROM inserted ins;
IF (#id IS NOT NULL)
BEGIN
-- insert a new record to audit table
PRINT 'inserted/updated id: ' + CAST(#id AS VARCHAR);
END
END
I realize that if more than one rows are updated like this,
UPDATE MyTable SET name = 'test rows' WHERE id in (1, 2, 3);
the tigger is called only once and only the firstone in [inserted] is updated. Actually, [inserted] may have more than one rows (3 in this case if id 1, 2,3 exist). In order words, the trigger is not fired on each row. Is that right?
I am using Microsoft SQL Server 2005.
Yeah the trigger is fired once per statement (not once per row) that makes the changes you are subscripting to. It will even fire if no rows where affected.
http://msdn.microsoft.com/en-us/library/ms189799(SQL.90).aspx
As Hojou said, your trigger will fire once per statement rather than once per affected row. This is different to databases like Interbase and Firebird, and threw me when I first started using SQL Server.
The whole point of the inserted and deleted 'virtual' tables is because the events are record-SET based, not row-based.
There are any number of tutorials out there that cover writing sql to process the inserted/deleted tables, but watch out for the shovelware ones. I've seen more than a couple of so-called tutorials that have just been copy/pasted from another database platform and won't actually work in SQL Server as they claim to (one of the top hits for 'SQL Server trigger example' in Google gets it completely wrong for UPDATE statements).
This is a reasonable introduction to Triggers and the concepts required to make sense of the inserted and deleted tables, with an explanation of why you will be missing events in your own example. The Microsoft docs themselves are reasonably useful once you get past their dull, lifeless structure and writing-style.
To insert records to an audit table from an insert you would do something like this in the trigger:
insert auditable (field1, field2, insert_date, insertedBy)
select field1, field2, getdate(), user_Name() from inserted
No fooling around with setting variables just a a plain insert based on a select statment.
Personally I would have a separate trigger for inserts, updates and deletes as you want differnt code for each.

Using SQL Server DTS Package to Conditionally Insert / Update Rows in Destination Table

I want to create a DTS Package to pull data from an Oracle table into a SQL2K
table. How can I insert rows that are not already in the SQL2K table and
update rows that already exist in the SQL2K table?
I guess I could truncate and repopulate the entire table or create a
temporary table and then do updates/inserts from the temp table into the
destination table.
Is there any easier way using DTS?
Thanks,
Rokal
You can do that in a DTS package using two data driven query tasks: one for the inserts and one for the updates. The data driven query tasks are a bit of a pain to use, but they work. I've also done this (a "merge") in sql server 2000 with an AS/400 database using a dynamic t-sql. You'd write a t-sql script that outputs psql and runs it againt a linked server to the Oracle database.
UPDATE:
A DTS "data driven query task" will let you insert|update data from the sql server connection in DTS to an oracle server connection in DTS w/o a temp table or a linked server.
Update2; here's some more info on what I mean:
http://www.databasejournal.com/features/mssql/article.php/3315951
http://msdn.microsoft.com/en-us/library/aa933507(SQL.80).aspx
Are you keeping the same primary key values?
If you are you have a number of options, some versions of SQL support the MERGE statement which will update or insert just like you require.
Or you can write your own.
Something along the lines of loading all the rows into a staging table in your SQL database and row by row checking for the existence of your primary key in your main SQL table. If the key exists update the row and if not insert it.
Yes, the primary key values in the source and destination will match.
I was hoping to accomplish this task without the use of a temporary (staging) table.
Also, I am using sql server 2000 so the MERGE statement is not available.
Try:
DELETE FROM dbo.WhateverTable WHERE WhateverTableID IN (SELECT WhateverTableID FROM MySource)
It might be pretty slow, use join instead:
Delete a
from firstTable a join secondTable b on a.id = b.id
There's no way with TSQL to do a INSERT or UPDATE in the same statement, but you could very easily do it in two statements (as you mentioned above).
Statement 1:
DELETE FROM dbo.WhateverTable
WHERE WhateverTableID IN (SELECT WhateverTableID FROM MySource)
Statement 2:
INSERT INTO dbo.WhateverTable
SELECT * FROM MySource
Also, is there any reason why you don't want to use a temp table?