How do I write a cursor to return only one column that contains digit values - sql

For example I had a table named car with two column(col1,col2)
For now I would like insert some values inside the column like:
('Super car','Yellow car')
('BMW5','XL')
('Benz','AGM')
so I would like write a cursor to return ('BMW5','XL') in one single column, how do I do that?(I'm using sql developer)
I would appreciate any suggestion! Thank you!
declare
cursor mycursor is select concat(col1,col2) from car where REGEXP_LIKE(left, '^[[:digit:]]+$')
begin
for counter in mycursor
loop
dbms_output.put_line(counter.concat);
endloop;
end

You can use Concat() function in your query inside the cursor i.e.
CONCAT returns Col1 concatenated with Col2
select concat(col1,col2)
from car where <<conditions if any>>
You can write above query in Cursor like
DECLARE
CURSOR car_cursor IS select concat(col1,col2)
from car WHERE REGEXP_LIKE(col1, '[[:digit:]]');
cv_col1_col2 VARCHAR2 ;
BEGIN
OPEN car_cursor;
LOOP
FETCH car_cursor INTO cv_col1_col2;
Dbms_output.put_line('Concated Name' || cv_col1_col2)
END LOOP
CLOSE car_cursor;
END;

This is how I understood the question (though, more through by description than the title as they aren't related much).
Sample data:
SQL> create table car (col1 varchar2(10), col2 varchar2(10));
Table created.
SQL> insert into car
2 select 'Super car', 'Yellow car' from dual union all
3 select 'BMW5', 'XL' from dual union all
4 select 'Benz', 'AGM' from dual;
3 rows created.
PL/SQL code that uses a cursor FOR loop, returning concatenated col1 and col2 values for rows in which either of those columns contains a digit:
SQL> set serveroutput on;
SQL> begin
2 for cur_r in (select col1 ||' '|| col2 result
3 from car
4 where regexp_like(col1 || col2, '\d') -- any column contains
5 ) -- a digit
6 loop
7 dbms_output.put_line(cur_r.result);
8 end loop;
9 end;
10 /
BMW5 XL
PL/SQL procedure successfully completed.
SQL>

Related

How to return result of many select statements as one custom table

I have a table (let's name it source_tab) where I store list of all database tables that meet some criteria.
tab_name: description:
table1 some_desc1
table2 some_desc2
Now I need to execute a select statement on each of these tables and return a result as a table (I created custom TYPE). However I have a problem - when using bulk collect, only the last select statement is returned. The same issue was with open cursor. Is there any possibility to achieve this goal, another then concatenating all select statements using union all and executing it as one statement? And because I'm the begginer in sql, my second question is, is it ok to use this dynamic sql in terms of sql injection issues? Below is simplified version of my code:
CREATE OR REPLACE FUNCTION my_function RETURN newly_created_table_type IS
ret_tab_type newly_created_table_type;
BEGIN
for r in (select * from source_tab)
loop
execute immediate 'select value1, value2,''' || r.tab_name || ''' from ' || r.tab_name bulk collect into ret_tab_type;
end loop;
return ret_tab_type;
END;
I'm using Oracle 11.
In your case you are trying to populate a collection dynamically and wanted result in a single collection. In your case its not possible to do that in a single loop. Also as mentioned by #OldProgrammer, piperow would be a better solution from performance point. See below demo:
--Tables and Values:
CREATE TABLE SOURCE_TAB(TAB_NAME VARCHAR2(100), DESCRIPTION VARCHAR2(100));
/
SELECT * FROM SOURCE_TAB;
/
INSERT INTO SOURCE_TAB VALUES('table1','some_desc1');
INSERT INTO SOURCE_TAB VALUES('table2','some_desc2');
/
CREATE TABLE TABLE1(COL1 NUMBER, COL2 NUMBER);
/
INSERT INTO TABLE1 VALUES(1,2);
INSERT INTO TABLE1 VALUES(3,4);
INSERT INTO TABLE1 VALUES(5,6);
/
Select * from TABLE1;
/
CREATE TABLE TABLE2(COL1 NUMBER, COL2 NUMBER);
/
INSERT INTO TABLE2 VALUES(7,8);
INSERT INTO TABLE2 VALUES(9,10);
INSERT INTO TABLE2 VALUES(11,12);
/
Select * from TABLE2;
/
--Object Created
--UDT
CREATE OR REPLACE TYPE NEWLY_CREATED_TABLE_TYPE IS OBJECT (
VALUE1 NUMBER,
VALUE2 NUMBER
);
/
--Type of UDT
CREATE OR TYPE NEWLY_CRTD_TYP AS TABLE OF NEWLY_CREATED_TABLE_TYPE;
/
--Function:
--Function
CREATE OR REPLACE FUNCTION MY_FUNCTION
RETURN NEWLY_CRTD_TYP PIPELINED
AS
CURSOR CUR_TAB
IS
SELECT *
FROM SOURCE_TAB;
RET_TAB_TYPE NEWLY_CRTD_TYP;
BEGIN
FOR I IN CUR_TAB
LOOP
--Here i made sure that all the tables have col1 & col2 columns since you are using dynamic sql.
EXECUTE IMMEDIATE 'select NEWLY_CREATED_TABLE_TYPE(COL1, COL2) from '|| I.TAB_NAME
BULK COLLECT INTO RET_TAB_TYPE;
EXIT WHEN CUR_TAB%NOTFOUND;
FOR REC IN 1 .. RET_TAB_TYPE.COUNT
LOOP
PIPE ROW (RET_TAB_TYPE (REC) );
END LOOP;
END LOOP;
RETURN;
END;
/
Output:
SQL> Select * from table(MY_FUNCTION);
VALUE1 VALUE2
---------- ----------
1 2
3 4
5 6
7 8
9 10
11 12
6 rows selected.
May be you can combine all the queries into one using UNION ALL before execution, if the number and type of columns to be retrieved from all the tables are identical.
CREATE OR REPLACE FUNCTION my_function
RETURN newly_created_table_type
IS
ret_tab_type newly_created_table_type;
v_query VARCHAR2 (4000);
BEGIN
SELECT LISTAGG (' select VALUE1,VALUE2 FROM ' || tab_name, ' UNION ALL ')
WITHIN GROUP (ORDER BY tab_name)
INTO v_query
FROM source_tab;
EXECUTE IMMEDIATE v_query BULK COLLECT INTO ret_tab_type;
RETURN ret_tab_type;
END;
You could then use a single select statement to get all the values.
select * FROM TABLE ( my_function );

How to convert varchar2 to numbers in PL / SQL

I have a bash script in which I would like to do such a query in SqlPlus
CREATE TABLE tab1(digits VARCHAR2(100));
INSERT INTO tab1(digits) VALUES (5,6);
COMMIT;
#!/bin/bash
.
.
DECLARE
lv_digits VARCHAR2(100):='';
BEGIN
SELECT digits INTO digits FROM tab1;
FOR i IN (SELECT column1 FROM tab2 WHERE column_id IN (lv_digits) AS text);
LOOP
DBMS_OUTPUT.PUT_LINE(i.text);
END LOOP;
END;
/
I don't know how to properly enter a condition into WHERE to return a value
How to manually introduc WHERE column_id IN (5,6) AS text it works correctly and the value must be loaded from another table with a column that is VARCHAR2.
DECLARE
lv_digits VARCHAR2(100):='';
BEGIN
SELECT digits INTO digits FROM tab1;
FOR i IN (SELECT column1 FROM tab2 WHERE column_id IN (5,6) AS text);
LOOP
DBMS_OUTPUT.PUT_LINE(i.text);
END LOOP;
END;
/
Does anyone have any idea how to convert this?
This can be useful to someone
DECLARE
lv_digits VARCHAR2(100):='';
BEGIN
SELECT digits INTO digits FROM tab1;
FOR i IN (SELECT column1 FROM tab2 WHERE column_id IN (select regexp_substr(digits,'[^,]+', 1, level) from karuzela_loop
connect by regexp_substr(digits, '[^,]+', 1, level) is not NULL) AS text);
LOOP
DBMS_OUTPUT.PUT_LINE(i.text);
END LOOP;
END;
/
#mathguy thanks for the tips

Need help on writing sql query with dynamic columns

I have to write a query which does below. I tried but couldn't write. Please help me.
I have table which returns below result set.
select *
from table1; --(rowid and ColumnName are columns of the table)
Output:
rowid ColumnName
------------------------------
1 Segment1
2 Segment2
I have another table which has below structure : (Segment1 and Segment2 are columns here)
select *
from table2;
Output:
appId Segment1 Segment2 Segment3
---------------------------------------------
a1 fld1 fld2 per
a2 cmp1 hcd4 klp
I need to write a query, which reads the "ColumnName" values from first table and retrieves column values in the second table.
That means, from the table1, I will know what are the available columns I the table2 and from table2, I will know what is the data stored against those columns.
Please let me know if I am not clear.
This query is in Oracle SQL
As mentioned in the comment you need a PLSQL block with dynamic sql. See below an example:
Tables:
create table table1 (row_id number,
ColumnName varchar2(100))
create table table2 (appId number,
Segment1 varchar2(100),
Segment2 varchar2(100),
Segment3 varchar2(100));
Insert all
into TABLE1 (ROW_ID, COLUMNNAME) Values (1, 'Segment1')
into TABLE1 (ROW_ID, COLUMNNAME) Values (2, 'Segment2')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (1, 'RRR', 'KKK', 'MMM')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (2, 'ZZZ', 'PPP', 'QQQ')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (3, 'LLL', 'NNN', 'DDD')
select * from dual;
Code:
DECLARE
var VARCHAR2 (1000);
v_sql VARCHAR2 (2000);
TYPE x_var IS TABLE OF VARCHAR2(1000);
z_var x_var;
num number:=0;
BEGIN
FOR rec IN ( SELECT DISTINCT columnname
FROM table1
ORDER BY 1)
LOOP
num := num +1;
if num = 1 then
var:= rec.columnname;
else
var := var || ' || '' , ''||' || rec.columnname;
end if;
END LOOP;
var := RTRIM (LTRIM (var, ','), ',');
v_sql := 'select '|| var ||' from table2';
EXECUTE IMMEDIATE v_sql BULK COLLECT INTO z_var;
FOR i IN 1 .. z_var.COUNT
LOOP
DBMS_OUTPUT.put_line (z_var(i));
END LOOP;
END;
Output:
SQL> /
RRR , KKK
ZZZ , PPP
LLL , NNN
Dynamic columns in a SQL statement are almost always a bad idea. There's usually a way to avoid these kind of problems and build a simpler solution.
But if this is one of those rare times when you really need to run dynamic SQL in SQL then you'll need to install and run something like my open source project Method4.
For example:
create table table1 as
select 1 id, 'Segment1' columnName from dual union all
select 2 id, 'Segment2' columnName from dual;
create table table2 as
select 'a1' appId, 'fld1' Segment1, 'fld2' Segment2, 'per' Segment3 from dual union all
select 'a2' appId, 'cmp1' Segment1, 'hcd4' Segment2, 'klp' Segment3 from dual;
select * from table(method4.dynamic_query(
q'[
select
'select appID, '
||listagg(columnName, ',') within group (order by id)
||' from table2'
sql_statement
from table1
]'
));
APPID SEGMENT1 SEGMENT2
----- -------- --------
a1 fld1 fld2
a2 cmp1 hcd4
There are a lot of downsides to running this way. The code is complicated, slow, and has some odd behavior. For an explanation of how this works, see this article
by Adrian Billington.
Will the below PL SQL block help your requirement.
BEGIN
FOR iter IN (
SELECT column_name
FROM all_tab_columns
WHERE upper(table_name) = 'table1'
AND UPPER(column_name) LIKE 'SEGMENT%'
)
LOOP
SELECT iter.column_name INTO temp_table FROM table1
dbms_output.put_line(temp_table.column_name);
END LOOP;
END;
/
Say you have tables like the following:
SQL> select * from someTable;
COLUMN1 COLUMN2 COLUMN3
---------- ---------- ----------
1 2 3
2 4 6
3 6 9
SQL> select * from tableOfColumns;
COLUMNN
-------
column1
column3
You may need something like the following:
SQL> declare
2 type tListOfResults is table of varchar2(1000);
3 vSQL varchar2(1000);
4 vResult tListOfResults ;
5 begin
6 select 'select ' || listagg (columnName, ' || '', '' || ') within group (order by columnName) || ' from someTable'
7 into vSQL
8 from tableOfColumns;
9 --
10 execute immediate vSQL bulk collect into vResult;
11 if vResult.count() > 0 then
12 for i in vResult.first .. vResult.last loop
13 dbms_output.put_line(vResult(i));
14 end loop;
15 end if;
16 end;
17 /
1, 3
2, 6
3, 9
PL/SQL procedure successfully completed.

select multiple columns of single row as elements of array

I have a table with 100 columns called like col_1, col_2, .. col_100
is there a way to select values of that columns of single into an array of 100 elements?
(Oracle 10.2)
you could just select them like:
SQL> create type foo as table of number; -- or varray, as you wish.
2 /
Type created.
SQL> select foo(l.a, l.b, l.c) foo from your_tab l;
FOO
-----------------
FOO(1, 2, 3)
etc..
Here's a brute force method. There's probably a more elegant way, or at least one that will cut down on typing. The example uses five columns rather than 100.
DECLARE
-- Change VARCHAR2(10) in the next line to your col_1 .. col_100 type
TYPE My100Array IS TABLE OF VARCHAR2(10) INDEX BY PLS_INTEGER;
myVals My100Array;
indx NUMBER;
BEGIN
SELECT 'These', 'are', 'the', 'column', 'values'
INTO myVals(1), myVals(2), myVals(3), myVals(4), myVals(5)
FROM DUAL;
FOR INDX IN 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(indx || ': ' || myVals(indx));
END LOOP;
END;
Here's the output when I run this:
1: These
2: are
3: the
4: column
5: values
Of course, this will be a bit tough with 100 columns, but once you get the query out of the way you'll have the array as you want it.
Another example:
DECLARE
CURSOR c_data IS
SELECT * FROM scott.emp; -- replace emp table with your_table
TYPE t_source_tab IS TABLE OF scott.emp%ROWTYPE;
l_tab t_source_tab;
BEGIN
SELECT * BULK COLLECT INTO l_tab FROM scott.emp;
-- display values in array --
FOR i IN l_tab.FIRST ..l_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (l_tab(i).hiredate ||chr(9)||l_tab(i).empno ||chr(9)||l_tab(i).ename);
END LOOP;
END;
/
sounds like you want to unpivot your data..
unfortunately UNPIVOT was only added in 11g (not 10.2)
you could manually unpivot but one of the other solutions would work better i think.
However, if you were on 11g or later you could try this
create table my_table (col1 number,col2 number, col3 number);
Table MY_TABLE created.
insert into my_table values (4,5,6);
1 row inserted.
select * from my_table;
COL1 COL2 COL3
---------- ---------- ----------
4 5 6
select val from my_table unpivot ( val for col in ( col1,col2,col3));
VAL
----------
4
5
6
from there is trivial to select into an single column array
DECLARE
CURSOR c_data IS
select val from my_table unpivot ( val for col in ( col1,col2,col3));
TYPE t_source_tab IS TABLE OF c_data%ROWTYPE;
l_tab t_source_tab;
BEGIN
open c_data;
fetch c_data bulk collect into l_tab;
close c_data;
-- display values in array --
FOR i IN l_tab.FIRST ..l_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (l_tab(i).val);
END LOOP;
END;
/

Oracle stored procedure help

HI am new to stored procedure.
Am using a dynamic sql with
select * from table into var
var is a variable.the var contains more than one value ,when i try to run the proc with inputs i get an error:
ORA-01422: exact fetch returns more than requested number of rows
Is there a way such that the variable can hold more than one row using dynamic sql.
Use collection variables:
DECLARE
TYPE tt_int IS TABLE OF INTEGER;
var tt_int;
BEGIN
SELECT id
BULK COLLECT
INTO var
FROM table;
END;
You have to bulk collect the result set into a table of type if you're going to select multiple rows.
declare
type record_type is table of <table_name>;
var_records record_type;
begin
select *
bulk collect into var_records
from <table_name>;
end;
/
See also my answer to PL SQL how to select all columns
you can also return the results to a ref cursor
set serveroutput on
DECLARE
REFEXAMPLE SYS_REFCURSOR;
VAR NUMBER ;
col varchar2(50);
BEGIN
OPEN REFEXAMPLE FOR --Here you open the cursor and fill it
SELECT *
FROM (
SELECT 1 VAR, 'a' COL FROM DUAL
UNION ALL
SELECT 2 VAR, 'b' COL FROM DUAL
UNION ALL
SELECT 3 VAR, 'c' COL FROM DUAL
UNION ALL
SELECT 4 VAR, 'd' COL FROM DUAL
UNION ALL
SELECT 5 VAR, 'e' COL FROM DUAL
) EXAMPLETABLE ;
DBMS_OUTPUT.PUT_LINE('var ' || 'col');
DBMS_OUTPUT.PUT_LINE('---------');
LOOP
FETCH REFEXAMPLE INTO VAR, col; --now loop through
EXIT WHEN REFEXAMPLE%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(var || ' ' || col || ' ');
END LOOP;
CLOSE REFEXAMPLE;
end ;
/
var col
---------
1 a
2 b
3 c
4 D
5 e
You can use a table variable, which provides you with an in-memory table of your results. You declare a table variable similar to a standard variable:
declare #MyVar table (col1 col1type, col2 col2type, etc.)