I have a query that has multiple dynamic statements, each table takes creations. construct the sentence so dynamic but not how to capture an error if it can not create the table.
SELECT #SQL_CREATE = 'SELECT * INTO ' || #BD ||
'PAT_CDF_CONCEPTOS_SERVICIO_ACC' || FECHA_NUMBER ||
' FROM '|| #BD || 'PAT_CDF_CONCEPTOS_SERVICIO; COMMIT;';
execute (#SQL_CREATE);
If you are using ASE you can use syntax bellow:
execute (#SQL_CREATE);
IF ##error = 0
begin
print 'Table created'
end
else
begin
print 'Error'
end
Related
I am trying to call a procedure inside a procedure but this gives me an error like:
Uncaught exception of type 'STATEMENT_ERROR' on line 19 at position 2 : This session warehouse WH_STD_EDWQA_ANALYST no longer exists.
My parent procedure construct is like creating a warehouse & the child procedure is to populate a metadata table(custom) by use of table(result_scan(last_query_id())).
Parent procedure construct:
create or replace procedure wh_resource_govern(type varchar, env varchar, ..., varchar)
returns varchar not null
language sql
as
$$
declare
wh_name varchar;
wh_setup varchar;
lv_acct_name varchar;
begin
wh_name := 'WH_' || type || '_' || env || '_' || team;
wh_setup := 'CREATE OR REPLACE WAREHOUSE' || ' ' || wh_name || ' ' || 'WITH' || ' '
|| 'WAREHOUSE_SIZE = ' || v_wh_size || ' '
...,
|| 'COMMENT= '|| '"' || v_created_by || '"' ;
execute immediate wh_setup;
commit;
call load_all_warehouse_metadata('a', 'b', 'c'); ----> This is where it is getting stuck.
end;
$$
;
Child procedure construct is given as below:
create or replace procedure load_all_warehouse_metadata(wh_type varchar, wh_env varchar, wh_team varchar)
returns varchar not null
language sql
as
$$
declare
lv_acct_name varchar;
begin
select current_account() into lv_acct_name;
show warehouses;
insert into ALL_WAREHOUSE_METADATA (account_name, warehouse_type, .., .., )
select :lv_acct_name, :wh_type, :wh_env, :wh_team, "name", ..., ...,
from table(result_scan(last_query_id()));
end;
$$
;
Any inputs on how to address this would be really helpful.
Creating a warehouse immediately makes it the current warehouse for the session, example:
create or replace warehouse FOO;
select current_warehouse(); -- FOO
drop warehouse FOO();
select current_warehouse(); -- NULL
When you run execute immediate wh_setup; in the first SP, it's setting the session's warehouse to the one you just created. Calling a child SP using owner's rights (default) from a warehouse that isn't the one that started the SP is causing context problems for the warehouse.
You can reproduce this error as follows:
create or replace procedure SP1()
returns varchar not null
language sql
--execute as caller
as
$$
declare
currentWarehouse varchar;
begin
select current_warehouse() into currentWarehouse;
create or replace warehouse FOO;
call SP2();
return currentWarehouse;
end;
$$;
create or replace procedure SP2()
returns varchar not null
language sql
--execute as caller
as
$$
declare
currentWarehouse varchar;
begin
select current_warehouse() into currentWarehouse;
create or replace temp table FOO(s string);
insert into FOO(S) values ('Bar');
return 'Done';
end;
$$;
call SP1();
You can fix this code sample immediately by uncommenting the two commented-out ownership options for SP1 and SP2:
--execute as caller (Remove the comment markers and recreate both SPs.)
You can also run a SQL command to use warehouse <wh_name> in your SP(s), but you must run the SP as caller in order to change warehouse context this way.
First of all my question will be, if there is a way of auto-generation partitions (like interval in Oracle for range partitioning) while inserting data into the partitioned table in DB2?
At the moment I have a schema with some hundreds of tables, which are not partitioned. And I suppose to partition them.
My steps will be:
rename all the tables to OLD_table_name
execute DDLs for those tables but already partitioned (by load_id column int data type)
execute for all, insert into table_name select * from OLD_table_name
...and here it starts.
(Of course the process must be automatically and I don't know which values contain load_id column + they will be all different for all the tables, otherwise it would be possible to generate simply alter statements and execute them).
Therefore I would go for cursor.
For the moment I have solution which is working but I don't like it:
BEGIN
FOR CL AS MYCURS INSENSITIVE CURSOR FOR
select distinct 'alter table '||tb_nm||'
add partition PART_'||lpad(load_id,5,0)||'
starting from '||load_id||'
ending at '||load_id v_alt
from (
select load_id,'Table_01' tb_nm from Table_01
union
select load_id ,'Table_02'from Table_02
union
....
/*I have generated this union statements for whole set of tables*/)
do
execute immediate v_alt;
end for;
end
Also, I have tried some more elegant (on my opinion) variant, but didn't sucseed:
BEGIN
DECLARE v_stmnt VARCHAR(1000);
DECLARE v_check_val int;
DECLARE v_prep_stmnt STATEMENT;
for i as (select table_name
from sysibm.tables
where TABLE_SCHEMA ='shema_name'
)
do
SET v_stmnt = 'set ? = (SELECT distinct(load_id) FROM '||table_name||')';
PREPARE v_prep_stmnt FROM v_stmnt;
/*and here I stuck. I assume there must be possibility to run next execute as well in loop, but
all my attempts were not succsesfull*/
--EXECUTE v_prep_stmnt into v_check_val ;
end for;
END
Would highly appreciate any hint.
Try something like this:
--#SET TERMINATOR #
SET SERVEROUTPUT ON#
BEGIN
DECLARE L_TABSCHEMA VARCHAR(128) DEFAULT 'SCHEMA_NAME';
DECLARE L_COLNAME VARCHAR(128) DEFAULT 'LOAD_ID';
DECLARE L_VALUE INT;
DECLARE L_STMT VARCHAR(1024);
DECLARE SQLSTATE CHAR(5);
DECLARE C1 CURSOR FOR S1;
FOR I AS
SELECT TABNAME
FROM SYSCAT.COLUMNS
WHERE TABSCHEMA = L_TABSCHEMA AND COLNAME = L_COLNAME
DO
PREPARE S1 FROM 'SELECT DISTINCT(' || L_COLNAME || ') FROM ' || L_TABSCHEMA || '."' || I.TABNAME ||'"';
OPEN C1;
L1: LOOP
FETCH C1 INTO L_VALUE;
IF SQLSTATE<>'00000' THEN LEAVE L1; END IF;
SET L_STMT =
'alter table ' || L_TABSCHEMA || '."' || I.TABNAME || '" '
||'add partition PART_' || lpad(L_VALUE, 5, 0) || ' starting from ' || L_VALUE || ' ending at ' || L_VALUE;
--CALL DBMS_OUTPUT.PUT_LINE(L_STMT);
EXECUTE IMMEDIATE L_STMT;
END LOOP;
CLOSE C1;
END FOR;
END
#
I writed something like this below (it works), but in my case for 1,5 milions rows it is not so effective as I need (it will run maybe 2 days)
I saw something like BULK COLLECT FETCH FORALL etc. but I am not managing to rewrite my code to this without errors. Can you help me with it?
Thank you
--It is my code for rewriting
DECLARE
cnt NUMBER;
d_min NUMBER;
d_max NUMBER;
i NUMBER := 0;
CURSOR ts_metatata_cur IS select * from (select rownum as rn, id_profile from ts_metadata where typ=7 and per=3600 order by id_profile) where rn between 1 and 100000;
BEGIN
for metadata_rec in ts_metatata_cur
LOOP
XTS.GET_PROFILE_AGGR(metadata_rec.id_profile, cnt, d_min, d_max); --procedure with one IN parameter and three OUT parameter cnt, d_min, d_max
Execute immediate 'insert into TMP_PROFILES_OVERVIEW (id_profile, cnt, d_min, d_max) values (' || metadata_rec.id_profile || ', ' || cnt || ', ' || d_min || ', ' || d_max ||')';
i := i+1;
if (i > 10000) then
commit;
i := 0;
end if;
END LOOP;
commit;
END;
If it is necessary I give here procedure which I call:
--this is procedure, which I call in my script
CREATE OR REPLACE PROCEDURE XTS.GET_PROFILE_AGGR(id_prof IN NUMBER, cnt OUT NUMBER, d_min OUT NUMBER, d_max OUT NUMBER)
AS
res varchar2(61);
BEGIN
select cluster_table_name into res FROM XTS.TIME_SERIES TS where TS.id=id_prof;
Execute immediate 'select nvl(count(*),0), nvl(min(time),0), nvl(max(time),0) from '|| res || ' where time_series_id=' || id_prof || ' ' into cnt, d_min, d_max;
EXCEPTION
when others then
null;
END;
I changed my approach to task after advices from another people. I INSERT INTO only grouped data instead of INSERT INTO each row.
Something like this:
CREATE OR REPLACE PROCEDURE XTS.GET_PROFILE_AGGR
AS
CURSOR time_series_cur IS select distinct cluster_table_name as res from xts.time_serie;
BEGIN
for time_series_rec in time_series_cur
LOOP
Execute immediate 'INSERT INTO HDO.tmp_profiles_overview (id_profile, cnt, d_min, d_max) select time_series_id, count(*), min(time), max(time) from ' || time_series_rec.res || ' group by time_series_id';
END LOOP;
commit;
END;
I am doing some changes on my table and I couldn't figure out the problem.
This is my SQL script;
ALTER TABLE X ALTER COLUMN X_ID RESTART WITH (SELECT MAX(X_ID) FROM X);
Altough I used AS instead of WITH and tried other combinations, I couldn't find the exact syntax. (By the way, I cannot set this property in the initialization, I got to do it after creation of the table )
When looking at the syntax for ALTER TABLE you will find that you can only use a constant, e.g., "RESTART WITH 12345". A query itself is not possible. For automation you would need to break it up, use a variable, generate the ALTER statement and execute it.
Assuming this is for DB2 for LUW, you can automate the process of resetting identity values with some dynamic SQL:
begin
declare curmax bigint;
for r as select tabschema, tabname, colname, nextcachefirstvalue, seqid
from syscat.colidentattributes where tabschema = current_schema
do
prepare s from
'select max(' || r.colname || ') from ' ||
rtrim(r.tabschema) || '.' || r.tabname;
begin
declare c cursor for s;
open c;
fetch c into curmax;
close c;
end;
if curmax >= r.nextcachefirstvalue
then
execute immediate
'alter table ' || rtrim(r.tabschema) || '.' || r.tabname ||
' alter column ' || r.colname || ' restart with ' || varchar(curmax+1);
end if;
end for;
end
You may need to change the data type of curmax if your identities are not integer, and adjust the query against syscat.colidentattributes to use the appropriate schema name.
I need to dynamically populate an Oracle cursor (Oracle 10g). The SQL statement changes, based off of the input value to pull from different tables, and columns. What I don't want to do is have to maintain a temporary table that I truncate and load everytime the sproc is executed. Here is what I am currently doing, but if there is another alternative I would appreciate the help:
Stored Procedure
PROCEDURE Get_Type_One_Polygon_Values(in_role VARCHAR2, rc_generic OUT SYS_REFCURSOR) as
BEGIN
execute immediate 'truncate table teamchk.temp_type_one_roles';
execute immediate 'INSERT INTO TEAMCHK.TEMP_TYPE_ONE_ROLES ' ||
'SELECT ' || in_role || '_POLY_ID, ' || in_role || '_POLY_NAME ' ||
'FROM TEAMCHK.' || in_role;
open rc_generic for
select * from teamchk.temp_type_one_roles;
END;
Temp Table
CREATE TABLE TEAMCHK.TEMP_TYPE_ONE_ROLES
(
ROLE_ID NUMERIC(38,0),
ROLE_NAME VARCHAR2(75)
);
That's easy, you can use dynamic cursors...
create or replace PROCEDURE Get_Type_One_Polygon_Values
(in_role VARCHAR2, rc_generic OUT SYS_REFCURSOR) as
sql varchar2(100);
BEGIN
sql :='SELECT ' || in_role || '_POLY_ID, '
|| in_role || '_POLY_NAME '
|| 'FROM TEAMCHK.' || in_role;
open rc_generic for sql;
END;
It may be beneficial to use column aliases POLY_ID and POLY_NAME to unify them all in the refcursor.