I have a table that is layed out as follows :
Code,Year, Wk1Val, Wk2Val, Wk3Val, Wk4Val etc to to Wk52Val.
Is there SQL to show the data as follows, without having to do 52 Case statements :
Code, Year, Week, Value
You can use UNPIVOT with REPLACE functions. Be sure to add all 52 week columns to the UNPIVOT IN portion of the query.
SELECT
u.Code,
u.Year,
REPLACE(REPLACE(u.Weeks, 'Wk', ''), 'Val', '') AS Weeks,
u.Value
FROM test
UNPIVOT
(
Value FOR Weeks IN (Wk1Val, Wk2Val, Wk3Val, Wk4Val, Wk52Val)
) AS u;
Fiddle here.
Input:
Code
Year
Wk1Val
Wk2Val
Wk3Val
Wk4Val
Wk52Val
1
2022
5000
15000
9000
4000
1000
2
2022
900
3200
7000
8500
9500
3
2022
3500
7800
10000
6200
5600
Output:
Code
Year
Weeks
Value
1
2022
1
5000
1
2022
2
15000
1
2022
3
9000
1
2022
4
4000
1
2022
52
1000
2
2022
1
900
2
2022
2
3200
2
2022
3
7000
2
2022
4
8500
2
2022
52
9500
3
2022
1
3500
3
2022
2
7800
3
2022
3
10000
3
2022
4
6200
3
2022
52
5600
Related
I have a Table called TaxAmount. It has 3 columns(ID, Year, Amount). refer the below image.
I want to divide each row into 12 months. I attached a sample image below.
I'm new in Oracle side. please help me to write a Oracle Query to display the above result.
I tried ROWNUM. But No luck.
Here's one option:
SQL> select id, year, column_value as month, amount
2 from taxamount cross join
3 table(cast(multiset(select level from dual
4 connect by level <= 12
5 ) as sys.odcinumberlist))
6 order by id, year, month;
ID YEAR MONTH AMOUNT
---------- ---------- ---------- ----------
1 2022 1 100
1 2022 2 100
1 2022 3 100
1 2022 4 100
1 2022 5 100
1 2022 6 100
1 2022 7 100
1 2022 8 100
1 2022 9 100
1 2022 10 100
1 2022 11 100
1 2022 12 100
2 2022 1 200
2 2022 2 200
2 2022 3 200
2 2022 4 200
2 2022 5 200
2 2022 6 200
2 2022 7 200
2 2022 8 200
2 2022 9 200
2 2022 10 200
2 2022 11 200
2 2022 12 200
3 2022 1 150
3 2022 2 150
3 2022 3 150
3 2022 4 150
3 2022 5 150
3 2022 6 150
3 2022 7 150
3 2022 8 150
3 2022 9 150
3 2022 10 150
3 2022 11 150
3 2022 12 150
36 rows selected.
SQL>
While transposing single columns is pretty straight forward I need to transpose a large amount of data with 3 sets of , 10+ related columns needed to be transposed.
create table test
(month int,year int,po1 int,po2 int,ro1 int,ro2 int,mo1 int,mo2 int, mo3 int);
insert into test
values
(5,2013,100,20,10,1,3,4,5),(4,2014,200,30,20,2,4,5,6),(6,2015,200,80,30,3,5,6,7) ;
select * FROM test;
gives
month
year
po1
po2
ro1
ro2
mo1
mo2
mo3
5
2013
100
20
10
1
3
4
5
4
2014
200
30
20
2
4
5
6
6
2015
200
80
30
3
5
6
7
Transposing using UNPIVOT
select
month, year,
PO, RO, MO
from ( SELECT * from test) src
unpivot
( PO for Description in (po1, po2))unpiv1
unpivot
(RO for Description1 in (ro1, ro2)) unpiv2
unpivot
(MO for Description2 in (mo1, mo2, mo3)) unpiv3
order by year
Gives me this
month
year
PO
RO
MO
5
2013
100
10
3
5
2013
100
10
4
5
2013
100
10
5
5
2013
100
1
3
5
2013
100
1
4
5
2013
100
1
5
5
2013
20
10
3
5
2013
20
10
4
5
2013
20
10
5
5
2013
20
1
3
5
2013
20
1
4
5
2013
20
1
5
4
2014
200
20
4
4
2014
200
20
5
4
2014
200
20
6
4
2014
200
2
4
4
2014
200
2
5
4
2014
200
2
6
4
2014
30
20
4
4
2014
30
20
5
4
2014
30
20
6
4
2014
30
2
4
4
2014
30
2
5
4
2014
30
2
6
6
2015
200
30
5
6
2015
200
30
6
6
2015
200
30
7
6
2015
200
3
5
6
2015
200
3
6
6
2015
200
3
7
6
2015
80
30
5
6
2015
80
30
6
6
2015
80
30
7
6
2015
80
3
5
6
2015
80
3
6
6
2015
80
3
7
I will like to turn it to something like this. Is that possible?
month
year
PO
RO
MO
5
2013
100
10
3
5
2013
20
1
4
5
2013
0
0
5
4
2014
200
20
4
4
2014
30
2
5
4
2014
0
0
6
6
2015
200
30
5
6
2015
80
3
6
6
2015
0
0
7
Maybe use a query like below which creates rows as per your design using CROSS APPLY
select month,year,po,ro,mo from
test cross apply
(values (po1,ro1,mo1), (po2,ro2,mo2),(0,0,mo3))v(po,ro,mo)
see demo here
Unpivot acts similar as union,Use union all in your case
SELECT month,
year,
po1 AS PO,
ro1 AS RO,
mo1 AS MO
FROM test
UNION ALL
SELECT month,
year,
po2,
ro2,
mo2
FROM test
UNION ALL
SELECT month,
year,
0,
0,
mo2
FROM test
I have a data frame as shown below. which is a sales data of two health care product starting from December 2016 to November 2018.
product price sale_date discount
A 50 2016-12-01 5
A 50 2017-01-03 4
B 200 2016-12-24 10
A 50 2017-01-18 3
B 200 2017-01-28 15
A 50 2017-01-18 6
B 200 2017-01-28 20
A 50 2017-04-18 6
B 200 2017-12-08 25
A 50 2017-11-18 6
B 200 2017-08-21 20
B 200 2017-12-28 30
A 50 2018-03-18 10
B 300 2018-06-08 45
B 300 2018-09-20 50
A 50 2018-11-18 8
B 300 2018-11-28 35
From the above I would like to prepare below data frame
Expected Output:
product year number_of_months total_price total_discount number_of_sales
A 2016 1 50 5 1
B 2016 1 200 10 1
A 2017 12 250 25 5
B 2017 12 1000 110 5
A 2018 11 100 18 2
B 2018 11 900 130 3
Note: Please note that the data starts from Dec 2016 to Nov 2018.
So number of months in 2016 is 1, in 2017 we have full data so 12 months and 2018 we have 11 months.
First aggregate sum by years and product and then create new column for counts by months by DataFrame.insert and Series.map:
df1 =(df.groupby(['product',df['sale_date'].dt.year], sort=False).sum().add_prefix('total_')
.reset_index())
df1.insert(2,'number_of_months', df1['sale_date'].map({2016:1, 2017:12, 2018:11}))
print (df1)
product sale_date number_of_months total_price total_discount
0 A 2016 1 50 5
1 A 2017 12 250 25
2 B 2016 1 200 10
3 B 2017 12 1000 110
4 A 2018 11 100 18
5 B 2018 11 900 130
If want dynamic dictionary by minumal and maximal datetimes use:
s = pd.date_range(df['sale_date'].min(), df['sale_date'].max(), freq='MS')
d = s.year.value_counts().to_dict()
print (d)
{2017: 12, 2018: 11, 2016: 1}
df1 = (df.groupby(['product',df['sale_date'].dt.year], sort=False).sum().add_prefix('total_')
.reset_index())
df1.insert(2,'number_of_months', df1['sale_date'].map(d))
print (df1)
product sale_date number_of_months total_price total_discount
0 A 2016 1 50 5
1 A 2017 12 250 25
2 B 2016 1 200 10
3 B 2017 12 1000 110
4 A 2018 11 100 18
5 B 2018 11 900 130
For ploting is used DataFrame.set_index with DataFrame.unstack:
df2 = (df1.set_index(['sale_date','product'])[['total_price','total_discount']]
.unstack(fill_value=0))
df2.columns = df2.columns.map('_'.join)
print (df2)
total_price_A total_price_B total_discount_A total_discount_B
sale_date
2016 50 200 5 10
2017 250 1000 25 110
2018 100 900 18 130
df2.plot()
EDIT:
df1 = (df.groupby(['product',df['sale_date'].dt.year], sort=False)
.agg( total_price=('price','sum'),
total_discount=('discount','sum'),
number_of_sales=('discount','size'))
.reset_index())
df1.insert(2,'number_of_months', df1['sale_date'].map({2016:1, 2017:12, 2018:11}))
print (df1)
product sale_date number_of_months total_price total_discount \
0 A 2016 NaN 50 5
1 A 2017 NaN 250 25
2 B 2016 NaN 200 10
3 B 2017 NaN 1000 110
4 A 2018 NaN 100 18
5 B 2018 NaN 900 130
number_of_sales
0 1
1 5
2 1
3 5
4 2
5 3
I'm currently combining two tables through a UNION ALL query and performing SUM and GROUP BY operations on the result. Everything is working as expected, but I have a unique requirement which I can't seem to figure out how to implement.
My aim is to write SQL that says "when DEV_AGE column is >= 12 set the REVENUE value to what it would be if this column was 12". I provide the code below as I know this description can be a bit confusing:
REVENUE table:
ACC_YR DEV_AGE STATE REVENUE LOSS
2012 3 MA 4000 0
2012 6 MA 8000 0
2012 9 MA 12000 0
2012 12 MA 16000 0
LOSS table:
ACC_YR DEV_AGE STATE REVENUE LOSS
2012 3 MA 0 2000
2012 6 MA 0 7000
2012 9 MA 0 9000
2012 12 MA 0 10000
2012 15 MA 0 14000
2012 18 MA 0 14000
2012 21 MA 0 14000
2012 24 MA 0 15000
2012 27 MA 0 17000
Table after UNION ALL, GROUP BY, SUM:
ACC_YR DEV_AGE STATE REVENUE LOSS
2012 3 MA 4000 2000
2012 6 MA 8000 7000
2012 9 MA 12000 9000
2012 12 MA 16000 10000
2012 15 MA 0 14000
2012 18 MA 0 14000
2012 21 MA 0 14000
2012 24 MA 0 15000
2012 27 MA 0 17000
What I WANT to accomplish:
ACC_YR DEV_AGE STATE REVENUE LOSS
2012 3 MA 4000 2000
2012 6 MA 8000 7000
2012 9 MA 12000 9000
2012 12 MA 16000 10000
2012 15 MA 16000 14000
2012 18 MA 16000 14000
2012 21 MA 16000 14000
2012 24 MA 16000 15000
2012 27 MA 16000 17000
In other words, my REVENUE stops developing at a DEV_AGE of 12 (there are no rows in the REVENUE table beyond a DEV_AGE of 12), but I want every DEV_AGE beyond 12 to equal what the REVENUE was at 12 in the final table.
Here is an approach that uses window functions to calculate the revenue for age 12 and then logic to assign it:
select acc_yr, dev_age, state,
(case when dev_age > 12 then rev12 else revenue end) as revenue, loss
from (select l.acc_yr, l.dev_age, l.state, r.revenue, l.loss,
max(case when l.dev_age = 12 then r.revenue end) over (partition by l.acc_yr, l.state) as rev12
from loss l left join
revenue r
on l.acc_yr = r.acc_yr and l.dev_age = r.dev_age and l.state = dev.state
) lr;
I have Three tables as shown below..
I need output as shown in output table
for this i need to join three tables and order output in month order
tbl_MonthList
MonthID MonthList
1 January
2 February
3 March
4 April
5 May
6 June
7 July
8 August
9 September
10 October
11 November
12 December
tbl_Amount:
Month_id Amount_Received Customer_id
3 500 aaa
3 1000 bbb
4 700 jjj
5 300 aaa
5 400 jjj
5 500 ppp
7 1000 aaa
10 1500 bbb
12 700 jjj
tbl_Month_Target
MonthID MonthF_L
1 10000
2 150000
3 1000
4 50000
5 5000
6 3000
7 20000
8 12000
9 34000
10 85000
11 34000
12 45000
I need output as shown below
Month Total_amount MonthF_L
January 0 10000
February 0 150000
March 2000 1000
April 700 50000
May 1200 5000
June 0 3000
July 1000 20000
August 0 12000
September 0 34000
October 1500 85000
November 0 34000
December 700 45000
SELECT ML.MonthList AS Month,
Sum(A.Amount_Received) AS Total_amount,
First(MT.MonthF_L) AS MonthF_L
FROM (tbl_MonthList AS ML
INNER JOIN tbl_Month_Target AS MT ON ML.MonthID = MT.MonthID)
LEFT JOIN tbl_Amount AS A ON ML.MonthID = A.Month_id
GROUP BY ML.MonthList, ML.MonthID
ORDER BY ML.MonthID
Note: In MS Access, multiple joins must be explicitly nested within parentheses
Try this:
select ml.MonthList, sum(a.Amount_Received), mt.MonthF_L from tbl_MonthList ml
left join tbl_Month_Target mt on mt.MonthID = ml.MonthID
left join tbl_Amount a on ml.Month_id = ml.MonthID
group by ml.MonthList, mt.MonthF_L