How to insert updated rows in a new table on DB2 - IBM - sql

I need to create an update using sql dynamic and all the updated rows have to be sent in a log table.
In microsoft, i can use OUTPUT clause and it inserts the updated rows in a table, but how can i do this in db2, using sql dynamic?
I have the following tables:
AllCustomers - contains all customers from a db
Id
Name
1
John
2
Test
gdpr_id. - contains all customers which should be updated
Id
Name
1
John
gdpr_log - should contain the output of the update stmt
Id
Name
1
John
I found the below syntax , but it just displays the results.
SELECT fields FROM FINAL TABLE
(update table set field = 'value' where id ='xyz')
I tried to create another dynamic stmt as
INSERT INTO
SELECT fields FROM FINAL TABLE
(update table set field = 'value' where id ='xyz')
and the syntax is not recognized.
How can i replace it to insert all the updated values in a log table?
I have to use sql dynamic because the tables which need to be updated are stored in a metadata table and with a cursor, i create the update script for each line from the metadata table.
UPDATE:
Metadata table looks like this:
table
column
AllCustom
Name
AllCustom
Lastname
CREATE OR REPLACE PROCEDURE sp_test ()
DYNAMIC RESULT SETS 1
P1: BEGIN
--*****************VARIABLES *****************
DECLARE EOF INT DEFAULT 0;
declare v_table nvarchar(50);
declare v_column nvarchar(50);
declare v_rowid nvarchar(50);
declare v_stmt nvarchar(8000);
declare s1 statement;
--*****************UPDATE STEP *****************
-- Declare cursor
DECLARE cursor1 CURSOR WITH HOLD WITH RETURN FOR
SELECT table,column FROM metadata_tbl;
declare c1 cursor for s1;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET EOF = 1;
OPEN cursor1;
WHILE EOF = 0 DO
FETCH FROM cursor1 INTO v_table,v_column;
SET v_stmt = 'WITH A AS
(
SELECT name
FROM FINAL TABLE
(
UPDATE ' || v_table || ' set ' || v_column || ' = ''some name'' where id in (select ID from gdpr_id )
)
)
SELECT COUNT (1) as tst
FROM FINAL TABLE
(
INSERT INTO GDPR_LOG (table,name, LOGDATE)
SELECT ''' || v_table || ''', name, current_timestamp from A
) B';
PREPARE s1 FROM v_stmt ;
open c1 using v_table,v_column;
close c1;
END WHILE;
CLOSE cursor1;
END P1
Update step works fine, insert step duplicates the rows inserted.
What should I do to have the insert step ok?

You have to use SELECT as an outermost statement and keep inner SELECTs in distinct CTEs, if you have a number of them.
Try this:
WITH A AS
(
SELECT ID, NAME
FROM FINAL TABLE
(
UPDATE GDPR
SET NAME = 'Some name'
WHERE ID = 1
)
)
SELECT COUNT (1)
FROM FINAL TABLE
(
INSERT INTO GDPR_LOG (ID, NAME)
SELECT * FROM A
) B
Update:
Using dynamic SQL.
You must enclose the whole statement with some statement termination character (say, #) different from the default one (;) if you use some tool to run this compound statement and specify this statement terminator correctly there.
BEGIN
DECLARE C1 CURSOR FOR S1;
PREPARE S1 FROM
'
WITH A AS
(
SELECT ID, NAME
FROM FINAL TABLE
(
UPDATE GDPR
SET NAME = ?
WHERE ID = ?
)
)
SELECT COUNT (1)
FROM FINAL TABLE
(
INSERT INTO GDPR_LOG (ID, NAME)
SELECT * FROM A
) B
';
OPEN C1 USING 'Name', 1;
CLOSE C1;
END

Related

Trying to query a redshift within SELECT statement

Current table1:
col1
-------------
schema.table1
schema.table2
schema.table3
Desired table1:
col1 col2
------------------------------------------------------------
schema.table1 value of (select count(*) from schema.table1)
schema.table2 value of (select count(*) from schema.table1)
schema.table3 value of (select count(*) from schema.table1)
It is not working, I tried using function too, but function doesn't allow to use 'FROM'
select col1, (select count(*) from col1)
from table1
I am trying to create this query in redshift. Can anyone please help me out?
To perform this task you will need a stored procedure AND a defined cursor. The stored procedure allows for looping and the cursor provides the ability to execute a newly created statement (dynamic querying).
For example:
Create the starting materials, 3 tables and a table that references these tables.
create table foo as (select 1 as A);
create table goo as (select 2 as A);
create table hoo as (select 3 as A);
create table tabs as (select 'foo' as tab union all select 'goo' union all select 'hoo');
Next define the stored procedure the will create the dynamic SQL
CREATE OR REPLACE procedure count_tabs(curs1 INOUT refcursor)
AS
$$
DECLARE
row record;
statement varchar := '';
union_needed BOOL := false;
BEGIN
for row in select tab from tabs LOOP
IF union_needed THEN
statement := statement || ' UNION ALL ';
END IF;
statement := statement || 'select \'' || row.tab || '\' as table_name, count(*) as table_count from ' || row.tab ;
union_needed := true;
END LOOP;
RAISE NOTICE 'sql to execute: %',statement;
open curs1 for execute statement;
END;
$$ LANGUAGE plpgsql;
Lastly we need to call the procedure and execute the cursor
call count_tabs('mycursor');
fetch 1000 from mycursor;
A few notes on this:
This assumes you want the results as output on your bench. If you want to create a table with the results this is doable in the same structure
Since the FROM clause value(s) is unknown at compile time this needs to be done in 2 steps - create the query and then execute the query.
I believe you can have the procedure walk this same cursor itself but doing this is exceptionally slow

Same value being inserted into table with cursor

I'm trying to dynamically count the number of null values held in a table. So far I have this:
BEGIN
DECLARE STMT VARCHAR(2000);
FOR v AS CRS CURSOR FOR
SELECT NAME
FROM SESSION.TT1
DO
SET STMT = 'UPDATE SESSION.TT1 TT1
SET NULL_COUNT = (
SELECT COUNT(*) - COUNT('''||v.NAME||''') NULL_COUNT
FROM Table1
)
WHERE TT1.COLUMN_NAME = '''||v.NAME||'''';
EXECUTE IMMEDIATE STMT;
END FOR;
END
This runs fine, and does populate the temp table with data, but it updates every row with the same value (which is obviously incorrect).
Where have I gone wrong?
Note:
The temporary table, TT1, was generated by another procedure, it contains two columns; NAME, and NULL_COUNT. All values in the NULL_COUNT column are undefined at this point, and the NAME column contains column names retrieved from syscolumns.
I also tried removing the row and then inserting a new row rather than updating on the matched column name but this provides the same results.
You need double quotes (or no quotes) around the column name, otherwise you are are simply counting a literal value, rather than the column...
DROP TABLE TABLE1#
CREATE TABLE TABLE1(C INT)#
INSERT INTO TABLE1 VALUES(NULL),( NULL)#
DECLARE GLOBAL TEMPORARY TABLE TT1(NAME VARCHAR(128), NULL_COUNT BIGINT)#
INSERT INTO SESSION.TT1 VALUES ('C',null)#
BEGIN
DECLARE STMT VARCHAR(2000);
FOR v AS CRS CURSOR FOR
SELECT NAME
FROM SESSION.TT1
DO
SET STMT = 'UPDATE SESSION.TT1 TT1
SET NULL_COUNT = (
SELECT COUNT(*) - COUNT("'||v.NAME||'") NULL_COUNT
FROM TABLE1
)
WHERE TT1.NAME = '''||v.NAME||'''';
EXECUTE IMMEDIATE STMT;
END FOR;
END
#
SELECT * FROM SESSION.TT1
#
NAME NULL_COUNT
---- ----------
C 2

Using placeholders in Informix SQL queries

How I can use the placeholders within dynamic SQL string in case when I need to assign one of input parameters twice? Following code doesn't work.
CREATE FUNCTION somefunc( p_name VARCHAR( 32 ), p_id INT, p_weight INT ) RETURNING INT;
LET sp_id = 0;
LET somearea=12;
LET c_query ='SELECT FIRST 1 pr_id FROM sometable n WHERE n.pr_id> ? and n.pr_weight = ? ( UPPER( n.sname ) LIKE %UPPER(?))'||
' OR( UPPER(?) LIKE %UPPER( n.name)) ORDER BY 1, 2;';
PREPARE c_stmt
FROM c_query;
DECLARE c_cur CURSOR FOR c_stmt;
OPEN c_cur USING p_id, p_weight, p_name, p_name ;
FETCH c_cur INTO sp_id;
CLOSE c_cur;
FREE c_cur;
FREE c_stmt;
RETURN sp_id;
END FUNCTION;
Umm... not quite sure to fully understand the question (or the purpose of that SQL, hope it's us a test as it does in deed look weird)
Anyway, this is your SPL with the correct syntax:
D:\infx\ids12>cat 1.sql
DROP TABLE sometable;
CREATE TABLE sometable (name varchar(32), sname varchar(32), pr_id int, pr_weight int);
INSERT INTO sometable VALUES ('test','test',1,100);
INSERT INTO sometable VALUES ('tESt','tESt',2,200);
INSERT INTO sometable VALUES ('another','another',3,300);
DROP FUNCTION somefunc;
CREATE FUNCTION somefunc( p_name VARCHAR( 32 ), p_id INT, p_weight INT ) RETURNING INT;
DEFINE sp_id int;
DEFINE c_query varchar(200);
LET sp_id = 0;
LET c_query ="SELECT FIRST 1 pr_id FROM sometable n WHERE n.pr_id> ? and n.pr_weight = ? and ( UPPER( n.sname ) LIKE UPPER('%'||?||'%')) OR( UPPER('%'||?||'%') LIKE UPPER( n.name)) ORDER BY 1";
PREPARE c_stmt
FROM c_query;
DECLARE c_cur CURSOR FOR c_stmt;
OPEN c_cur USING p_id, p_weight, p_name, p_name ;
FETCH c_cur INTO sp_id;
CLOSE c_cur;
FREE c_cur;
FREE c_stmt;
RETURN sp_id;
END FUNCTION;
--SELECT FIRST 1 * FROM sometable n WHERE n.pr_id> 1 and n.pr_weight = 200 and ( UPPER( n.sname ) LIKE UPPER('%'||'TEST'||'%')) OR ( UPPER('%'||'TEST'||'%') LIKE UPPER( n.name)) ORDER BY 1;
EXECUTE FUNCTION somefunc('TEST',1,200);
D:\infx\ids12>dbaccess stores7 1.sql
Database selected.
Table dropped.
Table created.
1 row(s) inserted.
1 row(s) inserted.
1 row(s) inserted.
Routine dropped.
Routine created.
(expression)
2
1 row(s) retrieved.
Database closed.
D:\infx\ids12>
I think that what Jonathan was referring to when talking about dynamic SQL is that you don't need to build or prepare a SELECT statement using place holders (?) inside a SPL. You can execute mostly 'any' SQL statement.
The SPL above could be rewrite like:
D:\infx\ids12>cat 2.sql
DROP FUNCTION somefunc_simple;
CREATE FUNCTION somefunc_simple( p_name VARCHAR( 32 ), p_id INT, p_weight INT )
RETURNING INT;
DEFINE sp_id int;
LET sp_id=(SELECT FIRST 1 pr_id FROM sometable n WHERE n.pr_id> p_id and n.pr_weight = p_weight and ( UPPER( n.sname ) LIKE UPPER('%'||p_name||'%')) OR ( UPPER('%'||p_name||'%') LIKE UPPER( n.name)));
RETURN sp_id;
END FUNCTION;
EXECUTE FUNCTION somefunc_simple('TEST',1,200);
D:\infx\ids12>dbaccess stores7 2.sql
Database selected.
Routine dropped.
Routine created.
(expression)
2
1 row(s) retrieved.
Database closed.
D:\infx\ids12>
Have a read at the Informix SQL guide at
https://www.ibm.com/support/knowledgecenter/en/SSGU8G_12.1.0/com.ibm.sqls.doc/sqls.htm
It should give you an idea of how to write Informix SQL and SPL and what functionality is supported.
Also, Dynamic SQL is explained here:
https://www.ibm.com/support/knowledgecenter/en/SSGU8G_12.1.0/com.ibm.esqlc.doc/ids_esqlc_0528.htm

Row count column count in all tables in Sybase database

I have a database name ATs . In this database 150 tables. I want to create a statement that return the row count and column count all tables on the database .
I have created a store procedure For SQL SERVER 2008 but i don't know how to write this script for Sybase.
Sybase ASA has a bunch of system tables providing you with information about the structure of your database. The two tables that are of interest for you are SYSTABLE (all tables) and SYSCOLUMN (all columns).
I tried this quick and dirty stored procedure that works for me (on the rather aged ASA version 8!). It creates a temporary table and a cursor to iterate over all tables. For every table the table name, number of columns and number of rows are inserted into the temp table and finally returned.
(Hint: the tablefilter allows to return only a subset of the whole database, if you have many tables.)
CREATE PROCEDURE Usr_TableStats(in par_tablefilter char(100))
RESULT (tablename varchar(255), number_of_cols int, number_of_rows int)
BEGIN
declare err_notfound exception for sqlstate value '02000';
declare #table_id integer;
declare #tablename varchar(100);
declare #cols integer;
declare #sql varchar(300);
declare tables no scroll cursor for select table_id, table_name from sys.systable where table_type = 'BASE' and table_name like par_tablefilter || '%' order by table_name;
create table #tablestats (
tablename varchar(100) not null,
number_of_cols int not null default 0,
number_of_rows int not null default 0
);
open tables;
LoopTables: loop
fetch next tables into #table_id, #tablename;
if sqlstate = err_notfound then
leave LoopTables
else
SELECT COUNT(column_id) INTO #cols FROM SYSCOLUMN WHERE table_id = #table_id;
set #sql= 'INSERT INTO #tablestats SELECT ''' || #tablename || ''', ' || #cols || ', COUNT(*) FROM ' || #tablename || ';';
EXECUTE IMMEDIATE WITH QUOTES #sql;
end if
end loop LoopTables;
close tables;
SELECT tablename, number_of_cols, number_of_rows FROM #tablestats;
END
Call it in iSQL like this:
CALL Usr_TableStats('%'); -- all tables
CALL Usr_TableStats('ADDRESS%'); -- only tables starting with ADDRESS
sys.systable and sys.syscolumn should provide you with the information:
Select st.table_name,
st.count as row_count,
col_count = (SELECT count(*) FROM sys.syscolumn where table_id = st.table_id)
from SYS.SYSTABLE st where st.creator <> 0 and st.count > 0
order by st.table_name

MySQL Backup Table if it Exists

I am trying to write a script that will copy all the data in table a to table b if table a exists. Table b is the exact same structure as table a would be if it exists, though it may not. I am able to copy using the following statement: INSERT INTO 'B' SELECT * FROM 'A', but I don't know where to use IF EXISTS, or if I even can to determine if I an perform the insertion. I am trying to do this in SQL only as it will be run through as a .sql script from the command line.
MySQL only:
DROP PROCEDURE IF EXISTS myupdate;
DELIMITER //
CREATE PROCEDURE myupdate ()
BEGIN
DECLARE found VARCHAR(64);
SET found = (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = Database() AND TABLE_NAME = 'A');
IF found = 'types' THEN
INSERT INTO B SELECT * FROM A;
SELECT 'A into B';
ELSE
SELECT 'A not found';
END IF;
END;//
DELIMITER ;
CALL myupdate();
DROP PROCEDURE myupdate;
Expand to you're liking comparing the column definition in INFORMATION_SCHEMA.COLUMNS for A & B if you need finer control.
I have accepted Wrikken's answer but am using this as my final code. I need to reuse the procedure he provided for multiple tables, so I modified it slightly. It makes the assumption that the backup table has already been created.
DROP PROCEDURE IF EXISTS tableUpdate;
DELIMITER //
CREATE PROCEDURE tableUpdate(name VARCHAR(32))
BEGIN
DECLARE cnt tinyint(1);
DECLARE btable VARCHAR(36);
SET btable = CONCAT(name,'BAK');
SET cnt = (SELECT COUNT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'TAA' AND TABLE_NAME = name);
IF cnt > 0 THEN
SET #q:= CONCAT('INSERT INTO ',CONCAT(btable,CONCAT(' SELECT * FROM ',name)));
PREPARE stmt FROM #q;
EXECUTE stmt;
COMMIT;
ELSE
SELECT 'No update necessary.';
END IF;
END;//
DELIMITER ;
CALL tableUpdate('A');
DROP PROCEDURE tableUpdate;
You can do so by performing the following:
select count(*) from my_tables where table_name='b';
If count>0 then
update b...;
else
create table b;
insert into my_tables(table_name) values('b');
insert into b...;
end if;