I'm working in pl sql block to add double quote and comma for each break line currently my block is this:
DECLARE
v_fulltext varchar2(4000):= ' Total Total Total Unbalanced Unbalanced Flex Nonflex
Journal Entry Source Name Group ID Status Lines Batches Headers Batches Headers Errors Errors
---------------------------- ------------------ ------- ------- ------- ------- ---------- ---------- ---------- ----------
Manual 90005 Error 10 1 1 3 1 0 0
---------------------------- ------------------ ------- ------- ------- ------- ---------- ---------- ---------- ----------
*** TOTALS *** 1 1 1 1 1 0 0';
v_full_text_2 varchar2(4000);
v_full_text_quote varchar2(4000);
counter number := 0;
BEGIN
FOR sumaryline IN(SELECT
regexp_substr(v_fulltext, '[^('
|| CHR(13)
|| CHR(10)
|| ')]+', 1, level) sumaryline
FROM
dual
CONNECT BY
regexp_substr(v_fulltext, '[^('
|| CHR(13)
|| CHR(10)
|| ')]+', 1, level) IS NOT NULL)
LOOP
counter := counter +1;
v_full_text_quote := v_full_text_quote||'"'||sumaryline.sumaryline||'",'||chr(10);
END LOOP;
dbms_output.put_line(v_full_text_quote);
END;
The output is this:
How can I remove comma from the last line?
I have tried using:
END LOOP;
v_full_text_quote := rtrim(v_full_text_quote, ',');
But it does not work
Kind Regards.
Cesar.
That's because , isn't the last character in your string.. you have a line feed and carriage return following it. Try REGEXP_REPLACE, like this:
SELECT REGEXP_REPLACE(line,','||CHR(13)||CHR(10)||'$',CHR(13)||CHR(10))
FROM (
/* sample data */
SELECT 'line 1,'||CHR(13)||CHR(10)||
'line 2,'||CHR(13)||CHR(10)||
'line 3,'||CHR(13)||CHR(10) line
FROM dual
)
Outputs:
line 1,
line 2,
line 3
Of course, if you don't want that last CHR(13)||CHR(10) after the comma, then you could also just trim them all with nested RTRIMs:
RTRIM(RTRIM(RTRIM(v_full_text_quote,CHR(10)),CHR(13)),',')
Related
error in using pl/sql function in the blocks of pl/sql
i'm trying to replace a char with a varchar2 using a function
i want to replace 'M' char to 'Male' VARCHAR2 after extract the data from the table using cursor
this is my code
`
CREATE OR REPLACE FUNCTION change_gender(GENDER IN CHAR)
RETURN VARCHAR2(6);
IS
new_gender VARCHAR2(6);
BEGIN
IF (GENDER == 'M') THEN
new_gender := 'Male'
return new_gender;
END;
DECLARE
current_student_address STUDENTS.address%type;
CURSOR students_cursor IS
SELECT ID,NAME,GENDER
FROM STUDENTS
WHERE ADDRESS = current_student_address;
students_row students_cursor%ROWTYPE;
BEGIN
current_student_address := 'Road102';
FOR students_row IN students_cursor LOOP
DBMS_OUTPUT.PUT_LINE(students_row.id || ' ' || students_row.name || ' ' || change_gender(students_row.gender));
END LOOP;
END;
`
and this is the error I'm receiving
Error at line 2: PLS-00103: Encountered the symbol "(" when expecting one of the following:
. # % ; is default authid as cluster order using external
character deterministic parallel_enable pipelined aggregate
result_cache accessible rewrite
It's just =, not ==. Also, you're missing statement terminator (semi-colon), as well as end if. Return datatype can't have size.
As of the function's logic: what about other genders? What will function return then? It must return something (even if it were null).
Therefore, I'd suggest something like this:
Sample data:
SQL> select * from students;
ID NAME ADDRESS GENDER
---------- ------ ------- ----------
1 Little Road102 M
2 Foot Road102 F
Function:
SQL> CREATE OR REPLACE FUNCTION change_gender(GENDER IN CHAR)
2 RETURN VARCHAR2
3 IS
4 new_gender VARCHAR2(6);
5 BEGIN
6 RETURN case when gender = 'M' then 'Male'
7 when gender = 'F' then 'Female'
8 else 'Other'
9 end;
10 end;
11 /
Function created.
Testing:
SQL> set serveroutput on
SQL> DECLARE
2 current_student_address STUDENTS.address%type;
3 CURSOR students_cursor IS
4 SELECT ID,NAME,GENDER
5 FROM STUDENTS
6 WHERE ADDRESS = current_student_address;
7 students_row students_cursor%ROWTYPE;
8 BEGIN
9 current_student_address := 'Road102';
10 FOR students_row IN students_cursor LOOP
11 DBMS_OUTPUT.PUT_LINE(students_row.id || ' ' || students_row.name || ' ' ||
12 change_gender(students_row.gender));
13 END LOOP;
14 END;
15 /
1 Little Male
2 Foot Female
PL/SQL procedure successfully completed.
SQL>
I would like to replace every string are null by 'n' and every number by 0.
Is there a way to do that?
With a polymorphic table function I can select all the columns of a certain type but I can't modify the value of the columns.
Yes, it is possible with PTF also. You may modify columns in case you've set pass_through to false for the column in the describe method (to drop it) and copy it into new_columns parameter of the describe.
Below is the code:
create package pkg_nvl as
/*Package to implement PTF*/
function describe(
tab in out dbms_tf.table_t
) return dbms_tf.describe_t
;
procedure fetch_rows;
end pkg_nvl;
/
create package body pkg_nvl as
function describe(
tab in out dbms_tf.table_t
) return dbms_tf.describe_t
as
modif_cols dbms_tf.columns_new_t;
new_col_cnt pls_integer := 0;
begin
/*Mark input columns as used and as modifiable for subsequent row processing*/
for i in 1..tab.column.count loop
if tab.column(i).description.type in (
dbms_tf.type_number,
dbms_tf.type_varchar2
) then
/*Modifiable*/
tab.column(i).pass_through := FALSE;
/*Used in the PTF context*/
tab.column(i).for_read := TRUE;
/* Propagate column to the modified*/
modif_cols(new_col_cnt) := tab.column(i).description;
new_col_cnt := new_col_cnt + 1;
end if;
end loop;
/*Return the list of modified cols*/
return dbms_tf.describe_t(
new_columns => modif_cols
);
end;
procedure fetch_rows
/*Process rowset and replace nulls*/
as
rowset dbms_tf.row_set_t;
num_rows pls_integer;
in_col_vc2 dbms_tf.tab_varchar2_t;
in_col_num dbms_tf.tab_number_t;
new_col_vc2 dbms_tf.tab_varchar2_t;
new_col_num dbms_tf.tab_number_t;
begin
/*Get rows*/
dbms_tf.get_row_set(
rowset => rowset,
row_count => num_rows
);
for col_num in 1..rowset.count() loop
/*Loop through the columns*/
for rn in 1..num_rows loop
/*Calculate new values in the same row*/
/*Get column by index and nvl the value for return column*/
if rowset(col_num).description.type = dbms_tf.type_number then
dbms_tf.get_col(
columnid => col_num,
collection => in_col_num
);
new_col_num(rn) := nvl(in_col_num(rn), 0);
elsif rowset(col_num).description.type = dbms_tf.type_varchar2 then
dbms_tf.get_col(
columnid => col_num,
collection => in_col_vc2
);
new_col_vc2(rn) := nvl(in_col_vc2(rn), 'n');
end if;
end loop;
/*Put the modified column to the result*/
if rowset(col_num).description.type = dbms_tf.type_number then
dbms_tf.put_col(
columnid => col_num,
collection => new_col_num
);
elsif rowset(col_num).description.type = dbms_tf.type_varchar2 then
dbms_tf.put_col(
columnid => col_num,
collection => new_col_vc2
);
end if;
end loop;
end;
end pkg_nvl;
/
create function f_replace_nulls(tab in table)
/*Function to replace nulls using PTF*/
return table pipelined
row polymorphic using pkg_nvl;
/
with a as (
select
1 as id, 'q' as val_vc2, 1 as val_num
from dual
union all
select
2 as id, '' as val_vc2, null as val_num
from dual
union all
select
3 as id, ' ' as val_vc2, 0 as val_num
from dual
)
select
id
, a.val_num
, a.val_vc2
, n.val_num as val_num_repl
, n.val_vc2 as val_vc2_repl
from a
join f_replace_nulls(a) n
using(id)
ID | VAL_NUM | VAL_VC2 | VAL_NUM_REPL | VAL_VC2_REPL
-: | ------: | :------ | -----------: | :-----------
3 | 0 | | 0 |
2 | null | null | 0 | n
1 | 1 | q | 1 | q
db<>fiddle here
Use COALESCE or NVL and list the columns you want to apply them to:
SELECT COALESCE(col1, 'n') AS col1,
COALESCE(col2, 0) AS col2,
COALESCE(col3, 'n') AS col3
FROM table_name;
The way I understood the question, you actually want to modify table's contents. If that's so, you'll need dynamic SQL.
Here's an example; sample data first, with some numeric and character columns having NULL values:
SQL> desc test
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
NAME VARCHAR2(20)
SALARY NUMBER
SQL> select * from test order by id;
ID NAME SALARY
---------- ---------- ----------
1 Little 100
2 200
3 Foot 0
4 0
Procedure reads USER_TAB_COLUMNS, checking desired data types (you can add some more, if you want), composes the update statement and executes it:
SQL> declare
2 l_str varchar2(1000);
3 begin
4 for cur_r in (select column_name, data_type
5 from user_tab_columns
6 where table_name = 'TEST'
7 and data_type in ('CHAR', 'VARCHAR2')
8 )
9 loop
10 l_str := 'update test set ' ||
11 cur_r.column_name || ' = nvl(' || cur_r.column_name ||', ''n'')';
12 execute immediate l_str;
13 end loop;
14
15 --
16
17 for cur_r in (select column_name, data_type
18 from user_tab_columns
19 where table_name = 'TEST'
20 and data_type in ('NUMBER')
21 )
22 loop
23 l_str := 'update test set ' ||
24 cur_r.column_name || ' = nvl(' || cur_r.column_name ||', 0)';
25 execute immediate l_str;
26 end loop;
27 end;
28 /
PL/SQL procedure successfully completed.
Result:
SQL> select * from test order by id;
ID NAME SALARY
---------- ---------- ----------
1 Little 100
2 n 200
3 Foot 0
4 n 0
SQL>
You can create a table macro to intercept the columns and replace them with nvl if they're a number or varchar2:
create or replace function replace_nulls (
tab dbms_tf.table_t
)
return clob sql_macro as
stmt clob := 'select ';
begin
for i in 1..tab.column.count loop
if tab.column(i).description.type = dbms_tf.type_number
then
stmt := stmt || ' nvl ( ' ||
tab.column(i).description.name || ', 0 ) ' ||
tab.column(i).description.name || ',';
elsif tab.column(i).description.type = dbms_tf.type_varchar2
then
stmt := stmt || ' nvl ( ' ||
tab.column(i).description.name || ', ''n'' ) ' ||
tab.column(i).description.name || ',';
else
stmt := stmt || tab.column(i).description.name || ',';
end if;
end loop;
stmt := rtrim ( stmt, ',' ) || ' from tab';
return stmt;
end;
/
with a (
aa1,aa2,aa3
) as (
select 1, '2hhhh', sysdate from dual
union all
select null, null, null from dual
)
select * from replace_nulls(a);
AA1 AA2 AA3
---------- ----- -----------------
1 2hhhh 27-JUN-2022 13:17
0 n <null>
I have a SQL statement with the PIVOT operation. The maximum number of columns I will have in PIVOT is 5, but I can have less, 4, 3, 2.
How can I read these columns in my cursor and assign (fetch .. into...) to the fixed variables, without occurring the error ORA-01007.
...
sql_stmt := 'select * from
(select codcoligada,
idprd,
codcfo,
valnegociado
from tcitmorcamento
where codcoligada = ' || p_codcoligada || '
and codcotacao = ' || '''' || p_codcotacao || '''' || ')
pivot
(
sum(valnegociado) for codcfo in (' || pivot_clause || ')
)';
ret := t_tab_sesa_cotacao();
open vCursor for sql_stmt;
loop
/* If my cursor returns less than 5 columns in PIVOT the error occurs ORA-01007 */
fetch vCursor into vCodColigada, vIdProduto, vValor01, vValor02, vValor03, vValor04, vValor05;
exit when vCursor%NOTFOUND;
ret.extend;
ret(ret.count) := t_type_sesa_cotacao(vCodColigada, vIdProduto, vValor01, vValor02, vValor03, vValor04, vValor05);
end loop;
close vCursor;
...
If I return less than 5 colums, I want to fill in the remainder of the variables with a value of 0 or null.
The variables vCodColigada and vIdProduto are identified, only PIVOT columns that can vary between 1 and 5 (vValor1, vValor2, vValor3, vValor4, vValor5)
Result PIVOT SQL:
CODCOLIGADA IDPRD '000125' '002272' '002342'
----------------- ---------------- ---------------- ---------------- ----------------
1 15464 45 300 30
1 18460 35 200 20
1 57492 20 100 10
-------- End of Data --------
Example:
If the cursor returns 3 values in the PIVOT (above), the variables vValor01, vValor02, vValor03 will be filled in, and the variables vValor04, vValor05 must be 0 or null.
Example:
CODCOLIGADA IDPRD VALOR01 VALOR02 VALOR03 VALOR04 VALOR05
----------------- ---------------- ---------------- ---------------- ---------------- ---------------- ----------------
1 15464 45 300 30 0 0
1 18460 35 200 20 0 0
1 57492 20 100 10 0 0
-------- End of Data --------
As I only have 3 columns in PIVOT, and I have 5 variables, the ORA-01007 error occurs in (fetch .. into ...).
Hope this below snippet helps. What the basic understanding is we need to add excess variable as null or blank to make this work.
SET serveroutput ON;
DECLARE
lv_pivot VARCHAR2(100):='''Y'',''N''';
TYPE lv
IS
RECORD
(
flg_y VARCHAR2(100),
flg_n VARCHAR2(100),
flg_e VARCHAR2(100));
type lv_tab
IS
TABLE OF lv;
lv_num lv_tab;
lv_check VARCHAR2(1000);
BEGIN
lv_check :=regexp_count(lv_pivot,',',1);
IF lv_check < 3 THEN
FOR z IN 1..(2-lv_check)
LOOP
lv_pivot:=lv_pivot||',null as val'||z;
END LOOP;
ELSE
lv_pivot:=lv_pivot;
END IF;
dbms_output.put_line(lv_pivot);
EXECUTE IMMEDIATE ' SELECT * FROM
(SELECT col1 FROM <table> )
pivot ( COUNT(1) FOR col1 IN ('||lv_pivot||'))' BULK COLLECT INTO lv_num;
END;
---------------------------Refactoring in Function------------------------------
--Create Object Type
CREATE OR REPLACE TYPE lv_obj IS OBJECT
(
flg_y VARCHAR2(100),
flg_n VARCHAR2(100),
flg_e VARCHAR2(100)
);
--Create Table Type
CREATE OR REPLACE TYPE lv_tab IS TABLE OF lv_obj;
--Create Function
CREATE OR REPLACE
FUNCTION test_func
RETURN lv_tab
AS
lv_pivot VARCHAR2(100):='''Y'',''N''';
lv_num lv_tab;
lv_check VARCHAR2(1000);
BEGIN
lv_check :=regexp_count(lv_pivot,',',1);
IF lv_check < 3 THEN
FOR z IN 1..(2-lv_check)
LOOP
lv_pivot:=lv_pivot||',null as val'||z;
END LOOP;
ELSE
lv_pivot:=lv_pivot;
END IF;
dbms_output.put_line(lv_pivot);
EXECUTE IMMEDIATE ' SELECT * FROM
(SELECT col1 FROM <table> )
pivot ( COUNT(1) FOR col1 IN ('||lv_pivot||'))' BULK COLLECT INTO lv_num;
RETURN lv_tab;
END;
-------------------------------------------------Output-----------------------------------------------
SELECT * FROM TABLE(test_func);
-------------------------------------------------------------------------------------------------------
Try padding the results in the query. Add a join to a select from dual statement that passes back all five columns.
How would I display all records in the database using the items table? My current query displays the information for item 894. I attempted using a loop, but no luck.
I have two tables, inventory and itemid. Where itemid has the item number and the description, and the inventory table has the items' information, such as size, color, price, and quantity on hand.
set serveroutput on
DECLARE
current_item number(8);
totalvalue number(8,2);
description varchar2(50);
item_id number(3);
CURSOR Inventory_Info IS
SELECT
itemsize
,color
,curr_price
,qoh
,curr_price*qoh as Total
FROM inventory
WHERE itemid=Current_item;
BEGIN
current_item:=894;
totalvAlue:=0;
SELECT
itemdesc, itemid
INTO description, item_id
FROM item
WHERE itemid=current_item;
DBMS_OUTPUT.PUT_LINE('Item ID: ' || TO_CHAR(item_id) || ' Item Description: ' || description);
OPEN Inventory_Info;
LOOP
Fetch Inventory_Info INTO Inventory_rocord;
EXIT WHEN Inventory_Info%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Size: ' || Inventory_record.itemsize);
DBMS_OUTPUT.PUT_LINE('Color: ' || Inventory_record.color);
DBMS_OUTPUT.PUT_LINE('Price: ' || Inventory_record.curr_price);
DBMS_OUTPUT.PUT_LINE('QOH: ' || Inventory_record.qoh);
DBMS_OUTPUT.PUT_LINE('Value: ' || Inventory_record.total);
TotalValue:=TotalValue + Inventory_record.total;
End Loop;
DBMS_OUTPUT.PUT_LINE('TOTAL VALUE: ' || TotalValue);
Close Inventory_Info;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No inventory for Item No. '|| current_item);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error Message: '|| SQLERRM);
END;
If we for a moment forget about the formatting this could be done much simpler with a cursor for loop.
set serveroutput ON
DECLARE
BEGIN
FOR item_rec IN (SELECT itemdesc, itemid
FROM item
) LOOP
DBMS_OUTPUT.PUT_LINE('Item ID: ' || TO_CHAR(item_rec.itemid)
|| ' Item Description: ' || item_rec.itemdesc);
FOR Inventory_record IN (SELECT itemsize
, color
, curr_price
, qoh
, curr_price*qoh AS Total
FROM inventory
WHERE itemid = item_rec.itemid
) LOOP
DBMS_OUTPUT.PUT_LINE('Size: ' || Inventory_record.itemsize);
DBMS_OUTPUT.PUT_LINE('Color: ' || Inventory_record.color);
DBMS_OUTPUT.PUT_LINE('Price: ' || Inventory_record.curr_price);
DBMS_OUTPUT.PUT_LINE('QOH: ' || Inventory_record.qoh);
DBMS_OUTPUT.PUT_LINE('Value: ' || Inventory_record.total);
TotalValue:= TotalValue + Inventory_record.total;
END LOOP;
END LOOP;
END;
You're using a separate SELECT..INTO to fetch the itemid, but you assign the itemid to a single value and don't change that.
Looking at your queries, you can move the itemid fetch into the cursor & join the 2 tables. You fetch the items into inventory_record, but I don't see the definition/declaration of that anywhere.
Here I declare a record type variable consisting of what you're fetching, open the cursor, fetch the details into that cursor. Since there's no explicit where condition, it will perform an inner join between item & inventory tables & fetch all rows matching the join condition.
set serveroutput ON
DECLARE
TYPE inventory_rec IS RECORD (
itemid item.itemid%TYPE,
itemdesc item.itemdesc%TYPE,
itemsize inventory.itemsize%TYPE,
color inventory.color%TYPE,
curr_price inventory.curr_price%TYPE,
qoh inventory.qoh %TYPE,
total inventory.curr_price%TYPE); -- record type to hold what cursor will be fetching
inventory_record INVENTORY_REC;
current_item NUMBER(8);
totalvalue NUMBER(8, 2);
description VARCHAR2(50);
item_id NUMBER(3);
CURSOR inventory_info IS
SELECT itemid,
itemdesc,
itemsize,
color,
curr_price,
qoh,
curr_price * qoh AS Total
FROM inventory
join item USING (itemid);
-- join item & inventory, so that it shows listings for all items present in inventory
BEGIN
OPEN inventory_info;
LOOP
FETCH inventory_info INTO inventory_record;
EXIT WHEN inventory_info%NOTFOUND;
dbms_output.Put_line('Current item: '
|| inventory_record.itemdesc);
dbms_output.Put_line('Size: '
|| inventory_record.itemsize);
dbms_output.Put_line('Color: '
|| inventory_record.color);
dbms_output.Put_line('Price: '
|| inventory_record.curr_price);
dbms_output.Put_line('QOH: '
|| inventory_record.qoh);
dbms_output.Put_line('Value: '
|| inventory_record.total);
totalvalue := totalvalue + inventory_record.total;
END LOOP;
dbms_output.Put_line('TOTAL VALUE: '
|| totalvalue);
CLOSE inventory_info;
EXCEPTION
WHEN no_data_found THEN
dbms_output.Put_line('No inventory for Item No. '
|| current_item);
WHEN OTHERS THEN
dbms_output.Put_line('Error Message: '
|| SQLERRM);
END;
Don't use nested look-ups, use joins. Databases are really good at joins, and the performance of set operations is a lot a better than row-by-row processing.
Also, you don't need to declare cursors and variables in most situations. Use a cursor loop and let Oracle do the heavy lifting for you.
set serveroutput on
DECLARE
totalvalue number(8,2);
BEGIN
totalvAlue:=0;
FOR inv_itm_rec IN (
SELECT itm.itemid
, itm.itemdesc
, inv.itemsize
, inv.color
,inv.curr_price
,inv.qoh
,inv.curr_price*inv.qoh as Total
FROM inventory inv
JOIN item itm
ON itm.itemid=inv.itemid
)
LOOP
DBMS_OUTPUT.PUT_LINE('ItemId: ' || inv_itm_rec.itemid);
DBMS_OUTPUT.PUT_LINE('Description: ' || inv_itm_rec.itemdesc);
DBMS_OUTPUT.PUT_LINE('Size: ' || inv_itm_rec.itemsize);
DBMS_OUTPUT.PUT_LINE('Color: ' || inv_itm_rec.color);
DBMS_OUTPUT.PUT_LINE('Price: ' || inv_itm_rec.curr_price);
DBMS_OUTPUT.PUT_LINE('QOH: ' || inv_itm_rec.qoh);
DBMS_OUTPUT.PUT_LINE('Value: ' || inv_itm_rec.total);
TotalValue:=TotalValue + inv_itm_rec.total;
END LOOP;
DBMS_OUTPUT.PUT_LINE('TOTAL VALUE: ' || TotalValue);
END;
Note, this solution assumes that evry ITEM does have a matching INVENTORY record. It would be a rum old warehouse application if the data model allowed anything else to be the case.
If it's just a report(it seems to be). Consider using sql*plus report formatting commands to display the output of a query in a way you want it to be displayed.
Here is an example:
SQL> create table Items(
2 i_num number,
3 i_descr varchar2(101),
4 i_size varchar2(3),
5 i_price number,
6 i_qoh number
7 );
Table created
SQL> create table Inventories(
2 id number,
3 i_num number
4 );
Table created
SQL> insert into items(i_num,i_size,i_price,i_qoh,i_descr)
2 select 1, 'S', 123, 25, 'Item_1' from dual union all
3 select 2, 'L', 424, 12, 'Item_1' from dual union all
4 select 4, 'S', 45, 54, 'Item_4' from dual union all
5 select 5, 'S', 78, 54, 'Item_4' from dual union all
6 select 6, 'S', 123, 22, 'Item_5' from dual union all
7 select 7, 'S', 127, 65, 'Item_5' from dual
8 ;
6 rows inserted
SQL> commit;
Commit complete
SQL> insert into inventories
2 select i_num, i_num
3 from items;
6 rows inserted
SQL> commit;
Commit complete
And now our report:
SQL> column i_descr format a10
SQL> column i_size format a3
SQL> column i_price format 99999
SQL> column i_qoh format 99999
SQL> column value format 99999
SQL> break on i_descr skip 2
SQL> compute sum label 'Total: ' of value on i_descr;
SQL> select itm.i_descr
2 , itm.i_size
3 , itm.i_price
4 , itm.i_qoh
5 , sum(i_price*i_qoh) Value
6 from inventories inv
7 join items itm on (inv.i_num = itm.i_num)
8 group by itm.i_num
9 , itm.i_descr
10 , itm.i_size
11 , itm.i_price
12 , itm.i_qoh
13 order by i_descr, i_price;
I_DESCR I_S I_PRICE I_QOH VALUE
---------- --- ---------- ------ ------
Item_1 S 123 25 3075
L 424 12 5088
********** ------
Total: 8163
Item_4 L 45 54 2430
S 78 54 4212
********** ------
Total: 6642
Item_5 S 123 22 2706
L 127 65 8255
********** ------
Total: 10961
6 rows selected.
I am trying to get the results of the underlying statements. The SQL statements works without any problems. However, in order to print the results I would like to use refcursor. I get the following errors:
ORA-00900: invalid SQL statement
ORA-01008: not all variables bound
ORA-00900: invalid SQL statement
VARIABLE reader refcursor;
DECLARE
line varchar2(32767);
BEGIN
line := 'SELECT role_id,';
FOR n IN (SELECT name
FROM (SELECT competence.skill_role.role_id,
competence.skill_label.name,
competence.skill_role.target_value
FROM competence.skill_role,
competence.skill_label
WHERE competence.skill_label.skill_id =
competence.skill_role.skill_id
AND competence.skill_label.language_id = 1)
matrix_result ) LOOP
line := line || '(decode(name,''' || n.name ||
''',target_value)) "' || n.name || '",';
END LOOP;
line := RTRIM(line, ',') ||
' FROM (SELECT competence.skill_role.role_id,
competence.skill_label.name,
competence.skill_role.target_value
FROM competence.skill_role, competence.skill_label
WHERE competence.skill_label.skill_id =
competence.skill_role.skill_id
AND competence.skill_label.language_id = 1) matrix_result';
--dbms_output.put_line(line);
--execute immediate line;
OPEN :reader FOR line;
END;
/
PRINT :reader;
Table data
CREATE TABLE competence.skill_role
(skill_id NUMBER,
role_id NUMBER,
target_value NUMBER)
/
INSERT ALL
INTO competence.skill_role VALUES (3432030, 1421866, 2)
INTO competence.skill_role VALUES (3434962, 1421866, 2)
INTO competence.skill_role VALUES (3488025, 3488804, 4)
SELECT * FROM competence.skill_role
SKILL_ID ROLE_ID target_value
---------- ------- -----------
3432030 1421866 2
3434962 1421866 2
3488025 3488804 4
CREATE TABLE competence.skill_label
(skill_id NUMBER,
name vchar2 (30))
/
INSERT ALL
INTO competence.skill_label VALUES (3432030, 'Alueen projektipätevyys')
INTO competence.skill_label VALUES (3434962, 'Moottorin koekäyttö')
INTO competence.skill_label VALUES (3488025, 'Etähallintajärjestelmät')
SELECT * FROM arc_competence.skill_label
SKILL_ID NAME
---------- -------
3432030, Alueen projektipätevyys
3434962, Moottorin koekäyttö
3488025, Etähallintajärjestelmät
I would like to have the following result from the first query. From your answer (if I understood correctly), it seems that I need to run the resultant query manually to get the answer. I'd like to have the result without running the resultant query :-) I don't have the access to the client machine at the moment but I am going there now.
ROLE_ID Alueen projektipätevyys Moottorin koekäyttö Etähallintajärjestelmät
1421866 2 2
3488804 4
If I correct your code so that it compiles
Your INSERT ALL statements are missing a SELECT
The name column in skill_label is defined as a vchar2(30) rather than a varchar2(30)
Your anonymous block references a column language_id that your DDL does not include
the code runs without error. If the only problem is that you want to combine the first two rows into a single row, you just need to add a MAX to all the columns other than ROLE_ID and to add a GROUP BY role_id to your query.
SQL> ed
Wrote file afiedt.buf
1 CREATE TABLE skill_role
2 (skill_id NUMBER,
3 role_id NUMBER,
4* target_value NUMBER)
SQL> /
Table created.
SQL> ed
Wrote file afiedt.buf
1 INSERT ALL
2 INTO skill_role VALUES (3432030, 1421866, 2)
3 INTO skill_role VALUES (3434962, 1421866, 2)
4 INTO skill_role VALUES (3488025, 3488804, 4)
5* select * from dual
SQL> /
3 rows created.
SQL> ed
Wrote file afiedt.buf
1 CREATE TABLE skill_label
2 (skill_id NUMBER,
3* name varchar2 (30))
SQL> /
Table created.
SQL> ed
Wrote file afiedt.buf
1 INSERT ALL
2 INTO skill_label VALUES (3432030, 'Alueen projektipΣtevyys')
3 INTO skill_label VALUES (3434962, 'Moottorin koekΣytt÷')
4 INTO skill_label VALUES (3488025, 'EtΣhallintajΣrjestelmΣt')
5* select * from dual
SQL> /
3 rows created.
SQL> commit;
Commit complete.
SQL> variable reader refcursor;
SQL> ed
Wrote file afiedt.buf
1 DECLARE
2 line varchar2(32767);
3 BEGIN
4 line := 'SELECT role_id,';
5 FOR n IN (SELECT name
6 FROM (SELECT skill_role.role_id,
7 skill_label.name,
8 skill_role.target_value
9 FROM skill_role,
10 skill_label
11 WHERE skill_label.skill_id =
12 skill_role.skill_id
13 )
14 matrix_result ) LOOP
15 line := line || 'max(decode(name,''' || n.name ||
16 ''',target_value)) "' || n.name || '",';
17 END LOOP;
18 line := RTRIM(line, ',') ||
19 ' FROM (SELECT skill_role.role_id,
20 skill_label.name,
21 skill_role.target_value
22 FROM skill_role, skill_label
23 WHERE skill_label.skill_id =
24 skill_role.skill_id
25 ) matrix_result ' ||
26 ' GROUP BY role_id' ;
27 dbms_output.put_line(line);
28 --execute immediate line;
29 OPEN :reader FOR line;
30* END;
31 /
PL/SQL procedure successfully completed.
SQL> print reader
ROLE_ID Alueen projektipΣtevyys Moottorin koekΣytt÷ EtΣhallintajΣrjestelmΣt
---------- ----------------------- ------------------- -----------------------
1421866 2 2
3488804 4