Rollup - Oracle db sql - sql

I have the below table
i want to aggregate and rollup the data to be displayed per below screenshot.
How do i go about this, is it possible to exactly rollup this way?

I would use grouping sets:
select fruit, type, sum(amount), sum(percent)
from t
group by grouping sets ( (fruit, type), (fruit) );

You can use the ROLLUP and then necessary conditions to omit the extra generated rows as follows:
SQL> -- Sample data
SQL> WITH DATAA (FRUIT, TYPE, AMOUNT, PERCENT) AS
2 (
3 SELECT 'Apple', 'Green', 10017, 17 FROM DUAL UNION ALL
4 SELECT 'Orange', 'Green', 10016, 16 FROM DUAL UNION ALL
5 SELECT 'Papaya', 'Yellow', 10014, 14 FROM DUAL UNION ALL
6 SELECT 'Papaya', 'Blue', 10005, 5 FROM DUAL UNION ALL
7 SELECT 'Papaya', 'Green', 10012, 12 FROM DUAL
8 )
9 -- Your query starts from here
10 SELECT *
11 FROM (
12 SELECT FRUIT, TYPE, AMOUNT, SUM(PERCENT) AS PERCENT
13 FROM DATAA
14 GROUP BY ROLLUP(FRUIT, TYPE, AMOUNT)
15 )
16 WHERE ( FRUIT IS NOT NULL AND TYPE IS NOT NULL AND AMOUNT IS NOT NULL )
17 OR ( FRUIT IS NOT NULL AND TYPE IS NULL AND AMOUNT IS NULL )
18 ORDER BY FRUIT, TYPE DESC NULLS LAST;
FRUIT TYPE AMOUNT PERCENT
------ ------ ---------- ----------
Apple Green 10017 17
Apple 17
Orange Green 10016 16
Orange 16
Papaya Yellow 10014 14
Papaya Green 10012 12
Papaya Blue 10005 5
Papaya 31
8 rows selected.
SQL>

Related

Count the number of times word appears in a single column

I'm attempting to count the number of times apples and oranges appear in my fruit column.
The table looks like this:
Fruit
-------
Apples
Apples Oranges
Apples Oranges
Apples
Oranges
Expected output:
Apples 4
Oranges 3
My code thus far. I'm not sure how to do it when both appear and how to add them to the totals. I'm sure there is an easier way that this.
SELECT
COUNT (CASE WHEN Fruit LIKE '%Apples%' THEN '1' END) AS Apples
COUNT (CASE WHEN Fruit LIKE '%Oranges%' THEN '1' END) AS Oranges
FROM Fruits
Cheers
If those fruits are single-worded and separated by a space, then such a generic approach might be interesting for you.
Lines #1 - 8 represent sample data; you already have that so you don't type it. Code you might need starts at line #10.
SQL> with fruit (fruit) as
2 -- sample data; you have that in a table
3 (select 'Apples' from dual union all
4 select 'Apples Oranges' from dual union all
5 select 'Apples Oranges' from dual union all
6 select 'Apples Lemon' from dual union all
7 select 'Oranges Plums' from dual
8 ),
9 -- split fruits to rows
10 temp as
11 (select regexp_substr(fruit, '[^ ]+', 1, column_value) fruit
12 from fruit cross join
13 table(cast(multiset(select level from dual
14 connect by level <= regexp_count(fruit, ' ') + 1
15 ) as sys.odcinumberlist))
16 )
17 select fruit, count(*)
18 from temp
19 group by fruit
20 order by fruit;
FRUIT COUNT(*)
-------------------------------------------------------- ----------
Apples 4
Lemon 1
Oranges 3
Plums 1
SQL>
Either with sum or count, it works
Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
SQL> with t as
(
select 'Apples' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples' as fruits from dual union all
select 'Oranges' as fruits 6 from dual
) select
SUM (CASE WHEN fruits LIKE '%Apples%' THEN '1' END) AS Apples ,
SUM (CASE WHEN fruits LIKE '%Oranges%' THEN '1' END) AS Oranges
FROM t
;
APPLES ORANGES
---------- ----------
4 3
SQL> with t as
(
select 'Apples' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples' as fruits from dual union all
select 'Oranges' as fruits from dual
) select
COUNT (CASE WHEN fruits LIKE '%Apples%' THEN '1' END) AS Apples ,
COUNT (CASE WHEN fruits LIKE '%Oranges%' THEN '1' END) AS Oranges
FROM t ;
APPLES ORANGES
---------- ----------
4 3
SQL>

Transpose in ORACLE SQL (Convert 6 columns into 3 Columns)

I've an output like the below.
I want that to be converted into the below
Where ID is the count of TOTAL, HIGH, SENT, WAITING, DATE. I have been contemplating for a while and couldn't get what I want. Can any one please help in ORACLE SQL?
Thanks in advance.
Here's one option:
SQL> with test (cdate, total, high, sent, waiting, loc) as
2 (select 1012018, 23, 4, 35, 45, 13456 from dual union all
3 select 1212018, 74, 2, 77, 82, 98765 from dual
4 ),
5 temp as
6 (select 5 rn, loc, cdate as value from test union all
7 select 1 rn, loc, total from test union all
8 select 2 rn, loc, high from test union all
9 select 3 rn, loc, sent from test union all
10 select 4 rn, loc, waiting from test
11 )
12 select rn, value, loc
13 from temp
14 order by loc, rn;
RN VALUE LOC
---------- ---------- ----------
1 23 13456
2 4 13456
3 35 13456
4 45 13456
5 1012018 13456
1 74 98765
2 2 98765
3 77 98765
4 82 98765
5 1212018 98765
10 rows selected.
SQL>

Oracle SQL - loop through unique values in one column to concatenate values from a second column

I have a table like this one
ID _______ LEN
PK01 10
PK01 20
PK01 30
PK05 10
PK05 70
PK11 50
PK11 70
PK11 110
PK11 150
I want to write a query that will return results like this:
ID _______ LEN
PK01 10, 20, 30
PK05 10, 70
PK11 50, 70, 110, 150
NOTES ON DATA:
As above, in my table `ID`s can be ordered but there are gaps between `ID`s
As above, `ID:LEN` is a `1:many` relationship
As above, different `ID`s can have a record with the same `LEN` value(s).
The table is reference data. Each `ID:LEN` combination constitutes 1 unique row. Neither field is nullable.
NOTE ON RESULTS:
This will be for a report (data extract).
The ALL LENS can be a concatenation of TO_CHAR(LEN) values.
I write a lot of SQL select statements. I'm just struggling with going to the next level: how to loop through IDs to concatenate LEN values for each ID group.
Nowadays, LISTAGG would be your choice:
SQL> with test (id, len) as
2 (select 'PK01', 10 from dual union all
3 select 'PK01', 20 from dual union all
4 select 'PK01', 30 from dual union all
5 select 'PK05', 10 from dual union all
6 select 'PK05', 70 from dual
7 )
8 select id,
9 listagg(len, ', ') within group (order by len) len
10 from test
11 group by id
12 order by id;
ID LEN
---- ------------------------------
PK01 10, 20, 30
PK05 10, 70
SQL>
If your database version doesn't support it (or the result exceeds 4000 characters), see whether XMLAGG helps:
SQL> with test (id, len) as
2 (select 'PK01', 10 from dual union all
3 select 'PK01', 20 from dual union all
4 select 'PK01', 30 from dual union all
5 select 'PK05', 10 from dual union all
6 select 'PK05', 70 from dual
7 )
8 select id,
9 rtrim(xmlagg(xmlelement(e, len ||', ')
10 order by len).extract ('//text()'), ', ') len
11 from test
12 group by id
13 order by id;
ID LEN
---- ------------------------------
PK01 10, 20, 30
PK05 10, 70
SQL>
If your Oracle version is 11.2.x or newer, you can try this:
SELECT ID "ID", LISTAGG(LEN, ', ') WITHIN GROUP(ORDER BY LEN) "LEN"
FROM <table_name>
GROUP BY id
ORDER BY id;
Hope this helps.

Oracle SQL query previous row result

I am just stuck now, now getting the logic to solve this query. Find the below tables and the output. There is Table A and table B which matches two column ID and DATE. If Date got matched then it should multiply the qty with percent else it should pick previous percent.
Table A Table B
ID Date Percent ID Date Qty
A 01/01/17 0.5 A 01/01/17 10
A 04/01/17 1 A 02/01/17 20
A 06/01/17 2 A 03/01/17 30
B 02/01/17 5 A 05/01/17 40
B 05/01/17 10 A 06/01/17 50
A 07/01/17 60
A 08/01/17 40
B 01/01/17 10
B 02/01/17 50
============================================
column column column comment comment comment
ID Date Qty Previous percent if row not matched
A 01/01/17 0.5 * 10 0.5 got new percent
A 02/01/17 0.5 * 20 0.5
A 03/01/17 0.5 * 30 0.5
A 04/01/17 1* 0 1 got new percent but no qty found
A 05/01/17 1 * 40 1
A 06/01/17 2 * 50 2 got new percent
B 01/01/17 10 * 0 0 no percent found
B 02/02/17 5 * 10 5 got new percent
B 5/1/17 10 * 0 10 got new percent
I interpreted the problem to mean "add a few more columns to Table B" - to show the "most current" percentage, showing its date from Table A, and the "gross quantity" from Table B and the "net quantity" by multiplying by the proper percentage.
If you also need one row for every row in Table A, simply delete the WHERE clause from the outermost query (towards the bottom of the query).
with table_a ( id, dt, pct ) as (
select 'A', to_date('01/01/17', 'mm/dd/rr'), 0.5 from dual union all
select 'A', to_date('04/01/17', 'mm/dd/rr'), 1 from dual union all
select 'A', to_date('06/01/17', 'mm/dd/rr'), 2 from dual union all
select 'B', to_date('02/01/17', 'mm/dd/rr'), 5 from dual union all
select 'B', to_date('05/01/17', 'mm/dd/rr'), 10 from dual
), table_b ( id, dt, qty ) as (
select 'A', to_date('01/01/17', 'mm/dd/rr'), 10 from dual union all
select 'A', to_date('02/01/17', 'mm/dd/rr'), 20 from dual union all
select 'A', to_date('03/01/17', 'mm/dd/rr'), 30 from dual union all
select 'A', to_date('05/01/17', 'mm/dd/rr'), 40 from dual union all
select 'A', to_date('06/01/17', 'mm/dd/rr'), 50 from dual union all
select 'A', to_date('07/01/17', 'mm/dd/rr'), 60 from dual union all
select 'A', to_date('08/01/17', 'mm/dd/rr'), 40 from dual union all
select 'B', to_date('01/01/17', 'mm/dd/rr'), 10 from dual union all
select 'B', to_date('02/01/17', 'mm/dd/rr'), 50 from dual
)
-- end of test data (not part of the SQL query); query begins BELOW THIS LINE
select id, qty_date, pct_date, qty as gross_qty, pct, qty * pct as net_qty
from ( select id, dt as qty_date,
last_value(case flag when 0 then dt end ignore nulls)
over (partition by id order by dt) as pct_date,
last_value(pct ignore nulls)
over (partition by id order by dt, flag) as pct,
qty, flag
from ( select id, dt, pct, null as qty, 0 as flag
from table_a
union all
select id, dt, null as pct, qty, 1 as flag
from table_b
)
)
where flag = 1
order by id, qty_date -- if needed
;
Output:
ID QTY_DATE PCT_DATE GROSS_QTY PCT NET_QTY
-- ---------- ---------- ---------- ---------- ----------
A 2017-01-01 2017-01-01 10 .5 5
A 2017-02-01 2017-01-01 20 .5 10
A 2017-03-01 2017-01-01 30 .5 15
A 2017-05-01 2017-04-01 40 1 40
A 2017-06-01 2017-06-01 50 2 100
A 2017-07-01 2017-06-01 60 2 120
A 2017-08-01 2017-06-01 40 2 80
B 2017-01-01 10
B 2017-02-01 2017-02-01 50 5 250
9 rows selected.

How to use distinct and sum both together in oracle?

For example my table contains the following data:
ID price
-------------
1 10
1 10
1 20
2 20
2 20
3 30
3 30
4 5
4 5
4 15
So given the example above,
ID price
-------------
1 30
2 20
3 30
4 20
-----------
ID 100
How to write query in oracle? first sum(distinct price) group by id then sum(all price).
I would be very careful with a data structure like this. First, check that all ids have exactly one price:
select id
from table t
group by id
having count(distinct price) > 1;
I think the safest method is to extract a particular price for each id (say the maximum) and then do the aggregation:
select sum(price)
from (select id, max(price) as price
from table t
group by id
) t;
Then, go fix your data so you don't have a repeated additive dimension. There should be a table with one row per id and price (or perhaps with duplicates but controlled by effective and end dates).
The data is messed up; you should not assume that the price is the same on all rows for a given id. You need to check that every time you use the fields, until you fix the data.
first sum(distinct price) group by id then sum(all price)
Looking at your desired output, it seems you also need the final sum(similar to ROLLUP), however, ROLLUP won't directly work in your case.
If you want to format your output in exactly the way you have posted your desired output, i.e. with a header for the last row of total sum, then you could set the PAGESIZE in SQL*Plus.
Using UNION ALL
For example,
SQL> set pagesize 7
SQL> WITH DATA AS(
2 SELECT ID, SUM(DISTINCT price) AS price
3 FROM t
4 GROUP BY id
5 )
6 SELECT to_char(ID) id, price FROM DATA
7 UNION ALL
8 SELECT 'ID' id, sum(price) FROM DATA
9 ORDER BY ID
10 /
ID PRICE
--- ----------
1 30
2 20
3 30
4 20
ID PRICE
--- ----------
ID 100
SQL>
So, you have an additional row in the end with the total SUM of price.
Using ROLLUP
Alternatively, you could use ROLLUP to get the total sum as follows:
SQL> set pagesize 7
SQL> WITH DATA AS
2 ( SELECT ID, SUM(DISTINCT price) AS price FROM t GROUP BY id
3 )
4 SELECT ID, SUM(price) price
5 FROM DATA
6 GROUP BY ROLLUP(id);
ID PRICE
---------- ----------
1 30
2 20
3 30
4 20
ID PRICE
---------- ----------
100
SQL>
First do the DISTINCT and then a ROLLUP
SELECT ID, SUM(price) -- sum of the distinct prices
FROM
(
SELECT DISTINCT ID, price -- distinct prices per ID
FROM tab
) dt
GROUP BY ROLLUP(ID) -- two levels of aggregation, per ID and total sum
SELECT ID,SUM(price) as price
FROM
(SELECT ID,price
FROM TableName
GROUP BY ID,price) as T
GROUP BY ID
Explanation:
The inner query will select different prices for each ids.
i.e.,
ID price
-------------
1 10
1 20
2 20
3 30
4 5
4 15
Then the outer query will select SUM of those prices for each id.
Final Result :
ID price
----------
1 30
2 20
3 30
4 20
Result in SQL Fiddle.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE MYTABLE ( ID, price ) AS
SELECT 1, 10 FROM DUAL
UNION ALL SELECT 1, 10 FROM DUAL
UNION ALL SELECT 1, 20 FROM DUAL
UNION ALL SELECT 2, 20 FROM DUAL
UNION ALL SELECT 2, 20 FROM DUAL
UNION ALL SELECT 3, 30 FROM DUAL
UNION ALL SELECT 3, 30 FROM DUAL
UNION ALL SELECT 4, 5 FROM DUAL
UNION ALL SELECT 4, 5 FROM DUAL
UNION ALL SELECT 4, 15 FROM DUAL;
Query 1:
SELECT COALESCE( TO_CHAR(ID), 'ID' ) AS ID,
SUM( PRICE ) AS PRICE
FROM ( SELECT DISTINCT ID, PRICE FROM MYTABLE )
GROUP BY ROLLUP ( ID )
ORDER BY ID
Results:
| ID | PRICE |
|----|-------|
| 1 | 30 |
| 2 | 20 |
| 3 | 30 |
| 4 | 20 |
| ID | 100 |