Converting column data into row in pl/sql - sql

Hi When i went for an interview they asked me this question.
Create table course(Name CHAR(10));
insert into course values ('Java');
insert into course values ('Oracle');
insert into course values ('Python');
insert into course values ('C');
insert into course values ('C++');
o/p:
Java Oracle python c c++
Thanks in advance,
Sandhya.

I presume that NAME column's datatype should have been VARCHAR2, not CHAR.
Anyway, another option (similar to Tejash's LISTAGG) which uses XMLAGG and is safer if the result is larger than 4000 characters.
SQL> SELECT RTRIM (
2 XMLAGG (XMLELEMENT (e, name || ' ') ORDER BY null).EXTRACT (
3 '//text()'),
4 ',')
5 result
6 FROM course;
RESULT
------------------------------------------------------------
Java Oracle Python C C++
SQL>
Or, as you tagged the question with PL/SQL tag, then an anonymous PL/SQL block might look like this:
SQL> set serveroutput on
SQL>
SQL> declare
2 l_result varchar2(100);
3 begin
4 for cur_r in (select name from course) loop
5 l_result := l_result ||' '|| cur_r.name;
6 end loop;
7
8 dbms_output.put_line(trim(l_result));
9 end;
10 /
Java Oracle Python C C++
PL/SQL procedure successfully completed.
SQL>
Or - similarly - a function:
SQL> create or replace function f_course
2 return varchar2
3 is
4 l_result varchar2(100);
5 begin
6 for cur_r in (select name from course) loop
7 l_result := l_result ||' '|| cur_r.name;
8 end loop;
9
10 return trim(l_result);
11 end;
12 /
Function created.
SQL> select f_course from dual;
F_COURSE
--------------------------------------------------------------
Java Oracle Python C C++
SQL>
Or, a procedure with an OUT parameter:
SQL> create or replace procedure p_course (par_result out varchar2)
2 is
3 l_result varchar2(100);
4 begin
5 for cur_r in (select name from course) loop
6 l_result := l_result ||' '|| cur_r.name;
7 end loop;
8
9 par_result := trim(l_result);
10 end;
11 /
Procedure created.
SQL> declare
2 l_out varchar2(100);
3 begin
4 p_course(l_out);
5 dbms_output.put_line(l_out);
6 end;
7 /
Java Oracle Python C C++
PL/SQL procedure successfully completed.
SQL>
As you can see, quite a few options; use the one that most suits your needs.

You can use an aggregate function - LISTAGG as following:
SQL> SELECT
2 LISTAGG(TRIM(NAME), ' ') WITHIN GROUP(
3 ORDER BY
4 NULL
5 ) AS RESULT
6 FROM
7 COURSE;
RESULT
--------------------------------------------------------------------------------
C C++ Java Oracle Python
SQL>
Cheers!!

Related

Can we use %rowtype attribute inside the plsql record?

Can we use %rowtype attribute inside the plsql record like the below code..
type xx is RECORD
( v_emp employees%rowtype ,
v_loc departments.LOCATION_ID%type
);
v_data xx;
I can, and I hope you can too.
SQL> set serveroutput on
SQL>
SQL> declare
2 type xx is RECORD
3 ( v_emp emp%rowtype ,
4 v_loc dept.LOC%type );
5 v_data xx;
6 begin
7 v_data.v_emp.ename := 'LF';
8 v_data.v_loc := 'x';
9
10 dbms_output.put_line(v_data.v_emp.ename ||', '|| v_data.v_loc);
11 end;
12 /
LF, x
PL/SQL procedure successfully completed.
SQL>

Convert string from a table to be used as a column for selection for another table

I have a table where I store records to be used as a column name for my queries where the record is an actual column on another table.
TBL_1
COL_1
==========
SAMPLE_COL
TBL_2
SAMPLE_COL_1 SAMPLE_COL2
============ ===========
ABC DEF
I'm having a problem using the record that I fetched to use as an actual column. I already tried a bunch of things like casting and using case (using case works but it's a bit of a brute force and I'm looking for a more elegant way of doing this).
This is a sample query that I have tried:
SELECT (SELECT column_1 FROM tbl_1)
FROM tbl_2
Expected output
SAMPLE_COL_1
============
ABC
Actual output
(SELECT column_1 FROM tbl_1)
============================
SAMPLE_COL_1
This is what I've tried that worked so far but a brute force technique
SELECT (
CASE
WHEN (SELECT column_1 FROM tbl_2) = 'SAMPLE_COL_1' THEN SAMPLE_COL_1
ELSE SAMPLE_COL_2
END
)
FROM tbl_2
Appreciate the help! Keep safe from COVID-19 everyone :)
It's not that easy as you'd want it to be - you'll have to use dynamic SQL. Here's an example, based on Scott's table(s).
Create a function that accepts table and column names and returns ref cursor.
SQL> create or replace function f_test
2 (par_table_name in varchar2, par_column_name in varchar2)
3 return sys_refcursor
4 is
5 rc sys_refcursor;
6 begin
7 open rc for 'select ' || dbms_assert.simple_sql_name(par_column_name) ||
8 ' from ' || dbms_assert.sql_object_name(par_table_name);
9 return rc;
10 end;
11 /
Function created.
Testing:
SQL> select f_test('dept', 'dname') from dual;
F_TEST('DEPT','DNAME
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DNAME
--------------
ACCOUNTING
RESEARCH
SALES
OPERATIONS
SQL> select f_test('dual', 'dummy') from dual;
F_TEST('DUAL','DUMMY
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
D
-
X
SQL>
Another example, with column (and table) names stored in a table (something like you posted).
Table that contains those info and the function:
SQL> select * from tbl_1;
TNAM CNAME
---- -----
dept dname
dual dummy
SQL> create or replace function f_test
2 (par_table_name in varchar2)
3 return sys_refcursor
4 is
5 l_str varchar2(1000);
6 rc sys_refcursor;
7 begin
8 select 'select ' || dbms_assert.simple_sql_name(cname) ||
9 ' from ' || dbms_assert.sql_object_name(tname)
10 into l_str
11 from tbl_1
12 where tname = dbms_assert.sql_object_name(par_table_name);
13 open rc for l_str;
14 return rc;
15 end;
16 /
Function created.
Testing:
SQL> select f_test('dept') from dual;
F_TEST('DEPT')
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DNAME
--------------
ACCOUNTING
RESEARCH
SALES
OPERATIONS
SQL>

How to use 'sysdate' if its a string constant

I extract data from a table, the field is mostly null, but sometimes it's sysdate. However since its from a table, after getting the field its 'sysdate', between single quotes. How can I use it?
I have tried to_date, to_date(to_char()).
I need something that works within
select to_date('sysdate') from dual;
You can use a case expression:
select case
when the_column = 'sysdate' then sysdate
else to_date(the_column)
end as date_value
from the_table;
The only way I know is dynamic SQL. Here's an example:
SQL> create table test (id number, col varchar2(20));
Table created.
SQL> insert into test
2 select 1, '''sysdate''' from dual union all
3 select 2, null from dual;
2 rows created.
SQL> declare
2 l_res test%rowtype;
3 l_str varchar2(200);
4 begin
5 for cur_r in (select id, col from test) loop
6 l_str := 'select ' || cur_r.id ||', '||
7 nvl(replace(cur_r.col, chr(39), null), 'null') || ' from dual';
8 execute immediate l_str into l_res;
9 dbms_output.put_line(l_res.id ||': '|| l_res.col);
10 end loop;
11 end;
12 /
1: 24.06.2019 12:18:39
2:
PL/SQL procedure successfully completed.
SQL>

getting "comma-separated list near 'xx.yy' invalid" with dbms_utility.comma_to_table

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.

If I turn a collection of Number into a table, what's the name of the column? 10gR2

If I wanted to replace the * with a column name, what would it be?
create type mytable$t as table of number;
/
declare
mytmou mytable$t := myTable$T();
cnt pls_integer ;
begin
mytmou := myTable$T(1,2,3,4,5,6);
SELECT count(*) into cnt From Table (mytmou);
dbms_output.put_line(cnt);
end;
6
COLUMN_VALUE is the name of the column
SQL> ed
Wrote file afiedt.buf
1 declare
2 mytmou mytable$t := myTable$T();
3 cnt pls_integer ;
4 begin
5 mytmou := myTable$T(1,2,3,4,5,6);
6 SELECT count(column_value) into cnt From Table (mytmou);
7 dbms_output.put_line(cnt);
8* end;
SQL> /
6
PL/SQL procedure successfully completed.