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
Related
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
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
IF EXISTS (SELECT abc FROM OBJECTS WHERE NAME = 'BIG' AND type = 'U')
DROP TABLE FIRSTDATA
go
CREATE TABLE FIRSTDATA
(
ROLLNO varchar(50) NOT NULL,
STNAME varchar(3) NULL,
)
go
INSERT INTO FIRSTDATA (ROLLNO, STNAME)
VALUES ('13', 'Becky)
You can use USER_TABLES to check if table already exists or not as follows:
Declare
CNT NUMBER:= 0;
BEGIN
SELECT COUNT(1)
INTO CNT
FROM USER_TABLES
WHERE TABLE_NAME = 'FIRSTDATA';
IF CNT = 0 THEN
EXECUTE IMMEDIATE 'CREATE TABLE FIRSTDATA
( ROLLNO varchar(50) NOT NULL, STNAME varchar(3) NULL )' ;
END IF;
END;
/
Note the following about your example:
Assuming based on the name that "ROLLNO" is intended to be a number, why are you using a varchar2 data type?
You define STNAME as varchar2(3), then insert a value with 5 characters. This will not work.
Oracle does not have a native "if exists" clause for DDL commands,
nor can you execute DDL commands directly within a PL/SQL block -
you must use an "EXECUTE IMMEDIATE" statement.
A better example would then be:
declare
cnt number;
begin
select count(*) into cnt from user_tables where table_name = 'FIRSTDATA';
if cnt > 0 then
execute immediate 'drop table firstdata';
end if;
execute immediate 'create table firstdata (rollno number not null, stname varchar2(30) )';
execute immediate 'insert into firstdata (rollno, stname) values (:rollno,:stname)' USING 50, 'Becky';
commit;
end;
/
I have a table "test_calculate" this has a column "CONN_BY" having values
column can have more than 2 number to multiply and this table may contain millions of rows , I need to get the result of the calculation from "CONN_BY" to "MVP".
I have used xmlquery for the calculation and dynamic query but these are quite slow. Is there another way which is much faster .Please suggest.
You can try the dynamic query.
Create a function which returns the calculated value and use it in your insert or select queries.
CREATE OR REPLACE FUNCTION UFN_CALCULATE (CLM_VALUE VARCHAR2)
RETURN NUMBER IS
RES_VAL NUMBER;
BEGIN
EXECUTE IMMEDIATE 'select '||CLM_VALUE||' FROM DUAL' INTO RES_VAL;
RETURN RES_VAL;
END;
You can use that function like below.
SELECT UFN_CALCULATE('.0876543 * .09876') FROM DUAL;
SELECT UFN_CALCULATE(CONN_BY) FROM YOUR_TABLE;
One option is using select ... connect by level <= regexp_count(conn_by,'[^*]+')... query for the implicit cursor within a PL/SQL code block
SQL> set serveroutput on
SQL> declare
mvp owa.nc_arr; -- numeric array to initialize each multiplication to 1 for each id value
begin
dbms_output.put_line('ID MVP');
dbms_output.put_line('--------');
for c in
(
select id,
to_number( regexp_substr(conn_by,'[^*]+',1,level) ) as nr,
level as lvl , max( level ) over ( partition by id ) as mx_lvl
from test_calculate
connect by level <= regexp_count(conn_by,'[^*]+')
and prior sys_guid() is not null
and prior conn_by = conn_by
order by id, lvl
)
loop
if c.lvl = 1 then mvp(c.id) := 1; end if;
mvp(c.id) := c.nr * mvp(c.id);
if c.lvl = c.mx_lvl then
dbms_output.put_line(c.id||' '||mvp(c.id));
end if;
end loop;
end;
/
where test_calculate is assumed to have an identity column(id)
Demo
For example a I have two schemas: SCHEMA_1 and SCHEMA_2. In SHEMA_1 I have a table named TABLE. This table includes two fields: FIELD_1, FIELD_2. In TABLE in FIELD_1 I have some letters: A, B, C. FIELD_2 has tables' names of SCHEMA 2: TABLE_10, TABLE_20, TABLE_30.
SCHEMA_2 includes three tables: TABLE_10, TABLE_20, TABLE_30 with some numbers.
enter image description here
I have to write query to get maximum number of each table in SCHEMA_2. How can I get this result>
enter image description here
Try this:
create table schema1.tableA
(field_1 char(1),
field_2 varchar(10));
insert into schema1.tableA
values ('A','table_10'),
('B','table_20'),
('C','table_30');
create table schema2.table_10
(field_1 dec(5,0));
insert into schema2.table_10
values (20), (30), (40);
create table schema2.table_20
(field_1 dec(5,0));
insert into schema2.table_20
values (6), (9), (12);
create table schema2.table_30
(field_1 dec(5,0));
insert into schema2.table_30
values (10), (15), (20);
with tmp (table_name, field_1) as (
select 'table_10', max(field_1) from schema2.table_10
union all
select 'table_20', max(field_1) from schema2.table_20
union all
select 'table_30', max(field_1) from schema2.table_30)
select a.field_1, b.field_1
from schema1.tableA a
join tmp b on b.table_name = a.field_2;
If you have too many tables to make the above work, you can use a user defined function like this:
create or replace function MaxNbr
(p_TableName varchar(128),
p_TableSchema varchar(128))
Returns dec(5,0)
language sql
not deterministic
no external action
reads sql data
returns null on null input
not fenced
begin
declare l_stmt varchar(1024);
declare l_table varchar(128);
declare l_schema varchar(128);
declare l_result dec(5,0);
set l_table = replace(upper(p_TableName),'"','');
set l_schema = replace(upper(p_TableSchema),'"','');
set l_stmt = 'values (select max(field_1) from "' || l_schema || '"."' ||
trim(l_table) || '") into ?';
prepare S1 from l_stmt;
allocate sql descriptor 'D1';
describe S1 using sql descriptor 'D1';
execute S1 into sql descriptor 'D1';
get sql descriptor 'D1' value 1 l_result = data;
deallocate sql descriptor 'D1';
return l_result;
end;
This is necessary because you cannot use a variable for an identifier like a table name or schema name.
To use the user defined function, you can do something like this:
set schema schema1;
set path = udf_schema;
select field_1, MaxNbr(field_2, 'schema2') as field_2
from tableA;