PL/SQL multiply Sql statements with if and then - sql

Im trying to write a PL/SQL Code which should ask if something is there count it and
if tmp > 0
so if the object = 'VALID' then execute the rest of the sql statements if not skip everything in the Then case. When I Execute my code it dont execute the code.
DECLARE
tmp INT;
BEGIN
SELECT COUNT(*)
INTO tmp
FROM CDB_registry
WHERE Status = 'VALID';
IF tmp > 0 THEN
Col c.Status Format A6
Col Containers Format A20
Select c.Status, v.name as Containers
From Cdb_Ols_Status c, V\$Containers v
Where c.con_id = v.con_id
And c.Name = 'OLS_DIRECTORY_STATUS'
Order By v.Con_ID;
prompt ****************************************************************************************
DECLARE
tmp INT;
BEGIN
SELECT COUNT(*)
INTO tmp
FROM Cdb_Ols_Status
WHERE Status = 'TRUE'
AND name = 'OLS_DIRECTORY_STATUS';
IF tmp > 0 THEN
DBMS_Output.put_line('The Output is true');
ELSE
DBMS_Output.put_line('The Output is wrong');
END IF;
END;
/
ELSE
DBMS_Output.put_line('There is no value');
END IF;
END;
/

Here's one options; I presumed some things (read comments within code) & agree with #gsalem's comment.
You commented that
cdb_ols_status table does not exist when tmp < 0
It means that you'll have to use dynamic SQL (execute immediate) because - if table doesn't exist, code won't even compile. I guess that's why you said that code I previously posted won't work.
DECLARE
tmp INT;
BEGIN
SELECT COUNT (*)
INTO tmp
FROM cdb_registry
WHERE status = 'VALID';
IF tmp > 0
THEN
-- If this SELECT return only 1 row, you could declare local variables to fetch
-- those values INTO them.
-- Otherwise, it depends on what you want to do with them; if you'd just want to
-- DISPLAY their values (which is what your SQL*Plus COL ... FORMAT commands suggest),
-- then see whether a LOOP helps.
FOR cur_r IN ( SELECT c.status, v.name AS containers
FROM cdb_ols_status c, v$containers v
WHERE c.con_id = v.con_id
AND c.name = 'OLS_DIRECTORY_STATUS'
ORDER BY v.con_id)
LOOP
DBMS_OUTPUT.put_line (cur_r.status || ' - ' || cur_r.containers);
END LOOP;
-- The rest of the script should be ran because TMP value is greater than 0.
EXECUTE IMMEDIATE 'select count(*) from cdb_ols_status '
|| q'[where status = 'TRUE' and name = 'OLS_DIRECTORY-STATUS']'
INTO tmp;
IF tmp > 0
THEN
DBMS_OUTPUT.put_line ('The Output is true');
ELSE
DBMS_OUTPUT.put_line ('The Output is wrong');
END IF;
ELSE
DBMS_OUTPUT.put_line ('There is no value');
END IF;
END;
/

Related

code goes into infinite loop when updating table's column

expert.
I'm new to PLSQL programming.
More than 200 tables have 'EXAMPLE' columns.
I want to update the column 'EXAMPLE' with 'YES'.
The purpose of the following code is to update the 'EXAMPLE' column for every 10000 records.
But I think the following code enters an infinite loop.
Where am I making a mistake?.
how can i fix this?
declare
v_match_count integer;
table_name varchar2(30);
begin
v_match_count:=0;
for tablolar in
(
SELECT table_name
FROM user_tab_columns,user_objects
WHERE
user_tab_columns.table_name=user_objects.object_name and user_objects.object_type not in ('VIEW') AND
column_name IN ( 'FILE_NO', 'PROT_NO' )
GROUP BY table_name
HAVING Count(*) > 1
) loop
begin
-- v_match_count:=v_match_count+1;
-- dbms_output.put_line(tablolar.table_name||' = '||v_match_count);
WHILE TRUE LOOP
IF tablolar.table_name||'.EXAMPLE' IS NOT NULL THEN --the line I changed in the code.
--dbms_output.put_line(tablolar.table_name||' = '||v_match_count||' girdi.');
execute immediate 'UPDATE HASTANE.'||tablolar.table_name|| ' SET EXAMPLE=''YES'' WHERE '||tablolar.table_name||'.EXAMPLE IS NULL AND ROWNUM<10000' ;
COMMIT;
END IF;
IF tablolar.table_name||'.EXAMPLE' IS NULL THEN --the line I changed in the code.
EXIT;
end if;
-- v_match_count:=v_match_count+1;
END LOOP;
v_match_count:=v_match_count+1;
dbms_output.put_line(tablolar.table_name||' = '||v_match_count);
end;
end loop;
end;
In the test for 'EXAMPLE' the value will never be NULL so you will never exit the loop. Try checking for the existence of the column 'EXAMPLE' in your query, then you can just do the update for each table and the loop will exit when all the records are read.
declare
v_match_count integer;
v_record_count INTEGER;
table_name varchar2(30);
begin
v_match_count:=0;
v_record_count := 0;
for tablolar in
(
SELECT table_name
FROM user_tab_columns,user_objects
WHERE
user_tab_columns.table_name=user_objects.object_name and user_objects.object_type not in ('VIEW') AND
column_name IN ( 'FILE_NO', 'PROT_NO' )
AND EXISTS (SELECT * FROM user_tab_columns x
WHERE x.table_name = user_tab_columns.table_name AND x.column_name = 'EXAMPLE')
GROUP BY table_name
HAVING Count(*) > 1
) loop
BEGIN
--- Get the record cound
execute immediate 'SELECT COUNT(*) FROM ' || 'HASTANE.' ||tablolar.table_name || ' WHERE '||tablolar.table_name||'.EXAMPLE IS NULL'
INTO v_record_count;
--- Do we have anything to update?
IF NVL(v_record_count,0) > 0 THEN
--- Update all the records that are NULL
FOR v_match_count IN 1..v_record_count LOOP
execute immediate 'UPDATE HASTANE.'||tablolar.table_name|| ' SET EXAMPLE=''YES'' WHERE '||tablolar.table_name||'.EXAMPLE IS NULL' ;
--- Check if it's time to COMMIT (every 10000 records)
IF MOD(v_match_count, 10000) = 0 THEN
COMMIT;
END IF;
END LOOP;
--- COMMIT remaining records since last COMMIT
COMMIT;
END IF;
end;
end loop;
end;

Create a NZPLSQL to find min(date) and a max(date) for a group of fields that serve as a key

I would like to know how I can execute a select from a procedure stored in Netezza.
I currently have the procedure under construction and I read that the raise notice serves to print on screen, however I do not know how (or if you can print a table in sql) just as if you were doing a query in traditional sql.
What the query is trying to do is that for a record,
COUNT (*) AS N_REPETITIONS,
CDS.CASO_PACIENTE_FK,
CDS.RESIVALITY_SPECIALITY_COD,
CDS.CASO_NIVEL_ATENCION_TIPO,
CDS.CASO_TIPO_PRESTACION_FK,
CDS.DIVING_DATE_DAY,
CDS.SALID_DATE_DAY
and that it gives me MIN(CDS.DERIVACION_FECHA_HORA) and MAX(CDS.SALIDA_FECHA_HORA) based on a key that are the fields.
If I have dates between MIN(CDS.DERIVACION_FECHA_HORA) and MAX(CDS.SALIDA_FECHA_HORA) for a CDS.CASO_PACIENTE_FK, i want that register added to number of register for each CDS.CASO_PACIENTE_FK
Here the code:
CREATE OR REPLACE PROCEDURE SP_ANALISIS_DUPLICADOS()
RETURNS REFTABLE(NETEZZA_PROD_LISTA_ESPERA_NOGES.LLAVE_CASOS_DUPLICADOS)
EXECUTE AS OWNER
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
num_args INTEGER;
I am just starting to work with Netezza.
Thank you in advance to whom you answer.
This is the code:
CREATE OR REPLACE PROCEDURE SP_ANALISIS_DUPLICADOS()
RETURNS REFTABLE(NETEZZA_PROD_LISTA_ESPERA_NOGES.LLAVE_CASOS_DUPLICADOS)
EXECUTE AS OWNER
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
num_args INTEGER;
typ oid;
idx INTEGER;
N_REPETICIONES NUMERIC(19);
CASO_PACIENTE_FK NUMERIC(19);
DERIVACION_ESPECIALIDAD_COD VARCHAR(255);
CASO_NIVEL_ATENCION_TIPO NUMERIC(19);
CASO_TIPO_PRESTACION_FK NUMERIC(19);
DERIVACION_FECHA_HORA TIMESTAMP;
SALIDA_FECHA_HORA TIMESTAMP;
--CURSOR FECHA_HORA_DERIVACION_MIN;
--CURSOR FECHA_HORA_SALIDA_MAX;
CURSOR CASO_PACIENTE_FK;
CURSOR DERIVACION_ESPECIALIDAD_COD;
CURSOR CASO_NIVEL_ATENCION_TIPO;
CURSOR CASO_TIPO_PRESTACION_FK;
--L_CONDITIONS VARCHAR(1000);
--Duplicados CNE MIN
BEGIN
num_args := PROC_ARGUMENT_TYPES.count;
FOR CASO_PACIENTE_FK IN
num_args := CASO_PACIENTE_FK.count;
--RAISE NOTICE ’Number of arguments: %’, num_args;
for i IN 0
CASO_PACIENTE_FK.count - 1 LOOP
typ := CASO_PACIENTE_FK(i);
idx := i+1;
FOR DERIVACION_ESPECIALIDAD_COD IN
num_args := DERIVACION_ESPECIALIDAD_COD.count;
for j IN 0
DERIVACION_ESPECIALIDAD_COD.count - 1 LOOP
typ := DERIVACION_ESPECIALIDAD_COD(j);
idx := j+1;
FOR CASO_NIVEL_ATENCION_TIPO IN
num_args := CASO_PACIENTE_FK.count;
--RAISE NOTICE ’Number of arguments: %’, num_args;
for k IN 0
CASO_NIVEL_ATENCION_TIPO.count - 1 LOOP
typ := CASO_NIVEL_ATENCION_TIPO(k);
idx := k+1;
FOR CASO_TIPO_PRESTACION_FK IN
num_args := CASO_TIPO_PRESTACION_FK.count;
--RAISE NOTICE ’Number of arguments: %’, num_args;
for a IN 0
CASO_TIPO_PRESTACION_FK.count - 1 LOOP
typ := CASO_TIPO_PRESTACION_FK(a);
idx := a+1;
--RAISE NOTICE ’argument $% is type % and has the value ’’%’’’,
--idx, typ, $idx;
F FLAG = 1 THEN
IF Fecha_Entrada = SELECT COUNT(*) AS N_REPETICIONES, MIN(CDS.DERIVACION_FECHA_HORA) AS FECHA_HORA_DERIVACION_MIN
FROM NETEZZA_PROD_SIGTE_DIARIO.SIG_CASO_DERIVACION_SALIDA CDS
WHERE (CDS.CASO_TIPO_PRESTACION_FK=1
OR CDS.CASO_TIPO_PRESTACION_FK=2)
--AND CDS.DERIVACION_FECHA_HORA >= CDS.DERIVACION_FECHA_HORA
GROUP BY CDS.CASO_PACIENTE_FK, CDS.DERIVACION_ESPECIALIDAD_COD, /*CDS.DERIVACION_ESTABLECIMIENTO_COD*/
CDS.CASO_NIVEL_ATENCION_TIPO, /*CDS.DERIVACION_FECHA_HORA,*/ CDS.CASO_TIPO_PRESTACION_FK, CDS.DERIVACION_FECHA_HORA,
CDS.SALIDA_FECHA_HORA
HAVING COUNT(*)>1
ORDER BY N_REPETICIONES, CDS.CASO_PACIENTE_FK, CDS.DERIVACION_FECHA_HORA, CDS.SALIDA_FECHA_HORA DESC
THEN Fecha_Entrada = 'TRUE';
ELSE FLAG = 0 THEN
Fecha_Entrada = 'FALSE'
F FLAG = 1 THEN
IF Fecha_Salida = SELECT COUNT(*) AS N_REPETICIONES, MAX(CDS.SALIDA_FECHA_HORA) AS FECHA_HORA_DERIVACION_MIN
FROM NETEZZA_PROD_SIGTE_DIARIO.SIG_CASO_DERIVACION_SALIDA CDS
WHERE (CDS.CASO_TIPO_PRESTACION_FK=1
OR CDS.CASO_TIPO_PRESTACION_FK=2)
--AND CDS.DERIVACION_FECHA_HORA >= CDS.DERIVACION_FECHA_HORA
GROUP BY CDS.CASO_PACIENTE_FK, CDS.DERIVACION_ESPECIALIDAD_COD, /*CDS.DERIVACION_ESTABLECIMIENTO_COD*/
CDS.CASO_NIVEL_ATENCION_TIPO, /*CDS.DERIVACION_FECHA_HORA,*/ CDS.CASO_TIPO_PRESTACION_FK, CDS.DERIVACION_FECHA_HORA,
CDS.SALIDA_FECHA_HORA
HAVING COUNT(*)>1
ORDER BY N_REPETICIONES, CDS.CASO_PACIENTE_FK, CDS.DERIVACION_FECHA_HORA, CDS.SALIDA_FECHA_HORA DESC
THEN Fecha_Salida = 'TRUE';
ELSE FLAG = 0 THEN
Fecha_Salida = 'FALSE'
END LOOP;
END LOOP;
END LOOP;
END LOOP;
RETURN REFTABLE;
RAISE NOTICE 'Hello, %', MYNAME; --Right Here must go the Println (Select field1, field2, etc.....)
--END;
--COMMIT;
END;
END_PROC;

PL/SQL counting by looping?

I have an assignment asking me to rewrite this PL/SQL code I wrote for a previous assignment:
DECLARE
-- Variables used to count a, b, c, d, and f grades:
na integer := 0;
nb integer := 0;
nc integer := 0;
nd integer := 0;
nf integer := 0;
BEGIN
select count(*) into na
from gradeReport1
where grade = 'A';
select count(*) into nb
from gradeReport1
where grade = 'B';
select count(*) into nc
from gradeReport1
where grade = 'C';
select count(*) into nd
from gradeReport1
where grade = 'D';
select count(*) into nf
from gradeReport1
where grade = 'F';
if na > 0 then
DBMS_OUTPUT.PUT_LINE('There are total ' || na || ' A''s');
else
DBMS_OUTPUT.PUT_LINE('There are no As');
end if;
if nb > 0 then
DBMS_OUTPUT.PUT_LINE('There are total ' || nb || ' B''s');
else
DBMS_OUTPUT.PUT_LINE('There are no Bs');
end if;
if nc > 0 then
DBMS_OUTPUT.PUT_LINE('There are total ' || nc || ' C''s');
else
DBMS_OUTPUT.PUT_LINE('There are no Cs');
end if;
if nd > 0 then
DBMS_OUTPUT.PUT_LINE('There are total ' || nd || ' D''s');
else
DBMS_OUTPUT.PUT_LINE('There are no Ds');
end if;
if nf > 0 then
DBMS_OUTPUT.PUT_LINE('There are total ' || nf || ' F''s');
else
DBMS_OUTPUT.PUT_LINE('There are no Fs');
end if;
END;
All it does is search a table I made called gradeReport that stores studentID's and associates them with a grade. The PL/SQL counts all instances of a grade A through F. The question wants me to rewrite this solution using looping and VARRAYS. Could anyone give me a hint to help get the ball rolling for me? I've only been using PL/SQL for a few weeks and don't have much more than a basic understanding of the syntax so I'm completely lost and have no idea where to start.
Not looking for any answers here, just some ideas.
Thank You
How about starting with the doc. http://docs.oracle.com/database/122/LNPLS/plsql-control-statements.htm#LNPLS004
DECLARE
-- Need to ensure the array size will hold all the grades
TYPE grade_tab IS VARRAY(200) OF gradeReport1.grade%TYPE;
-- variable used to store the grades:
t_grades grade_tab;
-- Variables used to count a, b, c, d, and f grades:
na INTEGER;
nb INTEGER;
nc INTEGER;
nd INTEGER;
nf INTEGER;
BEGIN
-- Store the grades in an array:
SELECT grade
BULK COLLECT INTO t_grades
FROM gradeReport1
WHERE grade IN ( 'A', 'B', 'C', 'D', 'F' );
-- Loop through the grades and count how many of each:
FOR i IN 1 .. t_grades.COUNT LOOP
IF t_grades(i) = 'A' THEN na := na + 1;
ELSIF t_grades(i) = 'B' THEN nb := nb + 1;
ELSIF t_grades(i) = 'C' THEN nc := nc + 1;
ELSIF t_grades(i) = 'D' THEN nd := nd + 1;
ELSIF t_grades(i) = 'F' THEN nf := nf + 1;
END IF;
END LOOP;
-- Output grade counts
END;
/
However, a much simpler solution would be to do the counting in a single SQL query (although this doesn't meet the assessment's requirements of using a VARRAY):
DECLARE
-- Variables used to count a, b, c, d, and f grades:
na INTEGER;
nb INTEGER;
nc INTEGER;
nd INTEGER;
nf INTEGER;
BEGIN
SELECT COUNT( CASE grade WHEN 'A' THEN 1 END ),
COUNT( CASE grade WHEN 'B' THEN 1 END ),
COUNT( CASE grade WHEN 'C' THEN 1 END ),
COUNT( CASE grade WHEN 'D' THEN 1 END ),
COUNT( CASE grade WHEN 'F' THEN 1 END )
INTO na,
nb,
nc,
nd,
nf
FROM gradeReport1;
-- Output grade counts...
END;
/
Edit: as the requirement is specifically for varrays, see replies by AmmoQ and MTO. As they both point out, though, you'd be unlikely to need arrays for this type of task in practice, and even if you did, you would use a nested table or an associative array and not a varray.
You'll want a Cursor FOR loop, along the lines of
for r in (
select grade from gradereport1
)
loop
...
end loop;
In real code you'd probably make that a group by query and have SQL do the counting for you.
Then just conditionally increment the counters in the loop depending in the value of r.grade.
You can rationalise all of the if statements for reporting the totals by writing a procedure that takes a grade and a total, as the logic is the same for all of them.
procedure showgrade
( p_grade gradereport1.grade%type
, p_count integer )
is
begin
...
end showgrade;
I'll leave the details as an exercise.
Just for fun, here is another approach, using arrays and looping (but not a varray - they really are a bit useless):
declare
type gradereport_tt is table of pls_integer index by gradereport.grade%type;
gradecounts gradereport_tt;
g gradereport.grade%type;
begin
-- Initialise counts:
gradecounts('A') := 0;
gradecounts('B') := 0;
gradecounts('C') := 0;
gradecounts('D') := 0;
gradecounts('E') := 0;
gradecounts('F') := 0;
-- Count grades:
for r in (
select grade from gradereport
)
loop
gradecounts(r.grade) := gradecounts(r.grade) +1;
end loop;
-- Report counts:
g := gradecounts.first;
while g is not null loop
dbms_output.put_line(g || ': ' || gradecounts(g));
g := gradecounts.next(g);
end loop;
end;
btw there is no need to put brackets after if as in some other languages, unless the condition contains a mixture of and and or conditions that need separating.
There is also no need to write anything in uppercase. It's quite common and Steven Feuerstein does it all the time, but they had this debate in the HTML/CSS world and settled on lowercase for readability. And if you are going to have an uppercase rule, at least use it consistently. Your code example has end if; but END; not to mention Select (which I've fixed). Some people seem to be able to read code like this without it driving them nuts, but I'm afraid I'm not one of them.
set SERVEROUTPUT ON
declare
type number_array is VARRAY(5) OF integer;
total integer :=0;
i number :=1;
begin
numbers :=number_array(14,45,67,89,21);
arr_size := numbers.count;
FOR i in 1..arr_size loop
total :=total+numbers(i);
end loop;
dbms_output.put_line('total-' || total);
end;
here I want to count the number_array elements, but I can't get the correct answers. what is the problem with this?
A solution using VARRAYs could look like that:
DECLARE
type chararray IS VARRAY(6) OF CHAR(1);
type numarray IS VARRAY(6) OF INTEGER;
grades chararray;
cnt numarray;
BEGIN
select grade, count(*)
bulk collect into grades, cnt
from gradeReport1
group by grade
order by grade;
for i in 1..grades.count loop
DBMS_OUTPUT.PUT_LINE('There are total ' || cnt(i) || ' ' ||grades(i)||'s');
end loop;
END;
/
But honestly, it's pointless to use VARRAYs in that case. Just use a cursor loop:
BEGIN
for c in ( select grade, count(*) cnt
from gradeReport1
group by grade
order by grade ) loop
DBMS_OUTPUT.PUT_LINE('There are total ' || c.cnt || ' ' ||c.grade||'s');
end loop;
END;
/
Finding the missing marks (those with a count of 0) is a bit more difficult, though.

Dynamic SQL - ORACLE

I have the following procedure, which does not compile correctly, because it refers to non existing objects (table does not exist)
Here is only a section of the code (i used generic names for tables and columns):
DECLARE
C INTEGER := 0;
BEGIN
SELECT COUNT(1) INTO C FROM USER_TABLES WHERE TABLE_NAME = 'MY_TABLE';
IF C > 0 THEN
DECLARE
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM MY_TABLE WHERE ACTIVE = 1;
BEGIN
FOR prec IN c_maps LOOP
some code...;
END LOOP;
EXECUTE IMMEDIATE 'some code..';
END;
END IF;
END;
/
I don't know how to write this statement dynamically, since the table "MY_TABLE" does not exist:
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM MY_TABLE WHERE ACTIVE =1;
I also tried to write it like:
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM (Select 'MY_TABLE' from dual) WHERE ACTIVE = 1;
However, than it refers to the column "ACTIVE" which also does not exist at compile time...It is possible to write the whole procedure inside "execute immediate" - block? I have tried different variants, however without success
You may need to open the cursor in a different way, so that the non existing table is only referred in dynamic SQL; for example:
declare
c integer := 0;
curs sys_refcursor;
v1 number;
v2 number;
begin
select count(1)
into c
from user_tables
where table_name = 'MY_TABLE';
if c > 0
then
open curs for 'select column_name1, column_name2 from my_table where active = 1';
loop
fetch curs into v1, v2;
exit when curs%NOTFOUND;
dbms_output.put_line(v1 || ' - ' || v2);
end loop;
else
dbms_output.put_line('The table does not exist');
end if;
end;
/

Inserting values in a table in FOR loop statement in pl sql

I want to run the loop from i=1 to i=12 and insert each value of M_1,M_2 and so on till M_12 in M_Maintenance table.I have tried but it is not working.Please help me out.
The code which I have tried is:-
BEGIN
FOR i IN 1 .. 12
LOOP
DECLARE
M_i NUMBER;
MONTH_i_COUNT NUMBER;
M_i_Maintenance NUMBER;
MONTH_i_SUM NUMBER;
BEGIN
SELECT ROUND (
( ( (SELECT SUM (MONTH_i_COUNT)
FROM XXBAXY.XXBAXY_BREAKDOWN_TAB
WHERE LOSS_CAT = 'Maintenance Related Losses')
+ (SELECT MONTH_i_COUNT
FROM XXBAXY.XXBAXY_BREAKDOWN_TAB
WHERE LOSS_CAT = 'Maintenance Related Losses'
AND description = 'SAFTY SHEET PRO. ')
- (SELECT MONTH_i_COUNT
FROM XXBAXY.XXBAXY_BREAKDOWN_TAB
WHERE LOSS_CAT = 'Maintenance Related Losses'
AND description =
'PREVENTIVE MAINTENANCE '))
/ (SELECT MONTH_i_SUM FROM XXBAXY.XXBAXY_ATTR_SUM_TAB))
* 100,
3
)
INTO M_i
FROM DUAL;
EXCEPTION
WHEN ZERO_DIVIDE
THEN
M_i := 0;
END;
INSERT INTO M_Maintenance (M_i_Maintenance)
VALUES (M_i);
END LOOP;
END;
Let's first make your query easier to read and better perfoming, if I don't mistake it should be this one:
SELECT ROUND(
SUM(CASE description
WHEN 'SAFTY SHEET PRO. ' THEN 2*MONTH_i_COUNT
WHEN 'PREVENTIVE MAINTENANCE ' THEN -MONTH_i_COUNT
ELSE MONTH_i_COUNT
END) / MIN(MONTH_i_SUM) *100, 3)
FROM XXBAXY.XXBAXY_BREAKDOWN_TAB
CROSS JOIN XXBAXY.XXBAXY_ATTR_SUM_TAB
WHERE LOSS_CAT = 'Maintenance Related Losses'
In this case your PL/SQL code can look like this (skipping the exception handler):
DECLARE
m NUMBER;
sqlstr VARCHAR2(1000);
BEGIN
FOR i IN 1..12 LOOP
sqlstr :=
'SELECT ROUND( '
' SUM(CASE description '
' WHEN ''SAFTY SHEET PRO. '' THEN 2*MONTH_'||i||'_COUNT '
' WHEN ''PREVENTIVE MAINTENANCE '' THEN -MONTH_'||i||'_COUNT '
' ELSE MONTH_'||i||'_COUNT '
' END) / MIN(MONTH_'||i||'_SUM) *100, 3) '
'FROM XXBAXY.XXBAXY_BREAKDOWN_TAB '
' CROSS JOIN XXBAXY.XXBAXY_ATTR_SUM_TAB '
'WHERE LOSS_CAT = ''Maintenance Related Losses''';
EXECUTE IMMEDIATE sqlstr INTO m;
EXECUTE IMMEDIATE 'INSERT INTO M_Maintenance (M_'||i||'_Maintenance) VALUES (:m)' USING m;
END LOOP;
END;
Did you mean, that your table XXBAXY.XXBAXY_BREAKDOWN_TAB contains fields MONTH_1_COUNT, MONTH_2_COUNT, MONTH_3_COUNT and so on? If so, first step should be is redesigning this table because of violation of 1NF. It's bad design, a way of immutable pain. If no, it's unclear why you try to use uninitialized local varables in SQL statement without any field references.
Generally it looks that you should avoid a FOR LOOP and PL/SQL and write a single insert/select statement which will produces all rows required.
Elaborating: your source has a common structure of
begin
for i in MIN..MAX loop
select something into value
from table1
where some_condition
and id = i;
insert into table2 values (value);
end loop;
end;
Almost always better way is do this as
insert into table2
select something
from table1
where some_condition
and id in (MIN..MAX);