How can I customize implict cursor to scan remote query (dblink) with similar name and identical structure (with similar filed name)?
I try this procedure code but I have this errors:
line: 9, column: 38, error: PL/SQL: ORA-00942: tabella o vista
inesistente
line: 15, column: 41, error: PLS-00364: uso della variabile di indice
loop 'C1' non valido
create table t1#DBLINK (
id number,
f1 varchar2(100),
f2 varchar2(100),
f3 varchar2(100),
f4 varchar2(100)
);
insert into t1#DBLINK values (1,'t1 r1 f1','t1 r1 f2','t1 r1 f3','t1 r1 f4');
insert into t1#DBLINK values (2,'t1 r2 f1','t1 r2 f2','t1 r2 f3','t1 r2 f4');
create table t2#DBLINK (
id number,
f1 varchar2(100),
f2 varchar2(100),
f3 varchar2(100),
f4 varchar2(100)
);
insert into t2#DBLINK values (1,'t2 r1 f1','t2 r1 f2','t2 r1 f3','t2 r1 f4');
insert into t2#DBLINK values (2,'t2 r2 f1','t2 r2 f2','t2 r2 f3','t2 r2 f4');
1 CREATE OR REPLACE PROCEDURE test AS
2 nt varchar2(10);
3 nf varchar2(10);
4 js varchar2(400);
5 BEGIN
6 FOR n0 IN 1..2
7 LOOP
8 nt := 't' || n0 || '#DBLINK'; -- table name
9 FOR c1 IN (SELECT * FROM nt)
10 LOOP
11 js := null;
12 FOR n1 IN 1..4
13 LOOP
14 nf := 'f' || n1; -- field name
15 js := js || c1.nf;
16 END LOOP;
17 dbms_output.put_line(js);
18 END LOOP;
19 END LOOP;
20 EXCEPTION WHEN OTHERS THEN
21 NULL
22 END;
Related
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION string_to_char (
c1 NUMBER,
c2 VARCHAR2,
c3 VARCHAR2
) RETURN VARCHAR2 IS
c1 NUMBER := 1;
c2 VARCHAR2(20);
c3 VARCHAR2(20) := ( '&c3' );
BEGIN
FOR c1 IN 1..20 LOOP
SELECT
substr(c3, c1, 1)
INTO c2
FROM
dual;
dbms_output.put_line(c2);
END LOOP;
END;
/*
Function STRING_TO_CHAR compiled
LINE/COL ERROR
--------- -------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
1/1 PLS-00410: duplicate fields in RECORD,TABLE or argument list are not permitted
Errors: check compiler log
*/
You named too many things with the same name (c1 in your case):
function's parameter
local variable
counter in FOR loop
If you modified it to e.g.
SQL> set serveroutput on
SQL>
SQL> CREATE OR REPLACE FUNCTION string_to_char (c1 NUMBER,
2 c2 VARCHAR2,
3 c3 VARCHAR2)
4 RETURN VARCHAR2
5 IS
6 pc1 NUMBER := 1;
7 pc2 VARCHAR2 (20);
8 pc3 VARCHAR2 (20) := c3;
9 BEGIN
10 FOR i IN 1 .. 20
11 LOOP
12 SELECT SUBSTR (c3, c1, 1) INTO pc2 FROM DUAL;
13
14 DBMS_OUTPUT.put_line (pc2);
15 END LOOP;
16
17 RETURN pc2;
18 END;
19 /
Function created.
SQL>
then it compiles and works (although, I'm not sure I understand what is its purpose):
SQL> SELECT string_to_char (1, 'A', 'B') FROM DUAL;
STRING_TO_CHAR(1,'A','B')
--------------------------------------------------------------------------------
B
B
B
B
B
B
B
B
B
B
B
B
B
B
B
B
B
B
B
B
B
SQL>
I'm trying to return a type as output parameter, but i'm getting error :
[Error] Compilation (12: 27): PLS-00382: expression is of wrong type
Here is the code:
create or replace TYPE OBJ_DCP FORCE as OBJECT (
ID NUMBER,
xxx_PROFILES_ID NUMBER,
DCP_NAME VARCHAR2(500 BYTE),
L_R_NUMBER VARCHAR2(150 BYTE),
STREET VARCHAR2(150 BYTE)
};
and
create or replace TYPE T_DCP_TYPE as TABLE OF OBJ_DCP;
and
create or replace procedure get_dcp_profiles(p_id in number, dcp_array_var OUT T_DCP_TYPE) is
cursor c_dcp_profiles is
select *
from table1 ca -- table1 has same structure of OBJ_DCP
where ca.id = p_id;
i number := 1;
begin
for r in c_dcp_profiles loop
dcp_array_var(i) := r; -- this line is errored
i := i + 1;
end loop;
end get_dcp_profiles;
Types are OK.
Sample table:
SQL> SELECT * FROM table1 ORDER BY id;
ID XXX_PROFILES_ID D L S
---------- --------------- - - -
1 1 a b c
1 2 d e f
2 3 x y z
SQL>
Procedure, modified:
SQL> CREATE OR REPLACE PROCEDURE get_dcp_profiles (
2 p_id IN NUMBER,
3 dcp_array_var OUT T_DCP_TYPE)
4 IS
5 CURSOR c_dcp_profiles IS
6 SELECT *
7 FROM table1 ca -- table1 has same structure of OBJ_DCP
8 WHERE ca.id = p_id;
9
10 i NUMBER := 1;
11 l_arr T_DCP_TYPE := T_DCP_TYPE ();
12 BEGIN
13 FOR r IN c_dcp_profiles
14 LOOP
15 l_arr.EXTEND;
16 l_arr (i) :=
17 OBJ_DCP (id => r.id,
18 xxx_profiles_id => r.xxx_profiles_id,
19 dcp_name => r.dcp_name,
20 l_r_number => r.l_r_number,
21 street => r.street);
22 i := i + 1;
23 END LOOP;
24
25 dcp_array_var := l_arr;
26 END get_dcp_profiles;
27 /
Procedure created.
Testing:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_tab t_dcp_type;
3 BEGIN
4 get_dcp_profiles (1, l_tab);
5
6 FOR i IN 1 .. l_tab.COUNT
7 LOOP
8 DBMS_OUTPUT.put_line (l_tab (i).id || ' ' || l_tab (i).dcp_name);
9 END LOOP;
10 END;
11 /
1 a
1 d
PL/SQL procedure successfully completed.
SQL>
You do not need to use cursors or to manually populate the collection.
If you declare the table as an object-derived table:
CREATE TABLE table1 OF OBJ_DCP;
Then you can simplify the procedure down to:
CREATE PROCEDURE get_dcp_profiles(
p_id IN number,
dcp_array_var OUT T_DCP_TYPE
)
IS
BEGIN
SELECT VALUE(ca)
BULK COLLECT INTO dcp_array_var
FROM table1 ca
WHERE ca.id = p_id;
END get_dcp_profiles;
/
For the sample data:
INSERT INTO table1 (id, xxx_Profiles_id, DCP_NAME, L_R_NUMBER, STREET)
SELECT 1, 42, 'ABC name', 'ABC number', 'ABC street' FROM DUAL UNION ALL
SELECT 1, 63, 'DEF name', 'DEF number', 'DEF street' FROM DUAL UNION ALL
SELECT 2, 12, 'GHI name', 'GHI number', 'GHI street' FROM DUAL;
Then:
DECLARE
dcps t_dcp_type;
BEGIN
get_dcp_profiles (1, dcps);
FOR i IN 1 .. dcps.COUNT LOOP
DBMS_OUTPUT.PUT_LINE (dcps(i).id || ' ' || dcps(i).dcp_name);
END LOOP;
END;
/
Outputs:
1 ABC name
1 DEF name
If you do not want to use an object-derived table then you can use:
CREATE OR REPLACE PROCEDURE get_dcp_profiles(
p_id IN number,
dcp_array_var OUT T_DCP_TYPE
)
IS
BEGIN
SELECT OBJ_DCP(id, xxx_profiles_id, dcp_name, l_r_number, street)
BULK COLLECT INTO dcp_array_var
FROM table1
WHERE id = p_id;
END get_dcp_profiles;
/
db<>fiddle here
Tables:
CREATE TABLE bus_details
(
bus_name CHAR (15) PRIMARY KEY,
total_seats NUMBER (3),
reserved_seats NUMBER (3)
);
CREATE TABLE busreservation_status
(
bus_name CHAR (15) REFERENCES bus_details (bus_name),
seat_id NUMBER (3),
reserved CHAR (2) CHECK (reserved IN ('y', 'n')),
customer_name CHAR (15)
);
PL/SQL code:
DECLARE
bname CHAR (15);
tot NUMBER (3);
resv NUMBER (3);
total_seats NUMBER (3);
CURSOR cur IS SELECT * FROM bus_details;
BEGIN
INSERT INTO bus_details
VALUES ('&bus_name', total_seats, 0);
OPEN cur;
LOOP
FETCH cur INTO bname, tot, resv;
IF cur%FOUND
THEN
FOR i IN 1 .. tot
LOOP
INSERT INTO busreservation_status
VALUES (bname,
i,
'n',
NULL);
END LOOP;
ELSE
EXIT;
END IF;
END LOOP;
CLOSE cur;
END;
/
Error:
ORA-06502: PL/SQL: numeric or value error
PL/SQL isn't supposed to be interactive with users. If you want to pass something to it, use a procedure with appropriate parameter(s).
If you rewrite code so that it looks like this
note cursor FOR loop instead of explicitly declared cursor which requires opening, declaring cursor variables, fetching, exiting the loop, closing the cursor
setting all required values for bus_details
don't use CHAR datatype unless it makes sense. For e.g. bus name it doesn't, as that datatype right-pads value with spaces up to max column length
Then it works:
SQL> DECLARE
2 l_bus_name VARCHAR2 (15) := 'BMW';
3 l_total_seats NUMBER := 20;
4 l_reserved_seats NUMBER := 5;
5 BEGIN
6 INSERT INTO bus_details (bus_name, total_seats, reserved_seats)
7 VALUES (l_bus_name, l_total_seats, l_reserved_seats);
8
9 FOR cur_r
10 IN (SELECT bus_name, total_seats, reserved_seats FROM bus_details)
11 LOOP
12 FOR i IN 1 .. cur_r.total_seats
13 LOOP
14 INSERT INTO busreservation_status
15 VALUES (cur_r.bus_name,
16 i,
17 'n',
18 NULL);
19 END LOOP;
20 END LOOP;
21 END;
22 /
PL/SQL procedure successfully completed.
Result is:
SQL> select * From bus_details;
BUS_NAME TOTAL_SEATS RESERVED_SEATS
--------------- ----------- --------------
BMW 20 5
SQL> SELECT * FROM busreservation_status;
BUS_NAME SEAT_ID RE CUSTOMER_NAME
--------------- ---------- -- ---------------
BMW 1 n
BMW 2 n
BMW 3 n
BMW 4 n
BMW 5 n
<snip>
BMW 19 n
BMW 20 n
20 rows selected.
SQL>
you have to set some value to the variable total_seats number(3); or prompt input value for total_seats when inserting operation in process:
for example:
Declare
bname Char(15);
tot Number(3);
resv Number(3);
total_seats Number(3) := 15;
Cursor cur Is
Select * From bus_details;
Begin
Insert Into bus_details Values ('&bus_name', total_seats, 0);
Open cur;
Loop
Fetch cur
Into bname, tot, resv;
If cur%Found Then
For i In 1 .. tot Loop
Insert Into busreservation_status Values (bname, i, 'n', Null);
End Loop;
Else
Exit;
End If;
End Loop;
Close cur;
End;
/
OR
Declare
bname Char(15);
tot Number(3);
resv Number(3);
total_seats Number(3);
Cursor cur Is
Select * From bus_details;
Begin
Insert Into bus_details Values ('&bus_name', '&total_seats', 0);
Open cur;
Loop
Fetch cur
Into bname, tot, resv;
If cur%Found Then
For i In 1 .. tot Loop
Insert Into busreservation_status Values (bname, i, 'n', Null);
End Loop;
Else
Exit;
End If;
End Loop;
Close cur;
End;
/
Yes you would need to insert bus_details first, the FK on busreservation_status to bus_details requires it. Further there are additional suggested changed.
Drop column reserved_seats from bus_details. This column is fully
derivable from busreservation_status and typically maintaining a
running count is just not worth the effort. But having it and not
maintaining leads to data errors. For Example: you can have more
seats reserved then the bus has seats.
Drop the column reserved from busreservation_status. This is
derivable from customer_name; if customer_name is not null the seat
is reserved. But assuming you "must keep it" then define is a
virtual column bases on customer_name. This would keep the column in
sync with there being a customer for the seat.
Change char columns to varchar2, and I would change number to
integer.
Create a procedure for setting up a bus (IMO better). The procedure
can be parameterized, not an attempt at user interaction - which
plsql can not do anyway. But whether you create a procedure or keep an anonymous block, there is no need for a loop; a single insert is all that is needed.
With the above in place we arrive at:
create table bus_details
(
bus_name varchar2 (15) primary key
, total_seats number (3)
);
create table busreservation_status
(
bus_name varchar2 (15) references bus_details (bus_name)
, seat_id number (3)
, customer_name varchar2 (15)
, reserved varchar2 (1) generated always as
( case when customer_name is null then 'n' else 'y' end) virtual
, constraint busreservation_status_pk
primary key (bus_name,seat_id)
);
create or replace
procedure define_bus(bus_name_in bus_details.bus_name%type
,seats_in bus_details.total_seats%type
)
is
begin
insert into bus_details(bus_name, total_seats)
values (bus_name_in, seats_in);
insert into busreservation_status(bus_name, seat_id)
select bus_name_in, level
from dual connect by level <= seats_in;
end ;
For quick look at the bus states at summary and detail level you can create views. The demo also includes a couple.
In oracle 11g r2,we want to get all the columns and Splicing, it issue:ORA-06502: PL/SQL: numeric or value error: character string buffer too small,and how to modify?
step 1:create table:
create table test.history_session as select * from dba_hist_active_sess_history where 1=1;
step 2: test:
declare
column_list1 varchar2(32767);
column_list2 varchar2(32767);
i number;
l number;
BEGIN
column_list1:='';
column_list2:='';
i:=0;
l:=0;
FOR v_column_name IN (SELECT owner, table_name, column_name,data_length,data_type FROM dba_tab_columns WHERE owner ='TEST' and table_name='HISTORY_SESSION'
and data_type in ('VARCHAR2','NVARCHAR2','NUMBER','CHAR') and data_length <=255 order by column_name) LOOP
if v_column_name.data_type='NUMBER' then
v_column_name.column_name:='to_char('||v_column_name.column_name||',''99999999999999999.99'')';
end if;
if l <3700 then
column_list1:=column_list1||v_column_name.column_name||chr(10)||'||''|'''||'||';
else
column_list2:=column_list2||v_column_name.column_name||chr(10)||'||''|'''||'||';
end if;
l:=l+v_column_name.data_length+10;
end loop;
dbms_output.put_Line(nvl(column_list1,' ')||nvl(column_list2,' '));
end;
/
error:
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 14
i want to know how to modify?
You can't set cursor's variable to something else; this is wrong:
v_column_name.column_name:='to_char('||v_column_name.column_name| ...
Declare a local variable instead, then do those transformations, if needed. Something like this (ran as SCOTT user, so I'm using USER_TAB_COLUMNS instead).
SQL> CREATE TABLE history_session
2 (
3 id NUMBER
4 );
Table created.
SQL> DECLARE
2 column_list1 VARCHAR2 (32767);
3 column_list2 VARCHAR2 (32767);
4 l_column_name VARCHAR2 (200); --> local variable
5 i NUMBER;
6 l NUMBER;
7 BEGIN
8 column_list1 := '';
9 column_list2 := '';
10 i := 0;
11 l := 0;
12
13 FOR v_column_name IN ( SELECT --owner,
14 table_name,
15 column_name,
16 data_length,
17 data_type
18 FROM user_tab_columns
19 WHERE 1 = 1
20 -- AND owner = 'TEST'
21 AND table_name = 'HISTORY_SESSION'
22 AND data_type IN ('VARCHAR2',
23 'NVARCHAR2',
24 'NUMBER',
25 'CHAR')
26 AND data_length <= 255
27 ORDER BY column_name)
28 LOOP
29 IF v_column_name.data_type = 'NUMBER'
30 THEN
31 --v_column_name.column_name :=
32 l_column_name :=
33 'to_char('
34 || v_column_name.column_name
35 || ',''99999999999999999.99'')';
36 ELSE
37 l_column_name := v_column_name.column_name;
38 END IF;
39
40 IF l < 3700
41 THEN
42 column_list1 :=
43 column_list1 || l_column_name --v_column_name.column_name
44 || CHR (10) || '||''|''' || '||';
45 ELSE
46 column_list2 :=
47 column_list2 || l_column_name --v_column_name.column_name
48 || CHR (10) || '||''|''' || '||';
49 END IF;
50
51 l := l + v_column_name.data_length + 10;
52 END LOOP;
53
54 DBMS_OUTPUT.put_Line (NVL (column_list1, ' ') || NVL (column_list2, ' '));
55 END;
56 /
to_char(ID,'99999999999999999.99')
||'|'||
PL/SQL procedure successfully completed.
SQL>
The result doesn't look pretty, but I'll leave it to you - fix it, now that procedure kind of works.
I have 2 columns in a table named Query and Values.
Query :
insert into
tbl_details(CName,Line,Type,Command,Rule,Client_ID,Site_ID,SName)
values (l_Name,:l_Line,:l_Type,:l_Command,:l_Rule,:l_Client_ID,:l_Site_ID,l_Name)
Values : #1(2):20 #2(1):H #3(2):IF #4(27):FA - RETAIN OLD MASTER DATA #5(0): #6(0):
CName and SName use same value Manu.
I want to replace binded vars with values in next columns to get executable query.
What i want:
I want to write a procedure which get values from both columns of table to make a query and execute that query.
insert into address_MP (CName,Line,Type,Command,Rule,Client_ID,Site_ID,SName)
values ('Manu','20','H','IF','FA - RETAIN OLD MASTER DATA','','Manu')
Here's one option, which converts both query VALUES clause, as well as values themselves into rows - each in its own cursor loop, pairing bind variable and its value via their row number. CHR(39) is a single quote.
As the table most probably doesn't contain a single row (is it identified by some ID? You never told us), you'll have to adjust it, otherwise it won't work properly.
Test table:
SQL> select * from test;
QUERY
--------------------------------------------------------------------------------
C_VALUES
--------------------------------------------------------------------------------
insert into
tbl_details(CName,Line,Type,Command,Rule,Client_ID,Site_ID,SName)
values (l_Name,:l_Line,:l_Type,:l_Command,:l_Rule,:l_Client_ID,:l_Site_ID,l_Name
)
#1(2):20 #2(1):H #3(2):IF #4(27):FA - RETAIN OLD MASTER DATA #5(0): #6(0):
SQL> set serveroutput on
Code & the result:
SQL> DECLARE
2 l_query test.query%TYPE;
3 l_name VARCHAR2 (30) := 'Manu';
4 BEGIN
5 SELECT query INTO l_query FROM test;
6
7 -- bind variables in QUERY
8 FOR cur_l
9 IN (SELECT ROW_NUMBER () OVER (ORDER BY lvl) rn, res l_val
10 FROM ( SELECT LEVEL lvl,
11 REGEXP_SUBSTR (res,
12 '[^,]+',
13 1,
14 LEVEL)
15 res
16 FROM (SELECT SUBSTR (query, INSTR (query, 'values')) res
17 FROM test)
18 CONNECT BY LEVEL <= REGEXP_COUNT (res, ':') + 1)
19 WHERE SUBSTR (res, 1, 1) = ':')
20 LOOP
21 -- values in VALUES
22 FOR cur_v
23 IN (SELECT rn, TRIM (SUBSTR (res, INSTR (res, ':') + 1)) c_val
24 FROM ( SELECT LEVEL rn,
25 REGEXP_SUBSTR (t.c_values,
26 '[^#]+',
27 1,
28 LEVEL)
29 res
30 FROM test t
31 CONNECT BY LEVEL <= REGEXP_COUNT (t.c_values, '#'))
32 WHERE rn = cur_l.rn)
33 LOOP
34 l_query :=
35 REPLACE (l_query,
36 cur_l.l_val,
37 CHR (39) || cur_v.c_val || CHR (39));
38 END LOOP;
39 END LOOP;
40
41 -- Put Manu into l_Name
42 l_query := REPLACE (l_query, 'l_Name', CHR (39) || l_name || CHR (39));
43
44 DBMS_OUTPUT.put_line (l_query);
45 END;
46 /
insert into
tbl_details(CName,Line,Type,Command,Rule,Client_ID,Site_ID,SName)
values ('Manu','20','H','IF','FA - RETAIN OLD MASTER DATA','','','Manu')
PL/SQL procedure successfully completed.
SQL>