how can sumvalue and addition null - oracle

How can I sum NULL values?
For example :
select sum(nvl(a,0) + nvl(b,0)) s, c from
(
select 1 a, null b, 'A' c from dual union
select null, null b , 'c' c from dual
)
group by c
Expected result: when a is NULL, b is NULL then sum(nvl(a,0) + nvl(b,0)) is NULL. How can I do this ?

Use a CASE statement to detect when both A and B are NULL:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE sample_data ( a, b, c ) AS
SELECT 1, CAST( NULL AS NUMBER ), 'A' FROM DUAL UNION ALL
SELECT NULL, NULL, 'C' FROM DUAL
Query 1:
SELECT SUM(
CASE
WHEN a IS NULL AND b IS NULL
THEN NULL
ELSE nvl(a,0) + nvl(b,0)
END
) AS s,
c
FROM sample_data
GROUP BY c
Results:
| S | C |
|--------|---|
| 1 | A |
| (null) | C |

You can try:
select Case when (sum(nvl(a,0) + nvl(b,0))) = 0 then null
else sum(nvl(a,0) + nvl(b,0)) end s, c
from
(
select 1 a, null b, 'A' c from dual union
select null, null b , 'c' c from dual
)
group by c

Related

multiple columns' row split in oracle

I got a single string row split into rows
For example,
A,B,C,D,E
into
A
B
C
D
E
but what I would like to is multiple columns' single row
| A,B,C | H,I,J,K,L | Q,R,X,Y,Z |
into
A | H | Q
B | I | R
C | J | X
| K | Y
| L |
How can I do this in oracle?
You can use hiearchy query as follows:
SQL> WITH DATAA ( D ) AS (
2 SELECT '| A,B,C | H,I,J,K,L | Q,R,X,Y,Z |'
3 FROM DUAL
4 )
5 -- your query starts from here
6 SELECT TRIM(REGEXP_SUBSTR(REGEXP_SUBSTR(D.D, '[^|]+', 1, 1), '[^,]+', 1, LEVEL)) AS COL1,
7 TRIM(REGEXP_SUBSTR(REGEXP_SUBSTR(D.D, '[^|]+', 1, 2), '[^,]+', 1, LEVEL)) AS COL2,
8 TRIM(REGEXP_SUBSTR(REGEXP_SUBSTR(D.D, '[^|]+', 1, 3), '[^,]+', 1, LEVEL)) AS COL3
9 FROM DATAA D
10 CONNECT BY LEVEL <= (
11 SELECT MAX(REGEXP_COUNT((REGEXP_SUBSTR(D.D, '[^|]+', 1, COLUMN_VALUE)), ',')) + 1
12 FROM DATAA D
13 CROSS JOIN TABLE ( CAST(MULTISET(
14 SELECT LEVEL LVL
15 FROM DUAL
16 CONNECT BY LEVEL <= REGEXP_COUNT(D.D, '[^|]+')
17 ) AS SYS.ODCIVARCHAR2LIST) ) LVLS
18 );
COL1 COL2 COL3
------- ----------- -----------
A H Q
B I R
C J X
K Y
L Z
SQL>

Oracle find the 1st node and last node in a path, CONNECT BY nocycle

I would like to know if there is anyway it return the beginning of the hierarchy and end of hierarchy for the code below
WITH o AS
(
SELECT 'A' as obj,
'C' as link
FROM dual
UNION ALL
SELECT 'C',
'D'
FROM dual
UNION ALL
SELECT 'D',
'E'
FROM dual
UNION ALL
SELECT 'X',
'Y'
FROM dual),
apath AS
(SELECT obj,
link
, sys_connect_by_path(obj || '->' || link, ',') pth
, connect_by_iscycle as cy
, connect_by_isleaf AS lf
, level
FROM o
CONNECT BY nocycle obj = PRIOR link)
SELECT *
FROM apath
where lf = 1
order by pth
I would like have the result like
Begin_Node, End_Node
A E
C E
D E
X Y
Current code returns
D E ,A->C,C->D,D->E 0 1 3
D E ,C->D,D->E 0 1 2
D E ,D->E 0 1 1
X Y ,X->Y 0 1 1
Thanks in advance.
Just use CONNECT_BY_ROOT and filter by CONNECT_BY_ISLEAF:
WITH o ( obj, link ) AS (
SELECT 'A', 'C' FROM DUAL UNION ALL
SELECT 'C', 'D' FROM DUAL UNION ALL
SELECT 'D', 'E' FROM DUAL UNION ALL
SELECT 'X', 'Y' FROM DUAL
)
SELECT CONNECT_BY_ROOT( obj ) AS begin_node,
link AS end_node
FROM o
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY nocycle obj = PRIOR link;
Outputs:
BEGIN_NODE | END_NODE
:--------- | :-------
A | E
C | E
D | E
X | Y
If you want all the columns then:
WITH o ( obj, link ) AS (
SELECT 'A', 'C' FROM DUAL UNION ALL
SELECT 'C', 'D' FROM dual UNION ALL
SELECT 'D', 'E' FROM dual UNION ALL
SELECT 'X', 'Y' FROM dual
)
SELECT CONNECT_BY_ROOT( obj ) AS begin_node,
link AS end_node,
SYS_CONNECT_BY_PATH( obj || '->' || link, ',' ) AS path,
CONNECT_BY_ISCYCLE as iscycle,
CONNECT_BY_ISLEAF AS isleaf,
level
FROM o
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY nocycle obj = PRIOR link;
Which outputs:
BEGIN_NODE | END_NODE | PATH | ISCYCLE | ISLEAF | LEVEL
:--------- | :------- | :-------------- | ------: | -----: | ----:
A | E | ,A->C,C->D,D->E | 0 | 1 | 3
C | E | ,C->D,D->E | 0 | 1 | 2
D | E | ,D->E | 0 | 1 | 1
X | Y | ,X->Y | 0 | 1 | 1
db<>fiddle here
If you're happy with what pth represents, it is simple to extract that info from it:
line #21: remove leading comma
lines #27 and #28 return begin and end nodes
<snip>
18 apath AS
19 (SELECT obj,
20 link
21 , ltrim(sys_connect_by_path(obj || '->' || link, ','), ',') pth
22 , connect_by_iscycle as cy
23 , connect_by_isleaf AS lf
24 , level
25 FROM o
26 CONNECT BY nocycle obj = PRIOR link)
27 SELECT regexp_substr(pth, '^\w+') begin_node,
28 regexp_substr(pth, '\w+$') end_node
29 FROM apath
30 where lf = 1
31 order by pth
32 /
BEGIN_NODE END_NODE
---------- ----------
A E
C E
D E
X Y
SQL>

how i need to pick values depending upon other values

I have a table with data shown below
no s d
100 I D
100 C D
101 C null
101 I null
102 C D
102 I null
then i'm using this query to partition
create table pinky nologging as
select no,status,dead
from(select no,status,dead,
row_number() over(partition by no order by dead desc) seq
from PINK) d
where seq = 1;
i'm getting this results
100 I D
101 C null
102 I null
but i want data like shown below
100 C D
101 I NULL
102 I NULL
i.e,
FOR I AND C COMBINATION and both d column is D then pick C
FOR I AND C COMBINATION and both d column is null then pick I
FOR I AND C COMBINATION and d column is null AND d then pick null corresponding value
Assuming that there can be only one dead is null record for each no:
with
-- you data, remove it when running the query
-- in your environment ...
pink (no, status, dead) as
(select 100, 'I', 'D' from dual union
select 100, 'C', 'D' from dual union
select 101, 'C', null from dual union
select 101, 'I', null from dual union
select 102, 'C', 'D' from dual union
select 102, 'I', null from dual
),
-- ... end of you data
--
temp as ( -- a temporary table (CTE) where we make some
-- preliminary calculations
select pink.*,
-- count rows with status = 'I' for this no
sum(case when status = 'I' then 1 else 0 end) over(partition by no) ni,
-- count rows with status = 'C' for this no
sum(case when status = 'C' then 1 else 0 end) over(partition by no) nc,
-- count rows with dead = 'D' for this no
sum(case when dead = 'D' then 1 else 0 end) over(partition by no) nd,
-- total number of rows (in case it's not always = 2)
count(*) over(partition by no) n
from pink
)
select no, status, dead
from temp
where -- pick 'C' if there's also 'I' and all dead = 'D'
status = 'C' and ni > 0 and nd = n
-- pick 'I' if there's also 'C' and all dead is null
or status = 'I' and nc > 0 and nd = 0
-- pick dead is null if there are I's and C's and
-- all other dead's = 'D'
or dead is null and ni > 0 and nc > 0 and n - nd = 1;

Age analysis on excel

I would like to distribute a set of data into a table shown below:
RANGE(days) No of days. Amount
0-30 - 0
31-180 1 4,185.78
181-365 2 74,056.86
366 and above 6 587,198.35
TOTAL 9 665,440.99
SOURCE Data
S/N START DATE Details Tran Amt END DATE
1 22/05/2015 A 448,749.14 30/06/2018
2 22/09/2015 B 4,883.02 30/06/2018
3 04/11/2015 C 45,646.27 30/06/2018
4 26/04/2016 D 42,861.99 30/06/2018
5 16/06/2016 E 23,144.23 30/06/2018
6 27/07/2016 F 21,913.70 30/06/2018
7 11/08/2017 G 61,396.94 30/06/2018
8 30/11/2017 H 12,659.92 30/06/2018
9 19/03/2018 I 4,185.78 30/06/2018
TOTAL 665,440.99
Thanks
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE data ( SN, START_DATE, Details, TranAmt ) AS
SELECT 1, DATE '2015-05-22', 'A', 448749.14 FROM DUAL UNION ALL
SELECT 2, DATE '2015-09-22', 'B', 4883.02 FROM DUAL UNION ALL
SELECT 3, DATE '2015-11-04', 'C', 45646.27 FROM DUAL UNION ALL
SELECT 4, DATE '2016-04-26', 'D', 42861.99 FROM DUAL UNION ALL
SELECT 5, DATE '2016-06-16', 'E', 23144.23 FROM DUAL UNION ALL
SELECT 6, DATE '2016-07-27', 'F', 21913.70 FROM DUAL UNION ALL
SELECT 7, DATE '2017-08-11', 'G', 61396.94 FROM DUAL UNION ALL
SELECT 8, DATE '2017-11-30', 'H', 12659.92 FROM DUAL UNION ALL
SELECT 9, DATE '2018-03-19', 'I', 4185.78 FROM DUAL;
Query 1:
WITH end_date ( end_date ) AS (
SELECT DATE '2018-06-30' FROM DUAL
),
ranges ( first, last ) AS (
SELECT 0, 30 FROM DUAL UNION ALL
SELECT 31, 180 FROM DUAL UNION ALL
SELECT 181, 365 FROM DUAL UNION ALL
SELECT 366, NULL FROM DUAL
)
SELECT NVL2(
r.first,
r.first || NVL2(
MAX( r.last ),
' - ' || MAX( r.last ),
' and above'
),
'Total'
) AS range,
COUNT( d.sn ) AS "No of days",
COALESCE( SUM( d.TranAmt ), 0 ) AS Amount
FROM data d
CROSS JOIN end_date e
RIGHT OUTER JOIN ranges r
ON ( d.start_date < e.end_date + 1 - r.first
AND ( r.last IS NULL
OR d.start_date >= e.end_date - r.last ) )
GROUP BY ROLLUP( r.first )
ORDER BY r.first
Results:
| RANGE | No of days | AMOUNT |
|---------------|------------|-----------|
| 0 - 30 | 0 | 0 |
| 31 - 180 | 1 | 4185.78 |
| 181 - 365 | 2 | 74056.86 |
| 366 and above | 6 | 587198.35 |
| Total | 9 | 665440.99 |

Oracle comma seperated to rows with grouping

I have the following requirement.
Do we have direct functions available in oracle 12c to accomplish this.
create table t1(input_name varchar2(500),input_values varchar2(500));
insert into t1 values('a,b,c,d,','1,2,3,4');
insert into t1 values('e,f,g,','5,6,7');
insert into t1 values('a1,b1,c1,d1,','11,12,13,14');
insert into t1 values('d,c,b,a,','100,200,300,400');
commit;
select * from t1;
INPUT_NAME INPUT_VALUES
------------------------------ ----------------
a,b,c,d, 1,2,3,4
e,f,g, 5,6,7
a1,b1,c1,d1, 11,12,13,14
d,c,b,a, 100,200,300,400
output:
a b c d e f g a1 b1 c1 d1
1 2 3 4 5 6 7 11 12 13 14
400 300 200 100
Thanks,
Rahmat Ali
Yes... if you have a known set of input names. But you would be better reorganising your data so that you are not storing correlated pairs of comma-separated lists.
SQL Fiddle
Oracle 11g R2 Schema Setup:
create table t1(input_name,input_values) AS
SELECT 'a,b,c,d,','1,2,3,4' FROM DUAL UNION ALL
SELECT 'e,f,g,','5,6,7' FROM DUAL UNION ALL
SELECT 'a1,b1,c1,d1,','11,12,13,14' FROM DUAL UNION ALL
SELECT 'd,c,b,a,','100,200,300,400' FROM DUAL
/
CREATE TYPE pair IS OBJECT(
name VARCHAR2(20),
value VARCHAR2(20)
)
/
CREATE TYPE pair_table IS TABLE OF PAIR
/
Query 1:
SELECT MAX( CASE name WHEN 'a' THEN value END ) AS a,
MAX( CASE name WHEN 'b' THEN value END ) AS b,
MAX( CASE name WHEN 'c' THEN value END ) AS c,
MAX( CASE name WHEN 'd' THEN value END ) AS d,
MAX( CASE name WHEN 'e' THEN value END ) AS e,
MAX( CASE name WHEN 'f' THEN value END ) AS f,
MAX( CASE name WHEN 'g' THEN value END ) AS g,
MAX( CASE name WHEN 'a1' THEN value END ) AS a1,
MAX( CASE name WHEN 'b1' THEN value END ) AS b1,
MAX( CASE name WHEN 'c1' THEN value END ) AS c1,
MAX( CASE name WHEN 'd1' THEN value END ) AS d1
FROM (
SELECT v.name,
v.value,
ROW_NUMBER() OVER ( PARTITION BY v.name ORDER BY ROWNUM ) AS rn
FROM t1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT pair(
REGEXP_SUBSTR( t.input_name, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 ),
REGEXP_SUBSTR( t.input_values, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 )
)
FROM DUAL
CONNECT BY level <= REGEXP_COUNT( t.input_name, '([^,]+)(,|$)' )
) AS pair_table
)
) v
)
GROUP BY rn
Results:
| A | B | C | D | E | F | G | A1 | B1 | C1 | D1 |
|-----|-----|-----|-----|--------|--------|--------|--------|--------|--------|--------|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 |
| 400 | 300 | 200 | 100 | (null) | (null) | (null) | (null) | (null) | (null) | (null) |
You can also use a PIVOT statement rather than multiple MAX( CASE ... END ) statements.
If you do not have a static set of input names then you will need to search for dynamic pivots.
Update:
Is there a way where I can avoid using types?
You can avoid creating types and just use a built-in VARRAY or collection like SYS.ODCIVARCHAR2LIST but then you will need two lists and it becomes complicated to correlate between the two.
WITH input_names ( rid, idx, name ) AS (
SELECT t.ROWID,
ROW_NUMBER() OVER ( PARTITION BY t.ROWID ORDER BY ROWNUM ) AS rn,
v.COLUMN_VALUE
FROM t1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.input_name, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 )
FROM DUAL
CONNECT BY level <= REGEXP_COUNT( t.input_name, '([^,]+)(,|$)' )
) AS SYS.ODCIVARCHAR2LIST
)
) v
),
input_values ( rid, idx, value ) AS (
SELECT t.ROWID,
ROW_NUMBER() OVER ( PARTITION BY t.ROWID ORDER BY ROWNUM ) AS rn,
v.COLUMN_VALUE
FROM t1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.input_values, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 )
FROM DUAL
CONNECT BY level <= REGEXP_COUNT( t.input_values, '([^,]+)(,|$)' )
) AS SYS.ODCIVARCHAR2LIST
)
) v
),
correlated ( name, value, rn ) AS (
SELECT n.name,
v.value,
ROW_NUMBER() OVER ( PARTITION BY n.name
ORDER BY ROWNUM )
FROM input_names n
INNER JOIN
input_values v
ON ( n.rid = v.rid AND n.idx = v.idx )
)
SELECT MAX( CASE name WHEN 'a' THEN value END ) AS a,
MAX( CASE name WHEN 'b' THEN value END ) AS b,
MAX( CASE name WHEN 'c' THEN value END ) AS c,
MAX( CASE name WHEN 'd' THEN value END ) AS d,
MAX( CASE name WHEN 'e' THEN value END ) AS e,
MAX( CASE name WHEN 'f' THEN value END ) AS f,
MAX( CASE name WHEN 'g' THEN value END ) AS g,
MAX( CASE name WHEN 'a1' THEN value END ) AS a1,
MAX( CASE name WHEN 'b1' THEN value END ) AS b1,
MAX( CASE name WHEN 'c1' THEN value END ) AS c1,
MAX( CASE name WHEN 'd1' THEN value END ) AS d1
FROM correlated
GROUP BY rn;

Resources