I have string like this: str:='ac_Abc.88,ac_Abc.99,ac_Abc.77'. I need to get first element after splitting with comma(,). So im using using like this:
str VARCHAR2(500);
dbms_utility.comma_to_table
( list => regexp_replace(str,'(^|,)','\1')
, tablen => l_count
, tab => l_array
);
I'm getting following error:
ORA-20001: comma-separated list invalid near bc.88
ORA-06512: at "SYS.DBMS_UTILITY", line 239
ORA-06512: at "SYS.DBMS_UTILITY", line 272
But if i have string like this, str:='ac_Abc88,ac_Abc99,ac_Abc77', the same method working fine and giving me expected results.
So i guess there is something need to be corrected to consider "." as regular character. Can you please suggest how can i solve this.
See How to split comma delimited string into rows
1. REGEXP_SUBSTR approach
SQL> WITH DATA AS(
2 SELECT 'ac_Abc.88,ac_Abc.99,ac_Abc.77' str FROM dual)
3 SELECT regexp_substr(str,'[^,]+',1,level) str
4 FROM DATA
5 CONNECT BY regexp_substr(str, '[^,]+', 1, level) IS NOT NULL
6 /
STR
-----------------------------
ac_Abc.88
ac_Abc.99
ac_Abc.77
SQL>
2. XML approach
SQL> SELECT EXTRACT (VALUE (d), '//row/text()').getstringval () str
2 FROM
3 (SELECT XMLTYPE ( '<rows><row>'
4 || REPLACE ('ac_Abc.88,ac_Abc.99,ac_Abc.77', ',', '</row><row>')
5 || '</row></rows>' ) AS xmlval
6 FROM DUAL
7 ) x,
8 TABLE (XMLSEQUENCE (EXTRACT (x.xmlval, '/rows/row'))) d
9 /
STR
--------------------
ac_Abc.88
ac_Abc.99
ac_Abc.77
3. Table function
SQL> CREATE TYPE test_type
2 AS
3 TABLE OF VARCHAR2(100)
4 /
Type created.
SQL>
SQL> CREATE OR REPLACE
2 FUNCTION comma_to_table(
3 p_list IN VARCHAR2)
4 RETURN test_type
5 AS
6 l_string VARCHAR2(32767) := p_list || ',';
7 l_comma_index PLS_INTEGER;
8 l_index PLS_INTEGER := 1;
9 l_tab test_type := test_type();
10 BEGIN
11 LOOP
12 l_comma_index := INSTR(l_string, ',', l_index);
13 EXIT
14 WHEN l_comma_index = 0;
15 l_tab.EXTEND;
16 l_tab(l_tab.COUNT) := SUBSTR(l_string, l_index, l_comma_index - l_index);
17 l_index := l_comma_index + 1;
18 END LOOP;
19 RETURN l_tab;
20 END comma_to_table;
21 /
Function created.
SQL> sho err
No errors.
SQL>
SQL> SELECT * FROM TABLE(comma_to_table('ac_Abc.88,ac_Abc.99,ac_Abc.77'))
2 /
COLUMN_VALUE
--------------------------------------------------------------------------------
ac_Abc.88
ac_Abc.99
ac_Abc.77
SQL>
4. Pipelined Function
SQL> CREATE OR REPLACE
2 FUNCTION comma_to_table(
3 p_list IN VARCHAR2)
4 RETURN test_type PIPELINED
5 AS
6 l_string LONG := p_list || ',';
7 l_comma_index PLS_INTEGER;
8 l_index PLS_INTEGER := 1;
9 BEGIN
10 LOOP
11 l_comma_index := INSTR(l_string, ',', l_index);
12 EXIT
13 WHEN l_comma_index = 0;
14 PIPE ROW ( SUBSTR(l_string, l_index, l_comma_index - l_index) );
15 l_index := l_comma_index + 1;
16 END LOOP;
17 RETURN;
18 END comma_to_table;
19 /
Function created.
SQL> sho err
No errors.
SQL>
SQL> SELECT * FROM TABLE(comma_to_table('ac_Abc.88,ac_Abc.99,ac_Abc.77'))
2 /
COLUMN_VALUE
--------------------------------------------------------------------------------
ac_Abc.88
ac_Abc.99
ac_Abc.77
It is because (Oracle doc reference)
COMMA_TO_TABLE Procedures
These procedures converts a comma-delimited list of names into a
PL/SQL table of names. The second version supports fully-qualified
attribute names.
A "name" referred to here is a valid Oracle (DB object) identifier, for which all naming rules apply. ac_Abc.88 is not a valid name, because in Oracle you can't have an identifier starting with a digit.
To resolve your problem with parsing strings of comma-delimited values, use the solution of Lalit Kumar B's.
Related
CREATE TABLE test_table
(
col1 NUMBER(10),
col2 NUMBER(10)
);
INSERT INTO test_table
VALUES (1, 2);
I am writing a stored procedure wherein if I give a table name as an input, that should give me the table data and column details.
For example:
SELECT *
FROM <input_table_name>;
But this causes an error that the SQL command has not ended properly even though I have taken care of this.
My attempt:
CREATE OR REPLACE PROCEDURE sp_test(iv_table_name IN VARCHAR2,
p_out_cur OUT SYS_REFCURSOR)
AS
lv_str VARCHAR2(400);
lv_count NUMBER(1);
lv_table_name VARCHAR2(255):=UPPER(iv_table_name);
BEGIN
lv_str := 'SELECT * FROM '||lv_table_name;
SELECT COUNT(1) INTO lv_count FROM all_tables WHERE table_name = lv_table_name;
IF lv_count = 0 THEN
dbms_output.put_line('Table does not exist');
ELSE
OPEN p_out_cur FOR lv_str;
END IF;
END sp_test;
Tool used: SQL developer(18c)
In dynamic SQL, you do NOT terminate statement with a semicolon.
EXECUTE IMMEDIATE 'SELECT * FROM '||lv_table_name||';';
-----
remove this
Anyway, you won't get any result when you run that piece of code. If you really want to see table's contents, you'll have to switch to something else, e.g. create a function that returns ref cursor.
Sample data:
SQL> SELECT * FROM test_table;
COL1 COL2
---------- ----------
1 2
3 4
Procedure you wrote is now correct:
SQL> CREATE OR REPLACE PROCEDURE sp_test (iv_table_name IN VARCHAR2,
2 p_out_cur OUT SYS_REFCURSOR)
3 AS
4 lv_str VARCHAR2 (400);
5 lv_count NUMBER (1);
6 lv_table_name VARCHAR2 (255) := UPPER (iv_table_name);
7 BEGIN
8 lv_str := 'SELECT * FROM ' || lv_table_name;
9
10 SELECT COUNT (1)
11 INTO lv_count
12 FROM all_tables
13 WHERE table_name = lv_table_name;
14
15 IF lv_count = 0
16 THEN
17 DBMS_OUTPUT.put_line ('Table does not exist');
18 ELSE
19 OPEN p_out_cur FOR lv_str;
20 END IF;
21 END sp_test;
22 /
Procedure created.
Testing:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_rc SYS_REFCURSOR;
3 l_col1 NUMBER (10);
4 l_col2 NUMBER (10);
5 BEGIN
6 sp_test ('TEST_TABLE', l_rc);
7
8 LOOP
9 FETCH l_rc INTO l_col1, l_col2;
10
11 EXIT WHEN l_rc%NOTFOUND;
12
13 DBMS_OUTPUT.put_line (l_col1 || ', ' || l_col2);
14 END LOOP;
15 END;
16 /
1, 2 --> contents of the
3, 4 --> TEST_TABLE
PL/SQL procedure successfully completed.
SQL>
A function (instead of a procedure with the OUT parameter):
SQL> CREATE OR REPLACE FUNCTION sf_test (iv_table_name IN VARCHAR2)
2 RETURN SYS_REFCURSOR
3 AS
4 lv_str VARCHAR2 (400);
5 lv_count NUMBER (1);
6 lv_table_name VARCHAR2 (255) := UPPER (iv_table_name);
7 l_rc SYS_REFCURSOR;
8 BEGIN
9 lv_str := 'SELECT * FROM ' || lv_table_name;
10
11 SELECT COUNT (1)
12 INTO lv_count
13 FROM all_tables
14 WHERE table_name = lv_table_name;
15
16 IF lv_count = 0
17 THEN
18 raise_application_error (-20000, 'Table does not exist');
19 ELSE
20 OPEN l_rc FOR lv_str;
21 END IF;
22
23 RETURN l_rc;
24 END sf_test;
25 /
Function created.
Testing:
SQL> SELECT sf_test ('liksajfla') FROM DUAL;
SELECT sf_test ('liksajfla') FROM DUAL
*
ERROR at line 1:
ORA-20000: Table does not exist
ORA-06512: at "SCOTT.SF_TEST", line 18
SQL> SELECT sf_test ('TEST_TABLE') FROM DUAL;
SF_TEST('TEST_TABLE'
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
COL1 COL2
---------- ----------
1 2
3 4
SQL>
How to write a PL/SQL program to separate the code into its two parts as shown in the following example: if the input is ABC031, the output should be:
Product Name is: ABC
Serial Number is: 031
Hints: Use the following functions if needed:
instr( string1, string2 [, start_position [, nth_appearance ] ] )
substr( string, start_position, [ length ] )
length( string )
DBMS_OUTPUT.PUT_LINE( )
Here are two examples, see if any of these helps.
SQL> set serveroutput on
SQL>
SQL> create or replace procedure p_test (par_input in varchar2)
2 is
3 l_product_name varchar2(10);
4 l_serial_number varchar2(10);
5 begin
6 -- first example
7 l_product_name := substr(par_input, 1, 3);
8 l_serial_number := substr(par_input, 4);
9
10 dbms_output.put_line('First example : ' || l_product_name ||', '|| l_serial_number);
11
12 -- second example, possibly better as it splits letters from digits
13 l_product_name := regexp_substr(par_input, '^[[:alpha:]]+');
14 l_serial_number := regexp_substr(par_input, '[[:digit:]]+$');
15
16 dbms_output.put_line('Second example: ' || l_product_name ||', '|| l_serial_number);
17 end;
18 /
Procedure created.
SQL> begin
2 p_test('ABC031');
3 end;
4 /
First example : ABC, 031
Second example: ABC, 031
PL/SQL procedure successfully completed.
SQL>
Can someone please help me? Why do I get this error "INTO list is of wrong type"?
DECLARE
TYPE v_dept_table IS TABLE OF DEPARTMENTS.DEPARTMENT_NAME%TYPE INDEX BY PLS_INTEGER;
v_record_dept_table v_dept_table;
v_loop_count NUMBER := 10;
v_dept_no NUMBER := 1;
BEGIN
FOR v_dept_no IN 1..v_loop_count LOOP
SELECT DEPARTMENTS.DEPARTMENT_NAME
INTO v_record_dept_table
FROM DEPARTMENTS
WHERE DEPARTMENT_ID = v_dept_no;
v_dept_no := v_dept_no + 1;
INSERT
INTO v_dept_table
VALUES v_record_dept_table;
END LOOP;
FOR indx IN NVL (v_dept_table.FIRST, 0) .. NVL (v_dept_table.LAST, -1)
LOOP
DBMS_OUTPUT.PUT_LINE(v_dept_table(indx));
END LOOP;
END;
ORA-06550: line 16, column 14:
PLS-00597: expression 'V_RECORD_DEPT_TABLE' in the INTO list is of wrong type
Why is it a wrong type? I'm using the hr schema from Oracle
Not exactly like that; you've done several things wrong. I tried to fix your code (with as little modifications as possible); have a look, read comments I wrote, compare it to your code. Side note: I used Scott's DEPT table as I don't have your DEPARTMENTS.
SQL> set serveroutput on
SQL> DECLARE
2 TYPE v_dept_table IS TABLE OF dept.dname%TYPE
3 INDEX BY PLS_INTEGER;
4
5 v_record_dept_table v_dept_table;
6
7 v_loop_count NUMBER := 10;
8 v_dept_no NUMBER := 1;
9 BEGIN
10 FOR v_dept_no IN 1 .. v_loop_count
11 LOOP
12 BEGIN
13 SELECT dname
14 INTO v_record_dept_table (v_dept_no) --> you're missing "(v_dept_no)"
15 FROM dept
16 WHERE deptno = v_dept_no;
17 -- Don't manually increment FOR loop variable; Oracle does it itself
18 -- v_dept_no := v_dept_no + 1;
19
20 -- You can't insert into "type"; besides, you've already inserted into V_RECORD_DEPT_TABLE.
21 -- INSERT INTO v_dept_table VALUES v_record_dept_table;
22 EXCEPTION
23 WHEN NO_DATA_FOUND
24 THEN
25 NULL;
26 END;
27 END LOOP;
28
29 -- loop through V_RECORD_DEPT_TABLE (collection), not V_DEPT_TABLE (type). No need for NVL.
30 FOR indx IN NVL (v_record_dept_table.FIRST, 0) ..
31 NVL (v_record_dept_table.LAST, -1)
32 LOOP
33 DBMS_OUTPUT.PUT_LINE (v_record_dept_table (indx));
34 END LOOP;
35 END;
36 /
ACCOUNTING
PL/SQL procedure successfully completed.
SQL>
Alternatively, see whether this helps. I used built-in type (sys.odcivarchar2list) and BULK COLLECT INTO (performs better).
SQL> DECLARE
2 v_record_dept_table SYS.odcivarchar2list;
3 BEGIN
4 SELECT dname
5 BULK COLLECT INTO v_record_dept_table
6 FROM dept;
7
8 FOR indx IN v_record_dept_table.FIRST .. v_record_dept_table.LAST
9 LOOP
10 DBMS_OUTPUT.PUT_LINE (v_record_dept_table (indx));
11 END LOOP;
12 END;
13 /
ACCOUNTING
RESEARCH
SALES
OPERATIONS
PL/SQL procedure successfully completed.
SQL>
I have a file which consists of thousand rows and I need to get a portion of the 2nd row (about 50 characters) and the last row of the file. Please advise. Thank you.
Im trying to do something like UTL_FILE.READLINE(fileloc, filename, nrow, lastrow).
SAMPLE:
Filename: CLOSED_SO_20190124.txt
DATA in the FILE:
0246608377|22795124004|
650930363|1-8IGO3S82920|
0245563264|22669075004|
0245563264|22669075004|
164260364|1-2DFE-6573219|
650821459|1-6HWQUF11209|
EXPECTED OUTPUT:
650930363|1-8IGO3S82920|
650821459|1-6HWQUF11209|
Here's an example.
Directory name & its location, as well as sample file contents:
SQL> select directory_name, directory_path from all_directories;
DIRECTORY_NAME DIRECTORY_PATH
------------------------------ --------------------
EXT_DIR c:\temp
SQL> $type c:\temp\sofile.txt
0246608377|22795124004|
650930363|1-8IGO3S82920|
0245563264|22669075004|
0245563264|22669075004|
164260364|1-2DFE-6573219|
650821459|1-6HWQUF11209|
SQL>
The procedure: a local counter (l_cnt) knows line number; if it is equal to 2, display that line. Also, when nothing's being found (so exception handler section is executed), I've reached the end of the file so I'm displaying the last line as well.
SQL> set serveroutput on
SQL>
SQL> declare
2 l_file utl_file.file_type;
3 l_dir varchar2(20) := 'EXT_DIR';
4 l_name varchar2(20) := 'sofile.txt';
5 l_line varchar2(50);
6 l_cnt number := 0;
7 begin
8 l_file := utl_file.fopen (l_dir, l_name, 'R');
9 loop
10 begin
11 utl_file.get_line(l_file, l_line);
12 l_cnt := l_cnt + 1;
13 if l_cnt = 2 then
14 dbms_output.put_line('2nd : ' || l_line);
15 end if;
16 exception
17 when no_data_found then
18 dbms_output.put_line('last: ' || l_line);
19 exit;
20 end;
21 end loop;
22 utl_file.fclose(l_file);
23 end;
24 /
2nd : 650930363|1-8IGO3S82920|
last: 650821459|1-6HWQUF11209|
PL/SQL procedure successfully completed.
SQL>
In Oracle 11g
select col
from
(
select
rownum AS rnum,
LEFT(myCol, 50) col
from Table1
where Rownum < 3
)
WHERE rnum = 2
In Oracle 12c
select LEFT(mycol, 50) col
from Table1
ORDER BY val
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;
For the last row
select LEFT(mycol, 50) col
from my_table
where pk = ( select max(pk) from my_table )
then you can union them
Select all the tables of database where column match than pass table name to next query using loop. If column name and column values matches than return true and exist for loop using a stored procedure:
CREATE OR REPLACE PROCEDURE TEST
(
NAME IN VARCHAR2 ,
ID IN NUMBER,
RE OUT SYS_REFCURSOR
) AS
BEGIN
OPEN RE FOR SELECT A.TABLE_NAME FROM
user_tables A JOIN user_tab_columns C
ON C.TABLE_NAME = A.TABLE_NAME
WHERE C.COLUMN_NAME = NAME;
FOR RE IN LOOP
v_Sql := 'SELECT COUNT(*) FROM '|| LOOP.TABLE_NAME || 'WHERE COLUMN_NAME =
ID';
EXECUTE IMMEDIATE v_Sql
IF v_Sql%ROWCOUNT > 0 THEN
return true;
EXIT
END LOOP;
END TEST;
For more understanding the problem
//Get all the tables of database where campus_id is exist in any table of
database
Campus, Class, Section (3 tables found)
Apply forloop on the records
Select count(campus_id) as total from (table name using loop) where campus_id = 1(value
pass)
if(total > 0){
Exist for loop and return true
}
else{
Again iterate the loop to next value
}
What you described doesn't make much sense. If there are several tables that contain a column you're checking and you exit the loop as soon as you find the first one, what about the rest of them?
Here's what I'd do, see if it helps. I'll create a function (not a procedure) that returns a table. In order to do that, I'll create type(s) first:
SQL> create or replace type t_record as object (tn varchar2(30), cnt number);
2 /
Type created.
SQL> create or replace type t_table as table of t_record;
2 /
Type created.
SQL>
The function:
in a cursor FOR loop I'm selecting tables that contain that column
L_STR is used to compose the SELECT statement
DBMS_OUTPUT.PUT_LINE is used to display it first, so that I could visually check whether it is correctly set or not.
if it is, I'm running it with the EXECUTE IMMEDIATE
the result is stored into a table type and returned to the caller
SQL> create or replace function f_colname
2 (par_column_name in varchar2,
3 par_column_value in varchar2
4 )
5 return t_table
6 is
7 retval t_table := t_table();
8 l_str varchar2(200);
9 l_cnt number;
10 begin
11 for cur_r in (select table_name
12 from user_tab_columns
13 where column_name = par_column_name
14 )
15 loop
16 l_str := 'select count(*) from ' || cur_r.table_name ||
17 ' where ' || par_column_name || ' = ' ||
18 chr(39) || par_column_value || chr(39);
19 -- Display l_str first, to make sure that it is OK:
20 -- dbms_output.put_line(l_str);
21 execute immediate l_str into l_cnt;
22 retval.extend;
23 retval(retval.count) := t_record(cur_r.table_name, l_cnt);
24 end loop;
25 return retval;
26 end;
27 /
Function created.
Testing:
SQL> select * from table (f_colname('DEPTNO', '10'));
TN CNT
------------------------------ ----------
TEST_201812 1
DEPT 1
EMP 3
SQL> select * from table (f_colname('ENAME', 'KING'));
TN CNT
------------------------------ ----------
EMP 1
BONUS 1
SQL>
That won't work properly for some datatypes (such as DATE) and will have to be adjusted, if necessary.
[EDIT: after you edited the question]
OK then, that's even simpler. It should still be a function (that returns a Boolean, as you said that - in case that something's being found - you want to return TRUE). Code is pretty much similar to the previous function.
SQL> create or replace function f_colname
2 (par_column_name in varchar2,
3 par_column_value in varchar2
4 )
5 return boolean
6 is
7 l_str varchar2(200);
8 l_cnt number;
9 retval boolean := false;
10 begin
11 for cur_r in (select table_name
12 from user_tab_columns
13 where column_name = par_column_name
14 )
15 loop
16 l_str := 'select count(*) from ' || cur_r.table_name ||
17 ' where ' || par_column_name || ' = ' ||
18 chr(39) || par_column_value || chr(39);
19 -- Display l_str first, to make sure that it is OK:
20 -- dbms_output.put_line(l_str);
21 execute immediate l_str into l_cnt;
22 if l_cnt > 0 then
23 retval := true;
24 exit;
25 end if;
26 end loop;
27 return retval;
28 end;
29 /
Function created.
Testing: as you can't return Boolean at SQL layer, you have to use an anonymous PL/SQL block, as follows:
SQL> declare
2 l_ret boolean;
3 begin
4 if f_colname('DEPTNO', '15') then
5 dbms_output.put_line('It exists');
6 else
7 dbms_output.put_line('It does not exist');
8 end if;
9 end;
10 /
It does not exist
PL/SQL procedure successfully completed.
SQL>