Convert T-SQL Declare statement to SAP HANA - converters

I cannot seem to get the query with declare statement working in SAP HANA. Below I've put the original working T-SQL version and the HANA version output from the SQL converter. I've tried several versions and combinations, but every time I get errors which you also find below. Anybody willing to give me an "how to" so I can copy this? I also spelled out the SAP Documentations, but nothing there what could help me.
Your help would be very appreciated.
The T-SQL Code:
DECLARE #NumAtCardDuplicate VARCHAR(50)
SET #NumAtCardDuplicate =
(SELECT TOP 1 DocNum
FROM TEST_RSCA.OPCH
WHERE CardCode = 'S100424'
AND NumAtCard = '118 120 266 805')
IF #NumAtCardDuplicate IS NOT NULL
SELECT 'Invoice number already used in entry ' + #NumAtCardDuplicate + '!'
ELSE
SELECT '118 120 266 805'
The translated HANA query:
NumAtCardDuplicate varchar(50);
SELECT
(SELECT TOP 1 "DocNum"
FROM TEST_RSCA.OPCH
WHERE "CardCode" = 'S100424'
AND "NumAtCard" = '118 120 266 805')
INTO NumAtCardDuplicate FROM DUMMY;
temp_var_0 integer;
SELECT :NumAtCardDuplicate INTO temp_var_0 FROM DUMMY;
IF :temp_var_0 IS NOT NULL THEN
SELECT 'Invoice number already used in entry ' || :NumAtCardDuplicate || '!'
FROM DUMMY;
ELSE
SELECT '118 120 266 805'
FROM DUMMY;
END IF;
The Errors I get:
Could not execute 'NumAtCardDuplicate varchar(50)' in 1 ms 989 µs .
SAP DBTech JDBC: [257]: sql syntax error: incorrect syntax near "NumAtCardDuplicate": line 1 col 1 (at pos 1)
Could not execute 'SELECT (SELECT TOP 1 "DocNum" FROM TEST_RSCA.OPCH WHERE "CardCode" ='S100424' AND "NumAtCard" = ...' in 3 ms 578 µs .
SAP DBTech JDBC: [337] (at 119): INTO clause not allowed for this SELECT statement: line 4 col 67 (at pos 119)
Could not execute 'temp_var_0 integer' in 1 ms 701 µs .
SAP DBTech JDBC: [257]: sql syntax error: incorrect syntax near "temp_var_0": line 1 col 1 (at pos 1)
Could not execute 'SELECT :NumAtCardDuplicate INTO temp_var_0 FROM DUMMY' in 1 ms 976 µs .
SAP DBTech JDBC: [467]: cannot use parameter variable: NUMATCARDDUPLICATE: line 4294967295 col 4294967295 (at pos 4294967295)
Could not execute 'IF :temp_var_0 IS NOT NULL THEN SELECT 'Invoice number already used in entry ' || ...' in 1 ms 560 µs .
SAP DBTech JDBC: [257]: sql syntax error: incorrect syntax near "IF": line 1 col 1 (at pos 1)
Could not execute 'ELSE SELECT '118 120 266 805' FROM DUMMY' in 1 ms 338 µs .
SAP DBTech JDBC: [257]: sql syntax error: incorrect syntax near "ELSE": line 1 col 1 (at pos 1)
SAP DBTech JDBC: [257]: sql syntax error: incorrect syntax near "END": line 1 col 1 (at pos 1)
Duration of 7 statements: 13 ms

I created a tables OPCH as shown below and inserted a row
create column table "SYSTEM"."OPCH"( "DOCNUM" VARCHAR (50) not null,
"CARDCODE" VARCHAR (7) null,
"NUMATCARD" VARCHAR (15) null,
primary key ("DOCNUM") )
insert into "SYSTEM"."OPCH" values('1','S100424','118 120 266 805');
Created a procedure with SQL Script as shown below
CREATE PROCEDURE SYSTEM.Z_CARDDUPLICATE LANGUAGE SQLSCRIPT
AS
BEGIN
declare NumAtCardDuplicate varchar2(50);
SELECT TOP 1 DocNum
INTO NumAtCardDuplicate
FROM SYSTEM.OPCH
WHERE CardCode = 'S100424'
AND NumAtCard = '118 120 266 805';
IF :NumAtCardDuplicate IS NOT NULL THEN
SELECT 'Invoice number already used in entry ' || :NumAtCardDuplicate || '!'
FROM DUMMY;
ELSE
SELECT '118 120 266 805'
FROM DUMMY;
END IF;
End
Called the procedure from SQL Console
CALL SYSTEM.Z_CARDDUPLICATE;
It works. Result is "Invoice number already used in entry 1!"
Statement 'CALL SYSTEM.Z_CARDDUPLICATE'
successfully executed in 132 ms 11 µs (server processing time: 29 ms 588 µs)
Fetched 1 row(s) in 0 ms 10 µs (server processing time: 0 ms 0 µs)
Let me know if this does not work for you.

To execute SQLScript on SQL Console, you are not required to create procedures any more in fact
Simply surround your SQL code between DO BEGIN ... END; clause as follows
do begin
declare NumAtCardDuplicate varchar(50);
SELECT TOP 1 DocNum
INTO NumAtCardDuplicate
FROM KODYAZ.OPCH
WHERE CardCode = 'S100424'
AND NumAtCard = '118 120 266 805';
IF :NumAtCardDuplicate IS NOT NULL THEN
SELECT 'Invoice number already used in entry ' || :NumAtCardDuplicate || '!'
FROM DUMMY;
ELSE
SELECT '118 120 266 805'
FROM DUMMY;
END IF;
end;
Please note that I used the same code block that Reddy has suggested to use

Related

Execute immediate use in select * from in oracle

I am trying to get maximum length of column data in oracle by executing a dynamic query as part of Select statement , except seems i don't think we can use execute immediate in an select clause. Could i get some help with syntax or understanding as to better way to do this.
SELECT
owner OWNER,
table_name,
column_name,
'select max(length('||column_name||')) from '||table_name||';' max_data_length
FROM
dba_tab_columns
WHERE
AND ( data_type = 'NUMBER'
OR data_type = 'INTEGER' )
the 4th column in above query spits out a sql string rather than computing the value and returning it.
Here is some food for thought. Note that I am only looking for numeric columns that don't already have precision specified in the catalog. (If you prefer, you can audit all numeric columns and compare the declared precision against the actual precision used by your data.)
I am also looking only in specific schemas. Instead, you may give a list of schemas to be ignored; I hope you are not seriously considering making any changes to SYS, for example, even if it does (and it does!) have numeric columns without specified precision.
The catalog doesn't store INTEGER in the data type; instead, it stores that as NUMBER(38) So I am not searching for data type INTEGER in DBA_TAB_COLUMNS. But this raises an interesting question - perhaps you should search for all columns where DATA_PRECISION is null (as in my code below), but also for DATA_PRECISION = 38.
In the code below I use DBMS_OUTPUT to display the findings directly to the screen. You will probably want to do something smarter with this; either create a table function, or create a table and insert the findings in it, or perhaps even issue DDL already (note that those also require dynamic SQL).
This still leaves you to deal with scale. Perhaps you can get around that with a specification like NUMBER(prec, *) - not sure if that will meet your needs. But the idea is similar; you will just need to write code carefully, like I did for precision (accounting for the decimal point and the minus sign, for example).
Long story short, here is what I ran on my system, and the output it produced.
declare
prec number;
begin
for rec in (
select owner, table_name, column_name
from all_tab_columns
where owner in ('SCOTT', 'HR')
and data_type = 'NUMBER'
and data_precision is null
)
loop
execute immediate
'select max(length(translate(to_char(' || rec.column_name ||
'), ''0-.'', ''0'')))
from ' || rec.owner || '.' || rec.table_name
into prec;
dbms_output.put_line('owner: ' || lpad(rec.owner, 12, ' ') ||
' table name: ' || lpad(rec.table_name, 12, ' ') ||
' column_name: ' || lpad(rec.column_name, 12, ' ') ||
' precision: ' || prec);
end loop;
end;
/
PL/SQL procedure successfully completed.
owner: HR table name: REGIONS column_name: REGION_ID precision: 1
owner: HR table name: COUNTRIES column_name: REGION_ID precision: 1
owner: SCOTT table name: SALGRADE column_name: GRADE precision: 1
owner: SCOTT table name: SALGRADE column_name: LOSAL precision: 4
owner: SCOTT table name: SALGRADE column_name: HISAL precision: 4
PL/SQL procedure successfully completed.
EDIT
Here are several additional points (mostly, corrections) based on extended conversations with Sayan Malakshinov in comments to my answer and to his.
Most importantly, even if we can figure out max precision of numeric columns, that doesn't seem directly related to the ultimate goal of this whole thing, which is to determine the correct Postgre data types for the existing Oracle columns. For example in Postgre, unlike Oracle, it is important to distinguish between integer and non-integer. Unless scale is explicitly 0 in Oracle, we don't know that the column is "integers only"; we could find that out, through a similar dynamic SQL approach, but we would be checking for non-integer values, not precision.
Various corrections: My query is careless with regard to quoted identifiers (schema name, table name, column name). See the proper use of double-quotes in the dynamic query in Sayan's answer; my dynamic query should be modified to use double-quotes in the same way his does.
In my approach I pass numbers through TO_CHAR and then remove minus sign and decimal period. Of course, one's system may use comma, or other symbols, for decimal separator; the safer approach is to remove everything that is not a digit. That can be done with
translate(col_name, '0123456789' || col_name, '0123456789')
The query also doesn't handle very large or very small numbers, which can be stored in the Oracle database, but can only be represented in scientific notation when passed through TO_CHAR().
In any case, since "max precision" doesn't seem directly related to the ultimate goal of mapping to correct data types in Postgre, I am not changing the code - leaving it in the original form.
Thanks to Sayan for pointing out all these issues.
One more thing - *_TAB_COLUMNS contains information about view columns too; very likely those should be ignored for the task at hand. Very easy to do, as long as we realize it needs to be done.
Reading carefully that AWS article and since both previous answers (including mine) use rough estimate (length+to_char without format model and vsize operate decimal length, not bytes), I decided to write another full answer.
Look at this simple example:
with
function f_bin(x number) return varchar2 as
bi binary_integer;
e_overflow exception;
pragma exception_init(e_overflow, -1426);
begin
bi:=x;
return case when bi=x then 'ok' else 'error' end;
exception when e_overflow then return 'error';
end;
function f_check(x number, f varchar2) return varchar2 as
begin
return case when to_number(to_char(abs(x),f),f) = abs(x) then 'ok' else 'error' end;
exception when VALUE_ERROR then return 'error';
end;
a(a1) as (
select * from table(sys.odcinumberlist(
1,
0.1,
-0.1,
-7,
power(2,15)-1,
power(2,16)-1,
power(2,31)-1,
power(2,32)-1
))
)
select
a1,
f_check(a1,'fm0XXX') byte2,
f_check(a1,'fm0XXXXXXX') byte4,
f_bin(a1) ff_signed_binary_int,
to_char(abs(a1),'fm0XXXXXXXXXXXXXXX') f_byte8,
f_check(a1,'fm0XXXXXXXXXXXXXXX') byte8,
vsize(a1) vs,
dump(a1) dmp
from a;
Result:
A1 BYTE2 BYTE4 FF_SIGNED_ F_BYTE8 BYTE8 VS DMP
---------- ---------- ---------- ---------- ---------------- ---------- ---------- ----------------------------------------
1 ok ok ok 0000000000000001 ok 2 Typ=2 Len=2: 193,2
.1 error error error 0000000000000000 error 2 Typ=2 Len=2: 192,11
-.1 error error error 0000000000000000 error 3 Typ=2 Len=3: 63,91,102
-7 ok ok ok 0000000000000007 ok 3 Typ=2 Len=3: 62,94,102
32767 ok ok ok 0000000000007FFF ok 4 Typ=2 Len=4: 195,4,28,68
65535 ok ok ok 000000000000FFFF ok 4 Typ=2 Len=4: 195,7,56,36
2147483647 error ok ok 000000007FFFFFFF ok 6 Typ=2 Len=6: 197,22,48,49,37,48
4294967295 error ok error 00000000FFFFFFFF ok 6 Typ=2 Len=6: 197,43,95,97,73,96
Here I used PL/SQL functions for readability and to make it more clear.
Function f_bin casts an input number parameter to PL/SQL binary_integer (signed int4) and compares the result with input parameter, ie it checks if it loses accuracy. Defined exception shows that it can raise an exception 1426 "numeric overflow".
Function f_check does double conversion to_number(to_char(...)) of the input value and checks if it's still equal to the input value. Here I use hexadecimal format mask (XX = 1 byte), so it checks if an input number can fit an in this format mask. Hexadecimal format model works with non-negative numbers, so we need to use abs() here.
F_BYTE8 shows formatted value that uses a function from the column BYTE8, so you can easily see the loss of accuracy here.
All the above were just for readability, but we can make the same using just pure SQL:
with
a(a1) as (
select * from table(sys.odcinumberlist(
1,
0.1,
-0.1,
-7,
power(2,15)-1,
power(2,16)-1,
power(2,31)-1,
power(2,32)-1
))
)
select
a1,
case when abs(a1) = to_number(to_char(abs(a1),'fmXXXXXXXXXXXXXXX') default null on conversion error,'fmXXXXXXXXXXXXXXX')
then ceil(length(to_char(abs(a1),'fmXXXXXXXXXXXXXXX'))/2)
else -1
end xx,
vsize(a1) vs,
dump(a1) dmp
from a;
Result:
A1 XX VS DMP
---------- ---------- ---------- ----------------------------------------
1 1 2 Typ=2 Len=2: 193,2
.1 -1 2 Typ=2 Len=2: 192,11
-.1 -1 3 Typ=2 Len=3: 63,91,102
-7 1 3 Typ=2 Len=3: 62,94,102
32767 2 4 Typ=2 Len=4: 195,4,28,68
65535 2 4 Typ=2 Len=4: 195,7,56,36
2147483647 4 6 Typ=2 Len=6: 197,22,48,49,37,48
4294967295 4 6 Typ=2 Len=6: 197,43,95,97,73,96
As you can see, here I return -1 in case of conversion errors to byte8 and number of non-zero bytes otherwize.
Obviusly it can be simplified even more: you can just check range limits and that x=trunc(x) or mod(x,1)=0.
Looks like that is what you need:
VSIZE returns the number of bytes in the internal representation of expr. If expr is null, then this function returns null.
https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/VSIZE.html
In Oracle INTEGER is just a number(*,0): http://orasql.org/2012/11/10/differences-between-integerint-in-sql-and-plsql/
select
owner,table_name,column_name,
data_type,data_length,data_precision,data_scale,avg_col_len
,x.vs
from (select/*+ no_merge */ c.*
from dba_tab_columns c
where data_type='NUMBER'
and owner not in (select username from dba_users where ORACLE_MAINTAINED='Y')
) c
,xmltable(
'/ROWSET/ROW/VSIZE'
passing dbms_xmlgen.getxmltype('select nvl(max(vsize("'||c.column_name||'")),0) as VSIZE from "'||c.owner||'"."'||c.table_name||'"')
columns vs int path '.'
) x
;
Update: if you read oracle internal number format (exponent+bcd-mantissa and look at a result of dump(x) function, you can see that Oracle stores numbers as a decimal value(4 bits per 1 decimal digit, 2 digits in 1 byte), so for such small ranges you can just take their maximum BCD mantissa+1 exponent as a rough estimation.

Number of trailing zeros in a number

I have a column MONTHLY_SPEND in the table with data type of NUMBER. I am trying to write a query which will return number of zeros in the column.
e.g..
1000 will return 3
14322 will return 0
1230 will return 1
1254000.65 will return 0
I tried using mod operator and 10 but without the expected result. Any help is appreciated. Please note that database is Oracle and we can't create procedure/function.
select nvl(length(regexp_substr(column, '0+$')), 0) from table;
Here is one way to find
create table spend
(Monthly_spend NUMBER);
Begin
insert into spend values (1000)
insert into spend values (14322)
insert into spend values (1230)
insert into spend values (1254000.65)
End;
This query will for this data :
select Monthly_spend,REGEXP_COUNT(Monthly_spend,0)
from spend
where Monthly_spend not like '%.%' ;
if have one more data like 102 and if it should be zero , then try below query:
select Monthly_spend,case when substr(Monthly_spend,-1,1)=0 THEN REGEXP_COUNT(Monthly_spend,0) ELSE 0 END from spend;
Here is final query for value like 2300120 or 230012000
select Monthly_spend,
case when substr(Monthly_spend,-1,1)=0 and REGEXP_COUNT(trim (0 from Monthly_spend),0)<=0 THEN REGEXP_COUNT(Monthly_spend,0)
when REGEXP_COUNT(trim (0 from Monthly_spend),0)>0 THEN LENGTH(Monthly_spend) - LENGTH(trim (0 from Monthly_spend))
ELSE 0 END from spend;
Output :
1000 3
1254000.65 0
14322 0
1230 1
102 0
2300120 1
230012000 3
You can try this, a simple solution.
select length(to_char(col1))-length(rtrim(to_char(col1), '0')) no_of_trailing_zeros from dual;
select length(to_char('123.120'))-length(rtrim(to_char('123.120'), '0')) no_of_trailing_zeros from dual;

Case Expression throwing exception

Consider the following code:
Select FLAGS,
CASE FLAGS
WHEN 0 THEN "10000000"
WHEN 1 THEN "01000000"
WHEN 2 THEN "00100000"
WHEN 3 THEN "00010000"
WHEN 4 THEN "00001000"
WHEN 5 THEN "00000100"
WHEN 6 THEN "00000010"
WHEN 7 THEN "00000001"
ELSE "00000000"
END AS Test-W
FROM V_TEST
The above statement is throwing error as:
ORA-00923: FROM keyword not found where expected
00923. 00000 - "FROM keyword not found where expected"
*Cause:
*Action: Error at Line: 14 Column: 17
My table name is V_TEST, column name is FLAGS. What am I doing wrong here?
use single quote ` for the literal expressions
The column Test-W needs to be enclosed in dobule quote
Select FLAGS,
CASE FLAGS
WHEN 0 THEN '10000000'
WHEN 1 THEN '01000000'
WHEN 2 THEN '00100000'
WHEN 3 THEN '00010000'
WHEN 4 THEN '00001000'
WHEN 5 THEN '00000100'
WHEN 6 THEN '00000010'
WHEN 7 THEN '00000001'
ELSE '00000000'
END as "Test-W"
FROM V_TEST
Of course, CASE is verbose and easy to interpret. You could also write the same query using DECODE -
SELECT FLAGS,
DECODE(FLAGS,
0 , '10000000',
1 , '01000000',
2 , '00100000',
3 , '00010000',
4 , '00001000',
5 , '00000100',
6 , '00000010',
7 , '00000001',
'00000000')
AS TEST-W
FROM V_TEST
/

how to paste numbers with thousand separator from excel to sqldeveloper table editor

There's data exported to excel:
MS_ID STARTDATETIME
3 704 285 09.11.2014 11:29:25
3 704 285 09.11.2014 11:29:25
I want to paste it back to Oracle using sqldeveloper table data editor.
For date column I do
alter session set NLS_DATE_FORMAT = 'mm.dd.yyyy HH24:mi:ss';
But I'm also getting:
INSERT INTO "PPB"."CDRGPRS" (MS_ID, STARTDATETIME) VALUES ('3 704 285', TO_DATE('09.11.2014 11:29:25', 'mm.dd.yyyy HH24:mi:ss'))
ORA-01722: invalid number
ORA-06512: at line 1
The SQL above is generated by sqldeveloper.
There's no NLS_NUMBER_FORMAT. How to make Oracle format and parse numbers with spaces as thousand separator?
TO_NUMBER() allows you to specify whatever separators you want using nls_numeric_characters.
SQL> select to_number( '100 123,23'
2 , '999G999G999D99'
3 , 'nls_numeric_characters = '', '''
4 ) as no
5 from dual;
NO
----------
100123.23
G is the number format model corresponding to the separator, which is the second option of your nls_numeric_characters.
If you want to convert it into a number improperly you can REPLACE() the spaces:
SQL> select to_number(replace( '100 12323', ' ')) as no
2 from dual;
NO
--------
10012323
Wrote this macro and put it to Personal.xlsb
Sub nospace()
Application.FindFormat.NumberFormat = "#,##0"
Application.ReplaceFormat.NumberFormat = "0"
Cells.Replace What:="", Replacement:="", _
SearchFormat:=True, ReplaceFormat:=True
End Sub

Displaying the contents according to their heading type

I am new to SQL. My doubt is how to display the output inside the procedure like this.
I have various databases and its type and the current size all in one table 'predict_storage'
I want to display the output as:
UAT:
TMAP: 100G
TCIG: 200G
QA:
QMAP: 100G
QCIG: 200G
DR:
DRMAP: 100G
DRCIG: 200G
Where UAT is the database type and TMAP, TCIG are databases and the current size is 100g. So I want the output to like this categorized based upon the database type.
Here is the code:
CREATE OR REPLACE PROCEDURE test11 IS
db_original_name_var varchar2(30);
db_type varchar2(20);
db_name varchar2(40);
Used_space_var NUMBER;
MAX_Used_space_monthvar NUMBER;
RATE NUMBER;
avg_space_future_min NUMBER;
avg_space_future_max NUMBER;
avgsp NUMBER;
avg_space_3m NUMBER;
avg_space_6m Number;
avg_space_yr NUMBER;
CURSOR db_list_cur is
select original_db_name,database_type
from database_list;
Begin
open db_list_cur;
LOOP
fetch db_list_cur
into db_original_name_var,db_type;
EXIT WHEN db_list_cur%NOTFOUND;
select substr(avg(ave_used_space),0,6) INTO Used_space_var from month_space where db_original_name_var=database_name;
select substr(max(ave_used_space),0,6) INTO MAX_Used_space_monthvar from month_space where db_original_name_var=database_name;
--rate calc
RATE := (MAX_Used_space_monthvar-Used_space_var)/Used_space_var;
avg_space_future_max:=(Used_space_var)+(Used_space_var* RATE);
avg_space_future_min:=(Used_space_var)-(Used_space_var* RATE);
avgsp := (avg_space_future_max + avg_space_future_min)/2;
avg_space_3m :=(avgsp)+(avgsp* rate* 3);
avg_space_6m :=(avgsp)+(avgsp* rate* 6);
avg_space_yr :=(avgsp)+(avgsp* rate* 12);
insert into Predict_report
(
database_name ,
Grwoth_rate ,
Current_AVERAGE,
Space_3mn,
SPACE_6mn,
Space_yr,
database_type
)
values
(
db_original_name_var,
RATE,
round(avgsp,3),
round(avg_space_3m,3),
round(avg_space_6m,3),
round(avg_space_yr,3),
db_type
);
commit;
loop
dbms_output.put_line(db_type||':');
select database_name into db_name from predict_report where database_type=db_type;
dbms_output.put_line(db_name);
END LOOP;
END LOOP;
close db_list_cur;
END;
/
The tables I have are
1) Storage Info
DB_ID NUMBER(38)
DATABASE_NAME VARCHAR2(50)
DATABASE_SIZE NUMBER
TIME_STAMP DATE
FREE_SPACE VARCHAR2(50)
DATA_LINK_NAME VARCHAR2(50)
CUSTOMER_SPACE NUMBER
DATABASE_TYPE VARCHAR2(30)
USED_SPACE NUMBER
2) Database_list
DB_ID NUMBER
DB_NAME VARCHAR2(50)
DATALINK_NAME VARCHAR2(50)
DATABASE_TYPE VARCHAR2(20)
ORIGINAL_DB_NAME VARCHAR2(30)
3)Predict Report
DATABASE_NAME NOT NULL VARCHAR2(50)
GRWOTH_RATE NUMBER
CURRENT_AVERAGE NUMBER
SPACE_3MN NUMBER
SPACE_6MN NUMBER
SPACE_YR NUMBER
DATABASE_TYPE VARCHAR2(20)
4) Month_space
DATABASE_NAME NOT NULL VARCHAR2(50)
GRWOTH_RATE NUMBER
CURRENT_AVERAGE NUMBER
SPACE_3MN NUMBER
SPACE_6MN NUMBER
SPACE_YR NUMBER
DATABASE_TYPE VARCHAR2(20)
-----The sql file is
set echo off numf 999G999G999G999 lin 32000 trims on pages 50000 head on feed off markup html off
alter session set nls_numeric_characters='.''' nls_date_format='Day DD. Month, YYYY';
spool /tmp/report.html
--prompt To: jialin.zhu#synchronoss.com
prompt TO: varun.jain#synchronoss.com
prompt cc: varun.jain#synchronoss.com
prompt From: varun.jain#synchronoss.com
prompt Subject: Daily space report
prompt Content-type: text/html
prompt MIME-Version: 1.0
set markup html on entmap off table 'BORDER="2" BGCOLOR="white" FONTCOLOR="black"'
prompt <i>Good morning, </i>
prompt <i>Here is the Space report as on &_DATE</i>
prompt <i>Kind Regards, </i>
------------------------------------------------------------------------------------------------------------------------------------------------
prompt <br/><h3>Database Space Report</h3>
set serveroutput on
CLEAR COLUMNS
Set HEADING ON
Set COLSEP ,
SET PAGESIZE 20000
SET timing off feedback off verify off echo off
prompt <br/><h3> Environment Space Summary</h3>
column database_type heading 'Database Type'
column Sum(current_average) format 9999 HEADING 'Total Space in GB'
select database_type,Sum(current_average) from predict_report group by database_type;
--------------------------------------------------------------------------------------------------------------------------------------------------
prompt <br/><h3> DR database Summary</h3>
COLUMN ('THETOTALSPACEINDRDATABASES:'||SUM(CURRENT_AVERAGE)||''||'GB') format 9999 heading 'total Space in DR database'
select ('The total space in DR databases :' ||Sum(current_average)||' '||'GB') from predict_report where DATABASE_TYPE not in ('UAT','QA');
------------------------------------------------------------------------------------------------------------------------------------------
prompt <br/><h3> databases Summary</h3>
column database_name format a30 heading ' DATABASE NAME'
column round(GRWOTH_RATE*100,0)||'%' heading 'GROWTH RATE'
COLUMN CURRENT_AVERAGE FORMAT 9999 HEADING 'TODAYS SPACE in GB'
COLUMN SPACE_3MN FORMAT 9999 HEADING 'SPACE AFT 3 MONTHS in GB'
COLUMN SPACE_6MN FORMAT 9999 HEADING 'SPACE AFT 6 MONTHS in GB'
COLUMN SPACE_YR FORMAT 9999 HEADING 'SPACE AFT A YEAR in GB'
truncate table Predict_report;
set serveroutput on
exec report;
SELECT database_name,round(GRWOTH_RATE*100,0)||'%',current_average,space_3mn,space_6mn,space_yr FROM Predict_report;
---------------------------------------------------------------------------------
prompt <br/><h3>Database Space Summary</h3>
column sum(current_average) FORMAT 9999 heading 'Total Space in GB'
column sum(space_3mn) FORMAT 9999 heading 'Total Space in 3 months in GB'
column sum(space_6mn) FORMAT 9999 heading 'Total Space in 6 months in GB'
column sum(space_yr) FORMAT 9999 heading 'Total Space in 1 year in GB'
select sum(current_average),sum(space_3mn),sum(space_6mn),sum(space_yr) from Predict_report ;
spool off;
---------to send the mail----------------
host /usr/sbin/sendmail -t </tmp/report.html
and the output in the email received is like (It is in table form in the EMAIL)
DATABASE NAME GROWTH RATE TODAYS SPACE in GB SPACE AFT 3 MONTHS in GB SPACE AFT 6 MONTHS in GB SPACE AFT A YEAR in GB
CLNK 0% 199 200 200 202
CCIG 0% 562 563 563 565
DTXN 5% 330 377 424 518
DCIG 0% 414 416 418 422
QMAP 0% 16 16 17 17
QLNP 0% 44 44 44 44
QHTS 1% 32 32 33 34
QFKP 1% 37 38 39 41
QSAG 0% 168 169 170 172
CSAG 0% 812 815 818 824
LTATG 0% 25 25 25 25
QCIG 0% 208 209 209 211
TLNP 0% 341 341 341 341
TMAP 0% 60 61 61 62
TSAG 0% 223 226 228
The extra loop you have now for output never terminates, so executing the procedure will appear to hang, at least until you run out of dbms_output buffer space; and doesn't retrieve the current value. And it's in the wrong place, as it will show data for all databases of that type each time round the loop, so you'll get duplicates (at best).
Although it doesn't seem to be a sensible way to do it, not least because you're assuming the procedure will always be run by something that can show the output and has the output enabled, you could do something like this, after close db_list_cur:
db_type := null;
for r in (select database_type, database_name, current_average
from predict_report
order by database_type, database_name)
loop
if db_type is null or db_type != r.database_type then
dbms_output.put_line(r.database_type || ':');
db_type := r.database_type;
end if;
dbms_output.put_line(r.database_name ||' '||
to_char(nvl(r.current_average, 0), '999990.00') ||'G');
end loop;
... or within the main loop based on the data you're inserting, rather than querying the table at the end (since they should match); but this will work whether you use your loops or a much simpler insert into predict_report select ....
You don't need to use cursors to calculate the statistics or populate the table; you don't need PL/SQL at all really. You can just join the two tables together and work out the numbers from the returned values. Something like this would do it:
select database_name,
rate as grwoth_rate,
avg_used_space as current_average,
avg_used_space + (avg_used_space * rate * 3) as space3mn,
avg_used_space + (avg_used_space * rate * 6) as space6mn,
avg_used_space + (avg_used_space * rate * 12) as spaceyr,
database_type
from (
select database_name,
database_type,
avg_used_space,
max_used_space,
(max_used_space - avg_used_space)/avg_used_space as rate
from (
select dl.original_db_name as database_name,
dl.database_type,
trunc(avg(ave_used_space), 2) as avg_used_space,
trunc(max(ave_used_space), 2) as max_used_space
from database_list dl
join month_space ms on ms.database_name = dl.original_db_name
group by dl.original_db_name,
dl.database_type
)
);
You can use that to insert into the new table, though not sure you even need that really, if you're only producing output from within the procedure - not clear if that's the case, though you don't seem to want to show all the statistics you're calculating.
I'm also not sure your statistics make much sense though. You seem to be calculating a growth rate over an arbitrary number of months, and then predicting future sizes by applying that rate to the average over the life of the database rather than it's current size, which you don't seem to be collecting - you have the current average, which isn't the same thing.

Resources