Store multiple results from one field in new columns in SQL - sql

Current query returns 3 lines for one invoice if there are 3 denial codes and more if there are multiple denial codes and multiple denial dates. I am trying to create a column for each denial code so all results can be on one line. The requirements from the client is for each denial to be in its own column so I am unable to use the listagg function.
The results should looks like the below:
office, invoice, denial date, denial code 1, denial code 2, denial code 3, denial date2, denial code 1...etc
Oracle database. Current code:
SELECT
A.OFFICE_NBR,
A.INV_NBR,
TO_DATE(A.CRTD_DT,'MM/DD/YYYY') AS CARC_DT,
A.CLM_ID,
A.CLM_LN_ID,
A.RSN_CD
FROM DENIALS A
WHERE A.OFFICE_NBR = '1234'
AND A.INV_NBR = '123456'

I took a guess at some test data but this should get you going. Please add some before and after data, that helps a lot to understand what you are trying to do, and allows for building a more realistic solution.
Anyway the caveat is you have to define ahead of time a DENIAL_CD_X column for each possible denial code. I will be interested to see if someone can come up with a dynamic solution for the denial_cd columns.
SQL> with DENIALS(OFFICE_NBR, INV_NBR, CRTD_DT, RSN_CD) as (
2 select '11', '1111', '07/31/2015', '1' from dual
3 union
4 select '11', '1111', '07/31/2015', '99' from dual
5 union
6 select '11', '1111', '07/31/2015', '50' from dual
7 union
8 select '11', '1113', '06/01/2014', '34' from dual
9 union
10 select '11', '1113', '06/01/2014', '71' from dual
11 union
12 select '32', '3232', '06/21/2015', '34' from dual
13 union
14 select '32', '3232', '07/31/2015', '99' from dual
15 )
16 select OFFICE_NBR, INV_NBR, TO_DATE(CRTD_DT,'MM/DD/YYYY') AS CARC_DT,
17 DENIAL_CD_1, DENIAL_CD_2, DENIAL_CD_3
18 from
19 (
20 select OFFICE_NBR, INV_NBR, CRTD_DT, rsn_cd,
21 row_number() over(partition by OFFICE_NBR, INV_NBR, CRTD_DT order by OFFICE_NBR, INV_NBR, CRTD_DT, rsn_cd) rn
22 from DENIALS
23 )
24 pivot
25 (
26 max(rsn_cd)
27 for rn in ('1' as DENIAL_CD_1, '2' as DENIAL_CD_2, '3' as DENIAL_CD_3)
28 )
29 order by OFFICE_NBR, INV_NBR, CRTD_DT;
OFFICE_NBR INV_NBR CARC_DT DENIAL_CD_1 DENIAL_CD_2 DENIAL_CD_3
---------- ------- ---------- ----------- ----------- -----------
11 1111 31-JUL-15 1 50 99
11 1113 01-JUN-14 34 71
32 3232 21-JUN-15 34
32 3232 31-JUL-15 99
SQL>

Related

SQL Implementing Forward Fill logic

I have a dataset within a date range which has three columns, Product_type, date and metric. For a given product_type, data is not available for all days. For the missing rows, we would like to do a forward date fill for next n days using the last value of the metric.
Product_type
date
metric
A
2019-10-01
10
A
2019-10-02
12
A
2019-10-03
15
A
2019-10-04
5
A
2019-10-05
5
A
2019-10-06
5
A
2019-10-16
12
A
2019-10-17
23
A
2019-10-18
34
Here, the data from 2019-10-04 to 2019-10-06, has been forward filled. There might be bigger gaps in the dates, but we only want to fill the first n days.
Here, n=2, so rows 5 and 6 has been forward filled.
I am not sure how to implement this logic in SQL.
Here's one option. Read comments within code.
Sample data:
SQL> WITH
2 test (product_type, datum, metric)
3 AS
4 (SELECT 'A', DATE '2019-10-01', 10 FROM DUAL
5 UNION ALL
6 SELECT 'A', DATE '2019-10-02', 12 FROM DUAL
7 UNION ALL
8 SELECT 'A', DATE '2019-10-03', 15 FROM DUAL
9 UNION ALL
10 SELECT 'A', DATE '2019-10-04', 5 FROM DUAL
11 UNION ALL
12 SELECT 'A', DATE '2019-10-16', 12 FROM DUAL
13 UNION ALL
14 SELECT 'A', DATE '2019-10-18', 23 FROM DUAL),
Query begins here:
15 temp
16 AS
17 -- CB_FWD_FILL = 1 if difference between two consecutive dates is larger than 1 day
18 -- (i.e. that's the gap to be forward filled)
19 (SELECT product_type,
20 datum,
21 metric,
22 LEAD (datum) OVER (PARTITION BY product_type ORDER BY datum)
23 next_datum,
24 CASE
25 WHEN LEAD (datum)
26 OVER (PARTITION BY product_type ORDER BY datum)
27 - datum >
28 1
29 THEN
30 1
31 ELSE
32 0
33 END
34 cb_fwd_fill
35 FROM test)
36 -- original data from the table
37 SELECT product_type, datum, metric FROM test
38 UNION ALL
39 -- DATUM is the last date which is OK; add LEVEL pseudocolumn to it to fill the gap
40 -- with PAR_N number of rows
41 SELECT product_type, datum + LEVEL, metric
42 FROM (SELECT product_type, datum, metric
43 FROM (-- RN = 1 means that that's the first gap in data set - that's the one
44 -- that has to be forward filled
45 SELECT product_type,
46 datum,
47 metric,
48 ROW_NUMBER ()
49 OVER (PARTITION BY product_type ORDER BY datum) rn
50 FROM temp
51 WHERE cb_fwd_fill = 1)
52 WHERE rn = 1)
53 CONNECT BY LEVEL <= &par_n
54 ORDER BY datum;
Result:
Enter value for par_n: 2
PRODUCT_TYPE DATUM METRIC
--------------- ---------- ----------
A 2019-10-01 10
A 2019-10-02 12
A 2019-10-03 15
A 2019-10-04 5
A 2019-10-05 5 --> newly added
A 2019-10-06 5 --> rows
A 2019-10-16 12
A 2019-10-18 23
8 rows selected.
SQL>
Another solution:
WITH test (product_type, datum, metric) AS
(
SELECT 'A', DATE '2019-10-01', 10 FROM DUAL
UNION ALL
SELECT 'A', DATE '2019-10-02', 12 FROM DUAL
UNION ALL
SELECT 'A', DATE '2019-10-03', 15 FROM DUAL
UNION ALL
SELECT 'A', DATE '2019-10-04', 5 FROM DUAL
UNION ALL
SELECT 'A', DATE '2019-10-16', 12 FROM DUAL
UNION ALL
SELECT 'A', DATE '2019-10-18', 23 FROM DUAL
),
minmax(mindatum, maxdatum) AS (
SELECT MIN(datum), max(datum) from test
),
alldates (datum, product_type) AS
(
SELECT mindatum + level - 1, t.product_type FROM minmax,
(select distinct product_type from test) t
connect by mindatum + level <= (select maxdatum from minmax)
),
grouped as (
select a.datum, a.product_type, t.metric,
count(t.product_type) over(partition by a.product_type order by a.datum) as grp
from alldates a
left join test t on t.datum = a.datum
),
final_table as (
select g.datum, g.product_type, g.grp, g.rn,
last_value(g.metric ignore nulls) over(partition by g.product_type order by g.datum) as metric
from (
select g.*, row_number() over(partition by product_type, grp order by datum) - 1 as rn
from grouped g
) g
)
select datum, product_type, metric
from final_table
where rn <= &par_n
order by datum
;

How to write SQL Query with group by and having [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Closed 12 months ago.
I have this table which has three columns: number, status and unix_time
number,status,Unix_time
01,Y,112
02,Y,112
01,Y,114
03,,114
01,,115
02,Y,115
04,Y,115
04,Y,119
05,Y,119
06,Y,120
05,Y,120
06,,121
05,Y,121
NOTE: actual value of the column unix_time is in millisec since 1970
OUTPUT: all numbers, whose last appearance (based on timestamp), have status 'Y'
number
02
04
05
Kindly help me, how can I write an oracle SQL query
One option is to use row_number (or perhaps rank? Depends on whether there can be duplicates as far as time is concerned) analytic function (to find which row is the last per each num), and then - in main query - return only those rows whose status is Y.
Sample data:
SQL> WITH
2 test (num, status, time)
3 AS
4 (SELECT '01', 'Y', 112 FROM DUAL
5 UNION ALL
6 SELECT '02', 'Y', 112 FROM DUAL
7 UNION ALL
8 SELECT '01', 'Y', 114 FROM DUAL
9 UNION ALL
10 SELECT '03', '', 114 FROM DUAL
11 UNION ALL
12 SELECT '01', '', 115 FROM DUAL
13 UNION ALL
14 SELECT '02', 'Y', 115 FROM DUAL
15 UNION ALL
16 SELECT '04', 'Y', 115 FROM DUAL
17 UNION ALL
18 SELECT '04', 'Y', 119 FROM DUAL
19 UNION ALL
20 SELECT '05', 'Y', 119 FROM DUAL
21 UNION ALL
22 SELECT '06', 'Y', 120 FROM DUAL
23 UNION ALL
24 SELECT '05', 'Y', 120 FROM DUAL
25 UNION ALL
26 SELECT '06', '', 121 FROM DUAL
27 UNION ALL
28 SELECT '05', 'Y', 121 FROM DUAL),
Query begins here:
29 temp
30 AS
31 (SELECT num,
32 status,
33 time,
34 ROW_NUMBER () OVER (PARTITION BY num ORDER BY time DESC) rn
35 FROM test)
36 SELECT num
37 FROM temp
38 WHERE rn = 1
39 AND status = 'Y';
NU
--
02
04
05
SQL>

Need row data column wise in oracle

I need to compare prices coming from 3 sources (27, 2, 55) from owners column. i am not sure if i should use select sub query or left join ?
Desired Result
Looks like a pivot.
Sample data in lines #1 - 10; query you might need begins at line #12.
SQL> with test (id_value, as_of, timezone, price, owner) as
2 -- sample data
3 (select 'EEM.A', date '2021-06-25', 'J1530', 55.04, 55 from dual union all
4 select 'EEM.A', date '2021-06-25', 'J1530', 55.04, 27 from dual union all
5 select 'EEM.A', date '2021-06-25', 'J1530', 55.04, 2 from dual union all
6 --
7 select 'AMX.N', date '2021-06-25', 'J1530', 15.4, 55 from dual union all
8 select 'AMX.N', date '2021-06-25', 'J1530', 15.4, 27 from dual union all
9 select 'AMX.N', date '2021-06-25', 'J1530', 15.4, 2 from dual
10 )
11 -- query begins here
12 select *
13 from test
14 pivot (max(price)
15 for owner in (55, 27, 2)
16 );
ID_VA AS_OF TIMEZ 55 27 2
----- ---------- ----- ---------- ---------- ----------
AMX.N 06/25/2021 J1530 15,4 15,4 15,4
EEM.A 06/25/2021 J1530 55,04 55,04 55,04
SQL>

Oracle SQL - return the date record when there is no count result

I have the tables below and I need my query to bring me the amount of operations grouped by date.
For the dates on which there will be no operations, I need to return the date anyway with the zero count.
Kind like that:
OPERATION_DATE | COUNT_OPERATION | COUNT_OPERATION2 |
04/06/2019 | 453 | 81 |
05/06/2019 | 0 | 0 |
-- QUERY I TRIED
SELECT
T1.DATE_OPERATION AS DATE_OPERATION,
NVL(T1.COUNT_OPERATION, '0') COUNT_OPERATION,
NVL(T1.COUNT_OPERATION2, '0') COUNT_OPERATIONX,
FROM
(
SELECT
trunc(t.DATE_OPERATION) as DATE_OPERATION,
count(t.ID_OPERATION) AS COUNT_OPERATION,
COUNT(CASE WHEN O.OPERATION_TYPE = 'X' THEN 1 END) COUNT_OPERATIONX,
from OPERATION o
left join OPERATION_TYPE ot on ot.id_operation = o.id_operation
where ot.OPERATION_TYPE in ('X', 'W', 'Z', 'I', 'J', 'V')
and TRUNC(t.DATE_OPERATION) >= to_date('01/06/2019', 'DD-MM-YYYY')
group by trunc(t.DATE_OPERATION)
) T1
-- TABLES
CREATE TABLE OPERATION
( ID_OPERATION NUMBER NOT NULL,
DATE_OPERATION DATE NOT NULL,
VALUE NUMBER NOT NULL )
CREATE TABLE OPERATION_TYPE
( ID_OPERATION NUMBER NOT NULL,
OPERATION_TYPE VARCHAR2(1) NOT NULL,
VALUE NUMBER NOT NULL)
I guess that it is a calendar you need, i.e. a table which contains all dates involved. Otherwise, how can you display something that doesn't exist?
This is what you currently have (I'm using only the operation table; add another one yourself):
SQL> with
2 operation (id_operation, date_operation, value) as
3 (select 1, date '2019-06-01', 100 from dual union all
4 select 2, date '2019-06-01', 200 from dual union all
5 -- 02/06/2019 is missing
6 select 3, date '2019-06-03', 300 from dual union all
7 select 4, date '2019-06-04', 400 from dual
8 )
9 select o.date_operation,
10 count(o.id_operation)
11 from operation o
12 group by o.date_operation
13 order by o.date_operation;
DATE_OPERA COUNT(O.ID_OPERATION)
---------- ---------------------
01/06/2019 2
03/06/2019 1
04/06/2019 1
SQL>
As there are no rows that belong to 02/06/2019, query can't return anything (you already know that).
Therefore, add a calendar. If you already have that table, fine - use it. If not, create one. It is a hierarchical query which adds level to a certain date. I'm using 01/06/2019 as the starting point, creating 5 days (note the connect by clause).
SQL> with
2 operation (id_operation, date_operation, value) as
3 (select 1, date '2019-06-01', 100 from dual union all
4 select 2, date '2019-06-01', 200 from dual union all
5 -- 02/06/2019 is missing
6 select 3, date '2019-06-03', 300 from dual union all
7 select 4, date '2019-06-04', 400 from dual
8 ),
9 dates (datum) as --> this is a calendar
10 (select date '2019-06-01' + level - 1
11 from dual
12 connect by level <= 5
13 )
14 select d.datum,
15 count(o.id_operation)
16 from operation o full outer join dates d on d.datum = o.date_operation
17 group by d.datum
18 order by d.datum;
DATUM COUNT(O.ID_OPERATION)
---------- ---------------------
01/06/2019 2
02/06/2019 0 --> missing in source table
03/06/2019 1
04/06/2019 1
05/06/2019 0 --> missing in source table
SQL>
Probably a better option is to dynamically create a calendar so that it doesn't depend on any hardcoded values, but uses the min(date_operation) to max(date_operation) time span. Here we go:
SQL> with
2 operation (id_operation, date_operation, value) as
3 (select 1, date '2019-06-01', 100 from dual union all
4 select 2, date '2019-06-01', 200 from dual union all
5 -- 02/06/2019 is missing
6 select 3, date '2019-06-03', 300 from dual union all
7 select 4, date '2019-06-04', 400 from dual
8 ),
9 dates (datum) as --> this is a calendar
10 (select x.min_datum + level - 1
11 from (select min(o.date_operation) min_datum,
12 max(o.date_operation) max_datum
13 from operation o
14 ) x
15 connect by level <= x.max_datum - x.min_datum + 1
16 )
17 select d.datum,
18 count(o.id_operation)
19 from operation o full outer join dates d on d.datum = o.date_operation
20 group by d.datum
21 order by d.datum;
DATUM COUNT(O.ID_OPERATION)
---------- ---------------------
01/06/2019 2
02/06/2019 0 --> missing in source table
03/06/2019 1
04/06/2019 1
SQL>

Grouping SQL results by Year and count

I have a table with the below structure:
I would like to retrieve the results using sql in the below format
I am new to SQL and can't figure out how to go about it. Is this possible without using procedures? How do I go achieve this? (the actual data size is huge and I have given only a snapshot here)
Part of it is pivoting. Totals by row and column (and really, even the pivoting) should be done in your reporting application, not in SQL. If you insist on doing it in SQL, there are fancier ways, but something like the silly query below will suffice.
with test_data (city, yr, ct) as (
select 'Tokyo' , 2016, 2 from dual union all
select 'Mumbai', 2013, 3 from dual union all
select 'Mumbai', 2014, 5 from dual union all
select 'Dubai' , 2011, 5 from dual union all
select 'Dubai' , 2015, 15 from dual union all
select 'Dubai' , 2016, 8 from dual union all
select 'London', 2011, 16 from dual union all
select 'London', 2012, 22 from dual union all
select 'London', 2013, 4 from dual union all
select 'London', 2014, 24 from dual union all
select 'London', 2015, 13 from dual union all
select 'London', 2016, 5 from dual
),
test_with_totals as (
select city, yr, ct from test_data union all
select city, 9999, sum(ct) from test_data group by city union all
select 'Grand Total', yr , sum(ct) from test_data group by yr union all
select 'Grand Total', 9999, sum(ct) from test_data
)
select * from test_with_totals
pivot ( sum (ct) for yr in (2011, 2012, 2013, 2014, 2015, 2016, 9999 as "Total"))
order by "Total";
Result:
CITY 2011 2012 2013 2014 2015 2016 Total
----------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
Tokyo 2 2
Mumbai 3 5 8
Dubai 5 15 8 28
London 16 22 4 24 13 5 84
Grand Total 21 22 7 29 28 15 122