select * from co.orders
fetch first 20 rows only
ORDER_ID | ORDER_DATETIME | CUSTOMER_ID | ORDER_STATUS | STORE_ID |
---|---|---|---|---|
792 | 13-SEP-18 04.00.07.171604 AM | 271 | COMPLETE | 1 |
793 | 13-SEP-18 01.35.16.589378 PM | 189 | COMPLETE | 1 |
794 | 13-SEP-18 02.43.07.711020 PM | 326 | COMPLETE | 7 |
795 | 13-SEP-18 09.54.11.860987 PM | 33 | COMPLETE | 1 |
796 | 14-SEP-18 03.49.09.408125 AM | 124 | COMPLETE | 1 |
797 | 14-SEP-18 12.34.45.663632 PM | 71 | COMPLETE | 1 |
798 | 14-SEP-18 08.57.11.301929 PM | 263 | COMPLETE | 1 |
799 | 15-SEP-18 02.14.38.589428 AM | 332 | COMPLETE | 1 |
800 | 15-SEP-18 04.15.57.350477 AM | 136 | COMPLETE | 1 |
801 | 15-SEP-18 10.29.28.102273 AM | 344 | COMPLETE | 1 |
802 | 15-SEP-18 02.51.22.725333 PM | 381 | COMPLETE | 1 |
803 | 16-SEP-18 12.59.07.298134 AM | 299 | COMPLETE | 9 |
804 | 16-SEP-18 01.50.55.331837 AM | 107 | COMPLETE | 1 |
805 | 16-SEP-18 03.24.41.791705 AM | 182 | COMPLETE | 1 |
806 | 16-SEP-18 04.02.36.192734 PM | 63 | COMPLETE | 5 |
807 | 16-SEP-18 08.21.02.742335 PM | 289 | COMPLETE | 1 |
808 | 17-SEP-18 03.02.22.353313 PM | 14 | COMPLETE | 1 |
809 | 17-SEP-18 09.37.42.477631 PM | 326 | COMPLETE | 1 |
810 | 18-SEP-18 08.12.35.904514 AM | 66 | COMPLETE | 8 |
811 | 18-SEP-18 09.04.41.210375 PM | 37 | COMPLETE | 8 |
Pivot the number of orders for customers 1, 2, and 3
select * from co.orders
pivot (
count(*)
for customer_id
in ( 1, 2, 3 )
)
fetch first 20 rows only
ORDER_ID | ORDER_DATETIME | ORDER_STATUS | STORE_ID | 1 | 2 | 3 |
---|---|---|---|---|---|---|
810 | 18-SEP-18 08.12.35.904514 AM | COMPLETE | 8 | 0 | 0 | 0 |
814 | 19-SEP-18 10.58.39.958788 AM | COMPLETE | 1 | 0 | 0 | 0 |
828 | 21-SEP-18 06.03.49.739002 PM | COMPLETE | 3 | 0 | 0 | 0 |
841 | 24-SEP-18 01.28.43.783625 PM | COMPLETE | 5 | 0 | 0 | 0 |
847 | 25-SEP-18 02.00.27.715942 PM | COMPLETE | 8 | 0 | 0 | 0 |
849 | 26-SEP-18 04.29.39.472854 AM | COMPLETE | 1 | 0 | 0 | 0 |
873 | 30-SEP-18 09.49.22.678804 PM | COMPLETE | 8 | 0 | 0 | 0 |
878 | 01-OCT-18 05.03.41.096154 PM | COMPLETE | 1 | 0 | 0 | 0 |
881 | 02-OCT-18 04.58.55.715259 PM | COMPLETE | 1 | 0 | 0 | 0 |
889 | 03-OCT-18 07.59.51.998323 PM | COMPLETE | 5 | 0 | 0 | 0 |
893 | 05-OCT-18 11.23.42.224134 PM | COMPLETE | 1 | 0 | 0 | 0 |
898 | 06-OCT-18 08.22.00.585315 PM | COMPLETE | 4 | 0 | 0 | 0 |
922 | 10-OCT-18 11.46.09.768559 AM | COMPLETE | 1 | 0 | 0 | 0 |
950 | 15-OCT-18 05.58.32.401980 PM | COMPLETE | 1 | 0 | 0 | 0 |
966 | 18-OCT-18 08.28.06.850038 AM | COMPLETE | 1 | 0 | 0 | 0 |
987 | 23-OCT-18 01.49.54.173598 AM | COMPLETE | 1 | 0 | 0 | 0 |
1 | 04-FEB-18 01.20.22.245677 PM | CANCELLED | 1 | 0 | 0 | 1 |
14 | 24-FEB-18 05.50.02.729015 PM | COMPLETE | 1 | 0 | 0 | 0 |
19 | 01-MAR-18 06.09.26.634522 AM | COMPLETE | 1 | 0 | 0 | 0 |
20 | 01-MAR-18 10.07.32.467335 AM | COMPLETE | 3 | 0 | 0 | 1 |
Fitlering pivot output
select * from co.orders
pivot (
count(*)
for customer_id
in ( 1, 2, 3 )
)
where "1" > 0 or "2" > 0 or "3" > 0
ORDER_ID | ORDER_DATETIME | ORDER_STATUS | STORE_ID | 1 | 2 | 3 |
---|---|---|---|---|---|---|
1 | 04-FEB-18 01.20.22.245677 PM | CANCELLED | 1 | 0 | 0 | 1 |
20 | 01-MAR-18 10.07.32.467335 AM | COMPLETE | 3 | 0 | 0 | 1 |
1491 | 16-JAN-19 08.12.31.926232 AM | COMPLETE | 1 | 1 | 0 | 0 |
201 | 29-APR-18 11.48.01.437203 AM | COMPLETE | 1 | 1 | 0 | 0 |
766 | 09-SEP-18 11.33.57.269603 AM | COMPLETE | 1 | 0 | 0 | 1 |
1390 | 31-DEC-18 03.47.26.170374 PM | COMPLETE | 1 | 1 | 0 | 0 |
298 | 24-MAY-18 08.30.09.072186 PM | COMPLETE | 1 | 0 | 0 | 1 |
5 | 11-FEB-18 06.01.30.906119 PM | COMPLETE | 1 | 0 | 1 | 0 |
832 | 22-SEP-18 10.12.29.878125 AM | COMPLETE | 3 | 0 | 0 | 1 |
307 | 27-MAY-18 04.27.28.810727 AM | COMPLETE | 1 | 0 | 0 | 1 |
159 | 20-APR-18 12.51.57.254596 PM | COMPLETE | 1 | 1 | 0 | 0 |
544 | 27-JUL-18 05.27.59.314441 AM | COMPLETE | 1 | 0 | 0 | 1 |
1520 | 20-JAN-19 11.42.12.885651 PM | COMPLETE | 1 | 0 | 0 | 1 |
63 | 22-MAR-18 06.17.24.214468 AM | COMPLETE | 1 | 0 | 0 | 1 |
182 | 26-APR-18 03.56.02.680569 AM | COMPLETE | 1 | 1 | 0 | 0 |
608 | 11-AUG-18 06.57.30.697703 PM | COMPLETE | 1 | 0 | 0 | 1 |
765 | 09-SEP-18 08.32.41.319898 AM | COMPLETE | 2 | 0 | 1 | 0 |
Remove the implicit grouping
with rws as (
select customer_id from co.orders
where customer_id in ( 1, 2, 3 )
)
select * from rws
pivot (
count(*)
for customer_id
in ( 1 as "CUST_1", 2 as "CUST_2", 3 as "CUST_3" )
)
CUST_1 | CUST_2 | CUST_3 |
---|---|---|
5 | 2 | 10 |
Expressions disallowed for pivot column
select * from co.orders
pivot (
count(*)
for to_char ( order_datetime, 'MON' )
in ( 'Jan', 'Feb', 'Mar' )
)
ORA-01738: missing IN keywordMore Details: https://docs.oracle.com/error-help/db/ora-01738
Pivoting on an expression
with order_totals as (
select to_char ( o.order_datetime, 'MON' ) order_month
from co.orders o
)
select * from order_totals
pivot (
count (*)
for order_month in (
'JAN' JAN, 'FEB' FEB, 'MAR' MAR, 'APR' APR, 'MAY' MAY, 'JUN' JUN
)
)
JAN | FEB | MAR | APR | MAY | JUN |
---|---|---|---|---|---|
215 | 173 | 227 | 149 | 112 | 126 |
Adding implicit grouping
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
to_char ( o.order_datetime, 'MON' ) order_month
from co.orders o
)
select * from order_totals
pivot (
count (*)
for order_month in (
'JAN' JAN, 'FEB' FEB, 'MAR' MAR, 'APR' APR, 'MAY' MAY, 'JUN' JUN
)
)
-- group by order_year -- this is implicit
order by order_year
ORDER_YEAR | JAN | FEB | MAR | APR | MAY | JUN |
---|---|---|---|---|---|---|
2018 | 0 | 18 | 73 | 114 | 112 | 126 |
2019 | 215 | 155 | 154 | 35 | 0 | 0 |
Pivoting multiple aggregations
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
to_char ( o.order_datetime, 'MON' ) order_month,
customer_id
from co.orders o
)
select * from order_totals
pivot (
count (*) orders,
count ( distinct customer_id ) customers
for order_month in (
'JAN' JAN, 'FEB' FEB, 'MAR' MAR
)
)
order by order_year
ORDER_YEAR | JAN_ORDERS | JAN_CUSTOMERS | FEB_ORDERS | FEB_CUSTOMERS | MAR_ORDERS | MAR_CUSTOMERS |
---|---|---|---|---|---|---|
2018 | 0 | 0 | 18 | 15 | 73 | 60 |
2019 | 215 | 167 | 155 | 131 | 154 | 107 |
Pivoting multiple columns
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
to_char ( o.order_datetime, 'MON' ) order_month,
customer_id
from co.orders o
)
select * from order_totals
pivot (
count (*) orders
for ( customer_id, order_month ) in (
( 1, 'JAN' ) jan_cust_1,
( 1, 'FEB' ) feb_cust_1,
( 2, 'FEB' ) feb_cust_2,
( 3, 'MAR' ) mar_cust_3
)
)
order by order_year
ORDER_YEAR | JAN_CUST_1_ORDERS | FEB_CUST_1_ORDERS | FEB_CUST_2_ORDERS | MAR_CUST_3_ORDERS |
---|---|---|---|---|
2018 | 0 | 0 | 1 | 2 |
2019 | 1 | 0 | 0 | 0 |
Dynamic pivoting
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
to_char ( o.order_datetime, 'MON' ) order_month
from co.orders o
)
select * from order_totals
pivot (
count (*) orders
for order_month in (
select to_char ( add_months ( sysdate, level - 1 ), 'MON' )
from dual
connect by level <= 3
)
)
ORA-00936: missing expressionMore Details: https://docs.oracle.com/error-help/db/ora-00936
Dynamic XML pivoting
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
to_char ( o.order_datetime, 'MON' ) order_month
from co.orders o
)
select xmlserialize ( document order_month_xml indent size = 2 )
from order_totals
pivot xml (
count (*) orders
for order_month in (
select to_char ( add_months ( sysdate, level - 1 ), 'MON' )
from dual
connect by level <= 3
)
)
XMLSERIALIZE(DOCUMENTORDER_MONTH_XMLINDENTSIZE=2) |
---|
<PivotSet> <item> <column name="ORDER_MONTH">FEB</column> <column name="ORDERS">18</column> </item> <item> <column name="ORDER_MONTH">JAN</column> <column name="ORDERS">1</column> </item> <item> <column name="ORDER_MONTH">MAR</column> <column name="ORDERS">73</column> </item> </PivotSet> |
<PivotSet> <item> <column name="ORDER_MONTH">FEB</column> <column name="ORDERS">155</column> </item> <item> <column name="ORDER_MONTH">JAN</column> <column name="ORDERS">215</column> </item> <item> <column name="ORDER_MONTH">MAR</column> <column name="ORDERS">154</column> </item> </PivotSet> |
Dynamic pivoting with fixed columns
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
dense_rank () over ( -- careful with row_number() vs rank() vs dense_rank()!
partition by extract ( year from o.order_datetime )
order by to_char ( o.order_datetime, 'YYYYMM' )
) rn
from co.orders o
)
select * from order_totals
pivot (
count (*) orders
for rn in ( 1, 2, 3 )
)
ORDER_YEAR | 1_ORDERS | 2_ORDERS | 3_ORDERS |
---|---|---|---|
2019 | 215 | 155 | 154 |
2018 | 18 | 73 | 114 |
Dynamic pivoting with fixed columns and metadata
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
to_char ( o.order_datetime, 'MON' ) order_month,
dense_rank () over ( -- careful with row_number() vs rank() vs dense_rank()!
partition by extract ( year from o.order_datetime )
order by to_char ( o.order_datetime, 'YYYYMM' )
) rn
from co.orders o
)
select * from order_totals
pivot (
count (*) orders, max ( order_month ) mth
for rn in ( 1, 2, 3 )
)
ORDER_YEAR | 1_ORDERS | 1_MTH | 2_ORDERS | 2_MTH | 3_ORDERS | 3_MTH |
---|---|---|---|---|---|---|
2019 | 215 | JAN | 155 | FEB | 154 | MAR |
2018 | 18 | FEB | 73 | MAR | 114 | APR |
Dynamic pivoting using dynamic SQL
declare
pivot_values clob;
stmt clob;
cur sys_refcursor;
output_json varchar2(100);
begin
select listagg (
'''' || to_char ( add_months ( sysdate, level - 1 ), 'MON' ) || '''', ','
) within group ( order by 1 )
into pivot_values
from dual
connect by level <= 3;
stmt := q'!
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
to_char ( o.order_datetime, 'MON' ) order_month
from co.orders o
)
select json_object ( * ) from order_totals
pivot (
count (*) orders
for order_month in ( !' || pivot_values || ' )
)';
dbms_output.put_line ( stmt );
open cur for stmt;
loop
fetch cur into output_json;
exit when cur%notfound;
dbms_output.put_line ( output_json );
end loop;
close cur;
end;
Statement processed.
with order_totals as ( select extract ( year from o.order_datetime ) order_year, to_char ( o.order_datetime, 'MON' ) order_month from co.orders o ) select json_object ( * ) from order_totals pivot ( count (*) orders for order_month in ( 'JAN','FEB','MAR' ) )
{"ORDER_YEAR":2019,"'JAN'_ORDERS":215,"'FEB'_ORDERS":155,"'MAR'_ORDERS":154}
{"ORDER_YEAR":2018,"'JAN'_ORDERS":0,"'FEB'_ORDERS":18,"'MAR'_ORDERS":73}
Add row and column total columns
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
to_char ( o.order_datetime, 'MON' ) order_month
from co.orders o
)
select order_year, sum ( jan ), sum ( feb ), sum ( mar ),
sum ( jan ) + sum ( feb ) + sum ( mar ) year_tot
from order_totals
pivot (
count (*)
for order_month in (
'JAN' JAN, 'FEB' FEB, 'MAR' MAR
)
)
group by rollup ( order_year )
order by order_year
ORDER_YEAR | SUM(JAN) | SUM(FEB) | SUM(MAR) | YEAR_TOT |
---|---|---|---|---|
2018 | 0 | 18 | 73 | 91 |
2019 | 215 | 155 | 154 | 524 |
- | 215 | 173 | 227 | 615 |
Add row and column total columns - v2
with order_totals as (
select count (*) orders,
case grouping_id ( to_char ( o.order_datetime, 'MON' ) )
when 1 then 'Total'
else to_char ( o.order_datetime, 'MON' )
end order_month,
case grouping_id ( extract ( year from o.order_datetime ) )
when 1 then 'Total'
else to_char ( extract ( year from o.order_datetime ) )
end order_year
from co.orders o
group by rollup ( extract ( year from o.order_datetime ) ),
rollup ( to_char ( o.order_datetime, 'MON' ) )
)
select * from order_totals
pivot (
sum ( orders )
for order_month in (
'JAN' JAN, 'FEB' FEB, 'MAR' MAR, 'Total' tot
)
)
order by order_year
ORDER_YEAR | JAN | FEB | MAR | TOT |
---|---|---|---|---|
2018 | - | 18 | 73 | 1391 |
2019 | 215 | 155 | 154 | 559 |
Total | 215 | 173 | 227 | 1950 |
create table year_month_sales as
with order_totals as (
select extract ( year from o.order_datetime ) order_year,
to_char ( o.order_datetime, 'MON' ) order_month
from co.orders o
)
select * from order_totals
pivot (
count (*)
for order_month in (
'JAN' JAN, 'FEB' FEB, 'MAR' MAR, 'APR' APR, 'MAY' MAY, 'JUN' JUN,
'JUL' JUL, 'AUG' AUG, 'SEP' SEP, 'OCT' OCT, 'NOV' NOV, 'DEC' DEC
)
)
order by order_year
Table created.
select * from year_month_sales
ORDER_YEAR | JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT | NOV | DEC |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2018 | 0 | 18 | 73 | 114 | 112 | 126 | 123 | 146 | 161 | 172 | 155 | 191 |
2019 | 215 | 155 | 154 | 35 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Columns to rows
select * from year_month_sales
unpivot (
orders
for sale_month
in ( jan, feb, mar )
)
ORDER_YEAR | APR | MAY | JUN | JUL | AUG | SEP | OCT | NOV | DEC | SALE_MONTH | ORDERS |
---|---|---|---|---|---|---|---|---|---|---|---|
2018 | 114 | 112 | 126 | 123 | 146 | 161 | 172 | 155 | 191 | JAN | 0 |
2018 | 114 | 112 | 126 | 123 | 146 | 161 | 172 | 155 | 191 | FEB | 18 |
2018 | 114 | 112 | 126 | 123 | 146 | 161 | 172 | 155 | 191 | MAR | 73 |
2019 | 35 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | JAN | 215 |
2019 | 35 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | FEB | 155 |
2019 | 35 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | MAR | 154 |
Unpivoting columns with different data types
select * from co.orders
unpivot (
col_value
for col_name
in ( order_datetime, customer_id, order_status, store_id )
)
ORA-01790: expression must have same datatype as corresponding expressionMore Details: https://docs.oracle.com/error-help/db/ora-01790
Unpivoting columns with different data types
with rws as (
select order_id,
to_char ( order_datetime, 'yyyy-mm-dd' ) order_datetime,
to_char ( customer_id ) customer_id ,
order_status,
to_char ( store_id ) store_id
from co.orders
)
select * from rws
unpivot (
col_value
for col_name
in ( order_datetime, customer_id, order_status, store_id )
)
fetch first 20 rows only
ORDER_ID | COL_NAME | COL_VALUE |
---|---|---|
792 | ORDER_DATETIME | 2018-09-13 |
792 | CUSTOMER_ID | 271 |
792 | ORDER_STATUS | COMPLETE |
792 | STORE_ID | 1 |
793 | ORDER_DATETIME | 2018-09-13 |
793 | CUSTOMER_ID | 189 |
793 | ORDER_STATUS | COMPLETE |
793 | STORE_ID | 1 |
794 | ORDER_DATETIME | 2018-09-13 |
794 | CUSTOMER_ID | 326 |
794 | ORDER_STATUS | COMPLETE |
794 | STORE_ID | 7 |
795 | ORDER_DATETIME | 2018-09-13 |
795 | CUSTOMER_ID | 33 |
795 | ORDER_STATUS | COMPLETE |
795 | STORE_ID | 1 |
796 | ORDER_DATETIME | 2018-09-14 |
796 | CUSTOMER_ID | 124 |
796 | ORDER_STATUS | COMPLETE |
796 | STORE_ID | 1 |
Unpivot many columns
with rws as (
select order_year, jan, feb, mar, apr from year_month_sales
)
select * from rws
unpivot (
( month_1, month_2 )
for sale_month
in ( ( jan, feb ), ( mar, apr ) as 'Mar Apr' )
)
ORDER_YEAR | SALE_MONTH | MONTH_1 | MONTH_2 |
---|---|---|---|
2018 | JAN_FEB | 0 | 18 |
2018 | Mar Apr | 73 | 114 |
2019 | JAN_FEB | 215 | 155 |
2019 | Mar Apr | 154 | 35 |
Tranpose rows and columns
select * from year_month_sales
unpivot (
orders
for sale_month
in (
jan, feb, mar, apr, may, jun,
jul, aug, sep, oct, nov, dec
)
)
pivot (
sum ( orders )
for order_year in (
2018, 2019
)
)
SALE_MONTH | 2018 | 2019 |
---|---|---|
DEC | 191 | 0 |
APR | 114 | 35 |
JUN | 126 | 0 |
NOV | 155 | 0 |
AUG | 146 | 0 |
FEB | 18 | 155 |
JUL | 123 | 0 |
OCT | 172 | 0 |
MAR | 73 | 154 |
JAN | 0 | 215 |
MAY | 112 | 0 |
SEP | 161 | 0 |