Range partitioning tables with data in DB2 LUW - loops

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
#

Related

Show a few columns in a stored procedure based on a parameter

I have a parameter in my stored procedure called internal. If "internal" = yes then I want to display an additional 2 columns in my results. If it's no I don't want to display these columns.
I can do a case statement and then I can set the column to be empty but the column name will still be returned in the results.
My questions are:
Is there a way not to return these 2 columns in the results at all?
Can I do it in one case statement and not a separate case statement for each column?
Thank you
No, CASE is a function, and can only return a single value.
And According to your comment:-
The issue with 2 select statements are that it's a major complicated
select statement and I really don't want to have to have the whole
select statement twice.
so you can use the next approach for avoid duplicate code:-
Create procedure proc_name (#internal char(3), .... others)
as
BEGIN
declare #AddationalColumns varchar(100)
set #AddationalColumns = ''
if #internal = 'Yes'
set #AddationalColumns = ',addtionalCol1 , addtionalCol2'
exec ('Select
col1,
col2,
col3'
+ #AddationalColumns +
'From
tableName
Where ....
' )
END
Try IF Condition
IF(internal = 'yes')
BEGIN
SELECT (Columns) FROM Table1
END
ELSE
BEGIN
SELECT (Columns With additional 2 columns) FROM Table1
END
You can do something like this solution. It allows you to keep only one copy of code if it's so important but you will have to deal with dynamic SQL.
CREATE TABLE tab (col1 INT, col2 INT, col3 INT);
GO
DECLARE #internal BIT = 0, #sql NVARCHAR(MAX)
SET #sql = N'SELECT col1 ' + (SELECT CASE #internal WHEN 1 THEN N', col2, col3 ' ELSE N'' END) + N'FROM tab'
EXEC sp_executesql #sql
GO
DROP TABLE tab
GO
Another option is to create a 'wrapper' proc. Keep your current one untouched.
Create a new proc which executes this (pseudo code):
create proc schema.wrapperprocname (same #params as current proc)
as
begin
Create table #output (column list & datatypes from current proc);
insert into #output
exec schema.currentproc (#params)
if #internal = 'Yes'
select * from #output
else
select columnlist without the extra 2 columns from #output
end
This way the complex select statement remains encapsulated in the original proc.
Your only overhead is keeping the #output table & select lists in in this proc in sync with any changes to the original proc.
IMO it's also easier to understand/debug/tune than dynamic sql.

can one use temp tables in teradata

Is there an equivalent for this sql server tsql in teradata:
IF OBJECT_ID('tempdb..#SomeTempTable') IS NOT NULL DROP TABLE #SomeTempTable;
CREATE TABLE #SomeTempTable (Bla NVARCHAR(255));
INSERT INTO #SomeTempTable
SELECT N'a'
UNION ALL
SELECT N'B'
There's no equivalent for the 1st statement, there might be a Stored Procedure for it like this:
REPLACE PROCEDURE Drop_Table_If_Exists
(
IN db_name VARCHAR(128) CHARACTER SET UNICODE,
IN tbl_name VARCHAR(128) CHARACTER SET UNICODE,
OUT msg VARCHAR(400) CHARACTER SET UNICODE
) SQL SECURITY INVOKER
BEGIN
DECLARE full_name VARCHAR(361) CHARACTER SET UNICODE;
DECLARE sql_stmt VARCHAR(500) CHARACTER SET UNICODE;
DECLARE exit HANDLER FOR SQLCODE 'T3807'--SQLEXCEPTION
BEGIN
IF SQLCODE = 3807 THEN SET msg = full_name || ' doesn''t exist.';
ELSE
RESIGNAL;
END if;
END;
SET full_name = '"' || COALESCE(db_name,DATABASE) || '"."' || tbl_name || '"';
SET sql_stmt = 'DROP TABLE ' || full_name || ';';
EXECUTE IMMEDIATE sql_stmt;
SET msg = full_name || ' dropped.';
END;
A VOLATILE table exists only within your current session (i.e. the same name might be used in different sessions for different tables) and is automatically dropped when the session disconnects. When you keep the naming convention (name of a temporary starts with #) you probably don't need the conditional Drop (you should know if you already created this table in the current session):
CREATE VOLATILE TABLE #SomeTempTable(
Bla VARCHAR(255) CHARACTER SET UNICODE)
ON COMMIT PRESERVE ROWS;
Caution, if you don't specify the Primary Index it will default to a NUPI on the first column.
The Select has a strange restriction, you need a FROM when you do a Set Operation like UNION/INTERSECT/EXCEPT. Workaround is either a dummy view (similar to Oracle's DUAL table) like this:
replace view dummy as select 1 as x;
INSERT INTO #SomeTempTable
SELECT 'a' FROM dummy
UNION ALL
SELECT 'B' FROM dummy
or a similar CTE:
INSERT INTO #SomeTempTable
WITH dummy AS (select 1 as x)
SELECT 'a' FROM dummy
UNION ALL
SELECT 'B' FROM dummy

SQL Use Declare to Omit Where Clause Items

Is there a way to exclude parts of a SQL Statement based on Declared Values?
For instance;
DECLARE #OnlyY as VARCHAR(1) = 'Y'
SELECT count(*) from main where IDATE > '2016-01-01'
If #OnlyY = 'Y' THEN
AND Qualify = 'Y'
END IF
In this case if #OnlyY isn't Y then the part in between the if/endif wouldn't happen at all.
The reason I need this is because I am porting an Access 97 application to .NET. In the Access 97 app there is a part that creates a temporary table and then generates a report from that table. The SQL involved is a huge set of if/then statements that remove data from the temporary table. I'm able to build the DataTable for viewing in a Datagridview. My issue is that I can't get a SSRS to have the same flexibility as .NET in the if/then statements.
So how should I go about doing this?
Try this:
SELECT COUNT(*)
FROM main
WHERE IDATE > '2016-01-01' AND
((Qualify = 'Y') OR (#OnlyY <> 'Y'))
If #OnlyY is not equal to 'Y', then the WHERE clause boils down to:
WHERE IDATE > '2016-01-01'
otherwise, WHERE clause becomes:
WHERE IDATE > '2016-01-01' AND (Qualify = 'Y')
One option you can look into is Dynamic SQL in which you can dynamically change anything you would need.
DECLARE #SQL AS NVARCHAR(MAX)
SET #SQL = N'SELECT COUNT(*) FROM main WHERE IDATE > ''2016-01-01'''
IF #OnlyY = 'Y'
BEGIN
SET #SQL += N' AND Qualify = ''Y'''
END
EXECUTE sp_executesql #SQL

Restart initial id of table with using MAX() method

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.

SQL iterative joining within while loop

I know that it's not best practice to apply a while loop within SQL code, but I can't really imagine a way around it.
I have an ever-growing set of historical tables that I want to summarize, and join, using a common ID. All of these historical tables have matching structure.
The table names contain the month and year; I iterate through all tables found between two periods - determined using the fixed timestamp #StartDate, and the timestamp #EndDate via GETDATE().
The while loop is intended to continually append additional columns from each historic file. There is one month per iteration, and each column contains the pertaining month's summary.
The problem is, every left join seems to erase what was there in the previous iteration. I use a conditional statement to determine if the temp table exists, and build it using a select ... into statement if it doesn't, and an insert into statement if it does.
The following is what I have:
while #MonthCount > 0
begin
select #Month = DateSuffix from #DateTable where Row = #MonthCount
select #Table = table_name from information_schema.tables where table_name like concat('USA_RETHHDs_%', #Month)
if #Table is not null
begin
if object_id('tempdb.dbo.#RETHHDS', 'U') is not null
drop table #RETHHDS
create table #RETHHDS
(BUC_CD varchar(12),
RET_BKD_HH_COUNT int)
select #Table = concat('dbo.', #Table)
set #sql =
'insert into #RETHHDS
select BUC_CD, count(distinct SK_HH_ID) as RET_BKD_HH_COUNT from ' + #Table +
' group by BUC_CD'
execute SP_EXECUTESQL #sql;
if object_id('tempdb.dbo.#HISTORICAL_TABLE', 'U') is null
begin
select #USA_BRANCHCATALOGUE.TR, #USA_BRANCHCATALOGUE.NAME, #RETHHDS.RET_BKD_HH_COUNT into #HISTORICAL_TABLE
from #USA_BRANCHCATALOGUE
left join #RETHHDS
on #USA_BRANCHCATALOGUE.TR = #RETHHDS.BUC_CD
end
else
begin
insert into #HISTORICAL_TABLE
select #USA_BRANCHCATALOGUE.TR, #USA_BRANCHCATALOGUE.NAME, #RETHHDS.RET_BKD_HH_COUNT
from #USA_BRANCHCATALOGUE
left join #RETHHDS
on #USA_BRANCHCATALOGUE.TR = #RETHHDS.BUC_CD
end
select * from #HISTORICAL_TABLE
end
select #MonthCount = #MonthCount - 1
end
As I loop through, the #HISTORICAL_TABLE does not grow with additional rightmost columns. Instead, it remains with columns TR, NAME, and RET_BKD_HH_COUNT.
Bonus points if there's an easy way to append the corresponding month to the RET_BKD_HH_COUNT field. However, when I do the following,
set #sql =
'create table #RETHHDS
(BUC_CD varchar(12),
RET_BKD_HH_COUNT_' + #Month + ' int)'
execute SP_EXECUTESQL #sql;
I receive the following message:
Invalid object name '#RETHHDS'.

Resources