concatenate the output values in oracle plsql - oracle

create table item_value_tab
as
select 1110 Item_value,000394 bb_to_no, 55555 order_number, 'ABC' cust_no from dual
union all
select 1110 Item_value,000394 bb_to_no, 66666 order_number, 'ABC' cust_no from dual
union all
select 1110 Item_value,000394 bb_to_no, 77777 order_number, 'ABC' cust_no from dual
union all
select 1110 Item_value,000123 bb_to_no, 1111 order_number, 'ABC' cust_no from dual
union all
select 1110 Item_value,000124 bb_to_no, 2222 order_number, 'ABC' cust_no from dual
union all
select 1110 Item_value,000124 bb_to_no, 3333 order_number, 'ABC' cust_no from dual
union all
select 1110 Item_value,000011 bb_to_no, 75257 order_number, 'ABC' cust_no from dual
union all
select 1110 Item_value,000011 bb_to_no, 65257 order_number, 'ABC' cust_no from dual;
Take below code in Cursor 1
SELECT DISTINCT DECODE(nvl(Item_value,0),bb_to_no,null,'Item Value '||nvl(bb_to_no,0)||' Is not correct for order number '||order_number||' FOR Customer '||cust_no) vmsg
from item_value_tab;
Getting Output
Item Value 000394 Is not correct for order number 55555 FOR Customer ABC
Item Value 000394 Is not correct for order number 66666 FOR Customer ABC
Item Value 000394 Is not correct for order number 77777 FOR Customer ABC
Item Value 000123 Is not correct for order number 1111 FOR Customer ABC
Item Value 000124 Is not correct for order number 2222 FOR Customer ABC
Item Value 000124 Is not correct for order number 3333 FOR Customer ABC
Item Value 000011 Is not correct for order number 75257 FOR Customer ABC
Item Value 000011 Is not correct for order number 65257 FOR Customer ABC
-------------------------------------------------------------------------------
create table item_not_tab
as
select 0 item_id,53555 bb_to_no,1052 order_number,'AAA' cust_no from dual
union all
select 0 item_id,53655 bb_to_no,1138 order_number,'AAA' cust_no from dual;
Need to take the below code in cursor 2
select DECODE(nvl(item_id,0),0,'items not created for '||bb_to_no||'order number '||order_number||' FOR Customer '||cust_no,NULL) vmsg
from item_not_tab;
Getting Output
items not created for 53555 order number 1052 FOR Customer AAA
items not created for 53655 order number 1052 FOR Customer AAA
Expected output
Item Value Problems
Item Value 000394 Is not correct for order number 55555,66666,77777 FOR Customer ABC
Item Value 000123,000124 Is not correct for order number 1111 FOR Customer ABC
Item Value 000124 Is not correct for order number 2222,3333 FOR Customer ABC
Item Value 000011 Is not correct for order number 75257,65257 FOR Customer ABC
Items not found Problems
items not created for 53555,53655 order number 1052 FOR Customer AAA
note: please don't joins both tables in one cursor, I need to take 2 cursors. Both tables are same actually, but test case purpose I have as 2 d/f tables
oracle 11g 1.1 and 1.2

You need to first concatenate the tot_number inside the LOOP, and then use the value outside the LOOP.
For example,
SQL> SET serveroutput ON
SQL> DECLARE
2 v VARCHAR2(100);
3 BEGIN
4 FOR i IN
5 (SELECT 55555 str FROM dual
6 UNION
7 SELECT 66666 str FROM dual
8 UNION
9 SELECT 77777 str FROM dual
10 )
11 LOOP
12 v:= ltrim(v||','||i.str, ',');
13 END LOOP;
14 dbms_output.put_line('Output is concatenated '||v);
15 END;
16 /
Output is concatenated 55555,66666,77777
PL/SQL procedure successfully completed.
SQL>

Please Check the below QUERY :
SELECT DISTINCT o.bb_to_no,
(SELECT listagg(order_number,',') WITHIN GROUP (ORDER BY 1 desc)
FROM item_value_tab i WHERE o.bb_to_no = i.bb_to_no) AS total_num,
(SELECT DISTINCT(k.cust_no) FROM item_value_tab k
WHERE o.bb_to_no = k.bb_to_no) AS cust_name
FROM item_value_tab o
(OR)
EDIT : Please check this.
//It create collection of type table to store all the order_numbers
CREATE OR REPLACE TYPE t_varchar2_tab AS TABLE OF NUMBER;
// Below procedure will concatenate the list of order_numbers stored in
// the above created collection type.
CREATE OR REPLACE FUNCTION tab_to_string
(p_varchar2_tab IN t_varchar2_tab,
p_delimiter IN VARCHAR2 DEFAULT ',') RETURN VARCHAR2 IS
l_string VARCHAR2(32767);
BEGIN
FOR i IN p_varchar2_tab.FIRST .. p_varchar2_tab.LAST LOOP
IF i != p_varchar2_tab.FIRST THEN
l_string := l_string || p_delimiter;
END IF;
l_string := l_string || p_varchar2_tab(i);
END LOOP;
RETURN l_string;
END tab_to_string;
SELECT bb_to_no,cust_no,
tab_to_string(CAST(COLLECT(order_number) AS t_varchar2_tab))
AS tot_number
FROM item_value_tab
GROUP BY bb_to_no,cust_no;
P.S : you can find more information on COLLECT() function on below link.
http://www.oracle-developer.net/display.php?id=306

Related

Insert record from one table to another table - Oracle

I have a table TABLE1 which has 5 columns (ROLL_NO, NAME, UNITS, CODE, AMOUNT);
CREATE TABLE TABLE1 (ROLL_NO VARCHAR2(3), NAME VARCHAR2(4), UNITS NUMBER, AMOUNT NUMBER, CODE VARCHAR2(3));
------------------------------------------------------------------------------------------
INSERT INTO TABLE1 VALUES ('101', 'JOHN', 1, 6, 'ABC');
INSERT INTO TABLE1 VALUES ('101', 'JOHN', 2, 6, 'ABC');
INSERT INTO TABLE1 VALUES ('102', 'TOMS', 1, 7, 'ABC');
INSERT INTO TABLE1 VALUES ('102', 'TOMS', 6, 7, 'ABC');
INSERT INTO TABLE1 VALUES ('103', 'FINN', 1, 1, 'BCD');
ROLL_NO NAME UNITS AMOUNT CODE
-------------------------------------------------------
101 JOHN 1 6 ABC
101 JOHN 2 6 ABC
-------------------------------------------
102 TOMS 1 7 ABC
102 TOMS 6 7 ABC
103 FINN 1 1 BCD
There is second table TABLE2 where we need to insert data from TABLE1
CREATE TABLE TABLE2 (ROLL_NO VARCHAR2(3), NAME VARCHAR2(4), RESULT VARCHAR2(3));
There are three conditions to insert data into TABLE2
1st case : If CODE is 'ABC' and SUM(UNITS) of particular ROLL_NO is equal to AMOUNT then don't insert data into TABLE2
2nd case : If CODE is 'ABC' and SUM(UNITS) of particular ROLL_NO is not equal to AMOUNT then insert data with RESULT column value as 'YES'
3rd case : If CODE is not 'ABC' then RESULT column will be 'YES'.
Note: NAME, CODE and AMOUNT will be same for particular ROLL_NO though ROLL_NO has multiple UNITS.
Example :
ROLL_NO 102 CODE 'ABC' and two lines with SUM(UNITS) as 7 and its equal to AMOUNT i.e. 7 and (1st case)
ROLL_NO 101 has CODE 'ABC' and two lines with SUM(UNITS) as 3 and its not equal to AMOUNT i.e. 6 (2nd case)
ROLL_NO 103 has CODE 'BCD' which is not equal to 'ABC'(3rd case)
At the end TABLE2 should have
ROLL_NO NAME RESULT
-----------------------------
101 JOHN YES
103 FINN YES
I have tried this oracle query but it is inserting data related to 102 ROLL_NO which I don't need
SELECT T1.ROLL_NO, T1.NAME,
CASE
WHEN T1.CODE <> 'ABC' THEN 'YES'
WHEN T1.CODE = 'ABC' AND T2.TOT_UNITS <> T1.AMOUNT THEN 'YES'
END RESULT
FROM (SELECT DISTINCT ROLL_NO, NAME, AMOUNT, CODE
FROM TABLE1 ) T1
JOIN (SELECT ROLL_NO, SUM(UNITS) AS TOT_UNITS
FROM TABLE1
GROUP BY ROLL_NO) T2 ON T1.ROLL_NO = T2.ROLL_NO
I am not able to figure out how to not insert ROLL_NO 102 record into TABLE2..Can anyone provide better query than this if possible? Thank you
A "better" option is to scan table1 only once.
SQL> insert into table2 (roll_no, name, result)
2 with temp as
3 (select roll_no, name, sum(units) sum_units, amount, code,
4 case when code = 'ABC' and sum(units) = amount then 'NO'
5 when code = 'ABC' and sum(units) <> amount then 'YES'
6 else 'YES'
7 end as result
8 from table1
9 group by roll_no, name, amount, code
10 )
11 select roll_no, name, result
12 from temp
13 where result = 'YES';
2 rows created.
SQL> select * from table2;
ROL NAME RES
--- ---- ---
101 JOHN YES
103 FINN YES
SQL>

How to insert values to newly added column

I am new to oracle thus this question.
There is a table already existed and I have added a new column to it.
There are 5 rows and I do not want to use update table with where clause to insert the values one by one in the new column. Is there a statement like INSERT ALL to insert the values into the new column in one shot ?
Thanks
You can also use something like below which in-effect I would say is multiple update only, wrapped in single statement.
SQL> select * from test_upd;
ID1 ID2
---------- ----------
1
2
3
4
SQL> update test_upd a set a.id2 =
2 (select
3 case
4 when id1=1 then 100
5 when id1=2 then 200
6 when id1=3 then 300
7 else 5000 end
8 from test_upd b
9 where a.id1=b.id1);
4 rows updated.
SQL> select * from test_upd;
ID1 ID2
---------- ----------
1 100
2 200
3 300
4 5000
Use a MERGE statement:
MERGE INTO your_table dst
USING (
SELECT 1 AS id, 'aaa' AS newvalue FROM DUAL UNION ALL
SELECT 2, 'bbb' FROM DUAL UNION ALL
SELECT 3, 'ccc' FROM DUAL UNION ALL
SELECT 4, 'ddd' FROM DUAL UNION ALL
SELECT 5, 'eee' FROM DUAL
) src
ON (dst.id = src.id)
WHEN MATCHED THEN
UPDATE SET value = src.newvalue;
Which, if you have the table:
CREATE TABLE your_table (id, value) AS
SELECT 1, CAST(NULL AS VARCHAR2(3)) FROM DUAL UNION ALL
SELECT 2, NULL FROM DUAL UNION ALL
SELECT 3, NULL FROM DUAL UNION ALL
SELECT 4, NULL FROM DUAL UNION ALL
SELECT 5, NULL FROM DUAL;
Then, after the MERGE, the table contains:
ID
VALUE
1
aaa
2
bbb
3
ccc
4
ddd
5
eee
db<>fiddle here

Extract Data Between Brackets in Oracle SQL

I have a table with data in below format
COURSE
[]
["12345"]
["12345","7890"
I want to extract the data between [] but without "
So, my output would be in below format
COURSE
12345
12345, 7890
I tried the below code which works fine for first 3 rows
select REGEXP_SUBSTR (COURSE,
'"([^"]+)"',
1,
1,
NULL,
1) from TEST;
But 4th row only results in 12345.
Why not simple translate?
SQL> with test (course) as
2 (select '[]' from dual union
3 select null from dual union
4 select '["12345"]' from dual union
5 select '["12345","7890"]' from dual
6 )
7 select course,
8 translate(course, 'a[]"', 'a') result
9 from test;
COURSE RESULT
---------------- -----------------------------------------
["12345","7890"] 12345,7890
["12345"] 12345
[]
SQL>

How to sort texts with '_' in Oracle exactly like EXCEL?

In Excel When I sort the texts in ascending order, results shows as below. Text with the underscore character precedes the others. And in Excel cell, when I type in ="_" < "A", then "True" shows as expected.
C10_
C10A
C20_
C20A
But, In Oracle, when I sort in ascending order, results shows as below.
(I guess, Oracle treats '_' < 'A' False)
C10A
C10_
C20A
C20_
How can I make Oracle sort the list exactly as Excel does? I have changed ASC to DESC, but the result was not what I expect.
My sorting code is as below,
WITH DATAA AS (
SELECT *
FROM
(
SELECT 'C10_'rr FROM DUAL
UNION
SELECT 'C10A' rr FROM DUAL
UNION
SELECT 'C20_' rr FROM DUAL
UNION
SELECT 'C20A' rr FROM DUAL
)
)
SELECT *
FROM DATAA
ORDER BY rr ASC;
You can achieve this by altering the sorting method using NLS_SORT as following:
According to ORACLE Documentation:
NLS_SORT specifies the type of sort for character data. It overrides
the default value that is derived from NLS_LANGUAGE.
NLS_SORT contains either of the following values:
NLS_SORT = BINARY | sort_name
BINARY specifies a binary sort. sort_name specifies a linguistic sort
sequence.
Here is how you can achieve the result.
SQL> -- Your original query
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10A
C10_
C20A
C20_
--
SQL> -- Now altering the sorting method
SQL> --
SQL> --
SQL> alter session set NLS_SORT = German;
Session altered.
SQL> --
SQL> --
SQL> -- Now see the result
SQL> --
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10_
C10A
C20_
C20A
SQL>
Cheers!!

Select default in case of no value returned

I am trying to get some default value in my resultset if query does not return anything. I am trying nvl for the same but it is not returning the expected default value. To simulate, Consider following query,
select nvl(null, '10') from dual where 1=0;
I want to get 10 in case of given condition is not true and query does not return any value. However above query not returning any row.
Your query returns zero rows. NVL() isn't going to change that (*).
The correct solution is for the program which executes the query to handle NO_DATA_FOUND exception rather than fiddling the query.
However, you need a workaround so here is one using two sub-queries, one for your actual query, one to for the default.
When your_query returns an empty set you get this:
SQL> with your_qry as
2 ( select col1 from t42 where 1=0 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
10
SQL>
And when it returns a row you get this:
SQL> with your_qry as
2 ( select col1 from t42 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
12
13
SQL>
The WITH clause is optional here, it just makes it easier to write the query without duplication. This would have the same outcome:
select col1
from t42
where col0 is null
union all
select 10
from dual
where not exists (select col1
from t42
where col0 is null)
;
(*) Okay, there are solutions which use NVL() or COALESCE() with aggregations to do this. They work with single column projections in a single row as this question poses, but break down when the real query has more than one row and/or more than one column. Aggregations change the results.
So this looks alright ...
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual where 1=0 union all
3 select 'X' as col0, 13 as col1 from dual where 1=0 )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Y 10
SQL>
... but this not so much:
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual union all
3 select 'X' as col0, 13 as col1 from dual )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Z 13
SQL>
May be something like this is what you need
You could change WHERE clause (in this case WHERE COL > 1) similarly in both places.
WITH T(COL) AS(
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL
)
SELECT COL FROM T WHERE COL > 1
UNION ALL
SELECT 10 AS COL FROM DUAL WHERE NOT EXISTS( SELECT 1 FROM T WHERE COL > 1)
You can use aggregation. An aggregation query always returns one row:
select coalesce(max(null), '10')
from dual
where 1 = 0;
I prefer coalesce() to nvl() because coalesce() is the ANSI standard function. But, nvl() would work here just as well.

Resources