How to split q1 data into months - sql

Hi I have one doubt in sql server:
in my table have 4 querter related totalamt values is available.
here I want split q1 sum values to months (april,may,june) of current year and
q2 sum values to months (july,Aug,Sep) of current year
q3 sum values to months (Oct,Nov,Dec) of current year
q4 sum values to months (Jan,Feb,Mar) of current year if i run current months are (jan or feb or mar ) then consider year as current year else next year of current year.
table :
CREATE TABLE [dbo].[task](
[Vertical] [varchar](50) NULL,
[AccountName] [varchar](50) NULL,
[q1] [money] NULL,
[q2] [money] NULL,
[q3] [money] NULL,
[q4] [money] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[task] ([Vertical], [AccountName], [q1], [q2], [q3], [q4]) VALUES (N'BFSI', N'susse', 90.0000, 15.0000, 30.0000, 6.0000)
GO
INSERT [dbo].[task] ([Vertical], [AccountName], [q1], [q2], [q3], [q4]) VALUES (N'BFSI', N'AIG', 100.0000, 50.0000, 40.0000, 60.0000)
GO
based on the above data I want output like below :
+----------+-------------+---------+-----------+------+
| vertical | accountname | reveune | month | year |
+----------+-------------+---------+-----------+------+
| BFSI | AIG | 13.3333 | December | 2018 |
| BFSI | AIG | 13.3333 | November | 2018 |
| BFSI | AIG | 13.3333 | October | 2018 |
| BFSI | AIG | 16.6666 | August | 2018 |
| BFSI | AIG | 16.6666 | July | 2018 |
| BFSI | AIG | 16.6666 | September | 2018 |
| BFSI | AIG | 20.00 | Feburary | 2019 |
| BFSI | AIG | 20.00 | January | 2019 |
| BFSI | AIG | 20.00 | March | 2019 |
| BFSI | AIG | 33.3333 | April | 2018 |
| BFSI | AIG | 33.3333 | June | 2018 |
| BFSI | AIG | 33.3333 | May | 2018 |
| BFSI | susse | 2.00 | Feburary | 2019 |
| BFSI | susse | 2.00 | January | 2019 |
| BFSI | susse | 2.00 | March | 2019 |
| BFSI | susse | 5.00 | August | 2018 |
| BFSI | susse | 5.00 | July | 2018 |
| BFSI | susse | 5.00 | September | 2018 |
| BFSI | susse | 10.00 | December | 2018 |
| BFSI | susse | 10.00 | November | 2018 |
| BFSI | susse | 10.00 | October | 2018 |
| BFSI | susse | 30.00 | April | 2018 |
| BFSI | susse | 30.00 | June | 2018 |
| BFSI | susse | 30.00 | May | 2018 |
+----------+-------------+---------+-----------+------+
I tried like below:
select vertical ,accountname ,[q1]/3 as reveune , 'April' as month ,year(getdate())as year from task
union
select vertical ,accountname ,[q1]/3 revenue ,'May' as month, year(getdate())as year from task
union
select vertical ,accountname ,[q1]/3 as reveune , 'June' as month ,year(getdate())as year from task
union
select vertical ,accountname ,[q2]/3 revenue ,'July' as month, year(getdate())as year from task
union
select vertical ,accountname ,[q2]/3 revenue ,'August' as month, year(getdate())as year from task
union
select vertical ,accountname ,[q2]/3 revenue ,'September' as month, year(getdate())as year from task
union
select vertical ,accountname ,[q3]/3 revenue ,'October' as month, year(getdate())as year from task
union
select vertical ,accountname ,[q3]/3 revenue ,'November' as month, year(getdate())as year from task
union
select vertical ,accountname ,[q3]/3 revenue ,'December' as month, year(getdate())as year from task
union
select vertical ,accountname ,[q4]/3 revenue ,'January' as month,
case when datepart(mm,getdate())=1 then datepart(yyyy,getdate()) else datepart(yyyy,getdate())+1 end year from task
union
select vertical ,accountname ,[q4]/3 revenue ,'Feburary' as month,
case when datepart(mm,getdate())=1 then datepart(yyyy,getdate()) else datepart(yyyy,getdate())+1 end year from task
union
select vertical ,accountname ,[q4]/3 revenue ,'March' as month,
case when datepart(mm,getdate())=1 then datepart(yyyy,getdate()) else datepart(yyyy,getdate())+1 end year from task
above query is giving expected result .but its taking very long time .can you please tell me any alternative solution
to achieve this task in sql server

You can try to build the desired output with cross apply:
select Vertical, AccountName, x.v as revenue, x.m as month, x.y as year
from [dbo].[task]
cross apply (values
(q1/3, 'April' , year(getdate()))
,(q1/3, 'May' , year(getdate()))
,(q1/3, 'June' , year(getdate()))
,(q2/3, 'July' , year(getdate()))
,(q2/3, 'August' , year(getdate()))
,(q2/3, 'September', year(getdate()))
,(q3/3, 'October' , year(getdate()))
,(q3/3, 'November' , year(getdate()))
,(q3/3, 'December' , year(getdate()))
,(q4/3, 'January' , case when datepart(mm,getdate())=1 then datepart(yyyy,getdate()) else datepart(yyyy,getdate())+1 end)
,(q4/3, 'February' , case when datepart(mm,getdate())=1 then datepart(yyyy,getdate()) else datepart(yyyy,getdate())+1 end)
,(q4/3, 'March' , case when datepart(mm,getdate())=1 then datepart(yyyy,getdate()) else datepart(yyyy,getdate())+1 end)
) x(v,m,y)
Note: I'm not sure if the association between months and quarters is actually correct, but this is just an implementative detail, you should be able to quickly fix it

Related

How to insert a Calender Items into Oracle DB records? [duplicate]

This question already has answers here:
How to populate calendar table in Oracle?
(3 answers)
Calendar table in SQL
(3 answers)
Closed 3 years ago.
I'm trying to insert a full year Calender into an ORACLE DB records
MY Columns are
----------------------------------------------------------------
| [FULL_DATE] | [DAY] | [MONTH_NAME] | [MONTH_NUMBER] | [YEAR] |
----------------------------------------------------------------
Function
(
#DATEFROM AS DATE
#DATETO AS DATE
) RETURNS DATE
AS
BEGIN
set #datefrom = '01/01/1995'
set #dateto = '31/12/1996'
while(#datefrom < #dateto)
BEGIN set #datefrom = DATEADD(day , 1 , #datefrom)
insert into SHEMA.DIM_TIME_TABLE ( FULL_DATE , DAY , MONTH , YEAR ) select DAY(GETDATE(#datefrom)) , DATENAME(MONTH , #datefrom), MONTH(GETDATE(#datefrom)) , YEAR(GETDATE(#datefrom))
END
RETURN
END
EXPECTED :
---------------------------------------------------------------
01 / 01 /1995 | 01 | JAN | 01 | 1995
---------------------------------------------------------------
02 / 01 /1995 | 02 | JAN | 01 | 1995
---------------------------------------------------------------
03 / 01 /1996 | 03 | JAN | 01 | 1995
In Oracle, you can use a recursive query to generate the date series, and then generate the expected columns in the outer query:
create table dim_time_table as
select
dt full_date,
extract(day from dt) day,
to_char(dt, 'month') month_name,
extract(month from dt) month_number,
extract(year from dt) year
from (
select to_date('1995-01-01', 'yyyy-mm-dd') + level - 1 as dt
from dual
connect by
to_date('1995-01-01', 'yyyy-mm-dd') + level
<= to_date('1997-01-01', 'yyyy-mm-dd')
)
Demo on DB Fiddle:
FULL_DATE | DAY | MONTH_NAME | MONTH_NUMBER | YEAR
:-------- | --: | :--------- | -----------: | ---:
01-JAN-95 | 1 | january | 1 | 1995
02-JAN-95 | 2 | january | 1 | 1995
03-JAN-95 | 3 | january | 1 | 1995
04-JAN-95 | 4 | january | 1 | 1995
05-JAN-95 | 5 | january | 1 | 1995
06-JAN-95 | 6 | january | 1 | 1995
07-JAN-95 | 7 | january | 1 | 1995
...

Return two values with CASE in SQL Server

I have a question: here is the problem, in my table I have some clients in this format:
id | year | Month | Amount
---+------+-------+--------
1 | 2016 | 02 | 250.00
2 | 2013 | 08 | 350.00
3 | 2015 | 12 | 450.00
4 | 2016 | 02 | 750.00
In my other table, I have a column ClientStartDate in this format 2015-12-15 00:00:00.000. So if the client start date is on 15th day of the month, than in table above I need to have two records , first record for the date Client started his work and second record for the same client but for the next month. It should look like this, let's say that client with id=3 started on 2015-12-15,table should look like this:
id | year | Month | Amount
---+------+-------+--------
1 | 2016 | 02 | 250.00
2 | 2013 | 08 | 350.00
3 | 2015 | 12 | 450.00
3 | 2016 | 01 | 150.00
4 | 2016 | 02 | 750.00
This is table where ClientStartDate comes from:
id | Name | ClientStartDate
---+--------+-------------------------
1 | John | 2016-02-01 00:00:00.000
2 | Anna | 2013-08-01 00:00:00.000
3 | Mike | 2015-12-15 00:00:00.000
4 | Nicolas| 2016-02-04 00:00:00.000
5 | Monika | 2013-11-15 00:00:00.000
This is FactTrans table where DateKey comes from:
id | amount | DateKey
----+--------+----------
1 | 208.67 | 20160201
1 | 19.12 | 20160205
2 | 55.42 | 20130820
2 | 5.42 | 20130811
4 | 23.98 | 20151121
5 | 17.99 | 20140820
Here is full code that I tried:
select
t1.ID,
left(cast(t1.datekey as varchar), 4) as Year,
left(right(cast(t1.datekey as varchar), 4), 2) as Month,
sum(t1.amount) as SumAmount
from
.dbo.FactTrans t1
inner join
dbo.Client t2 on t2.clientid = t1.clientid
where
(left(cast(t1.datekey as varchar), 4) = year(t2.clientstartdate)
and left(right(cast(t1.datekey as varchar), 4), 2) = month(t2.clientstartdate))
or
(case
when left(right(cast(t1.datekey as varchar), 4), 2)= 1
then 12
else left(right(cast(t1.datekey as varchar), 4), 2) - 1
end = month(t2.ClientStartDate)
and
case
when
month(t2.ClientStartDate) = 12
then
LEFT(cast(t1.datekey as varchar), 4) - 1
else
LEFT(cast(t1.datekey as varchar), 4)
end
= year(t2.ClientStartDate)
and
case
when
day(t2.ClientStartDate) = 15
then
month(t2.ClientStartDate) + 1
else
month(t2.ClientStartDate)
end
= left(cast(t1.datekey as varchar), 4)
)
group by t1.clientid, LEFT(cast(t1.datekey as varchar), 4) ,left(right(cast(t1.datekey as varchar), 4), 2)
order by t1.clientID
This case cover part where day = 15 but month =! 12, so here I only need to return next month and not next year. So my question is, can someone help me to write case where day=15 but month=12, and I need to return both next onth and next year for that client.
This case is WHERE statement.

MSSQL Count Multiple Columns

Say I have a table like this in ms sql 2008:
+------+--------+---------+
| year | JAN | FEB |
+------+--------+---------+
| 2016 | 5K2 | 5K2 |
| 2016 | 5K2 | 5K2 |
| 2016 | 5K2 | 5K2 |
| 2016 | 8Z | 8Z |
| 2016 | R5205 | R5205 |
| 2016 | 5K2 | 5K2 |
| 2016 | 5K2 | 5K2 |
| 2016 | NULL | NULL |
| 2016 | TE | NULL |
| 2016 | TE | NULL |
| 2016 | 8Z | 8Z |
+------+--------+---------+
And I want to get a count for each column, something like this
+------+--------+---------+
| opt | JAN_cnt| FEB_cnt |
+------+--------+---------+
| 5K2 | 5 | 4 |
| 8Z | 2 | 2 |
| R5205| 1 | 1 |
| TE | 2 | 0 |
| NULL | 1 | 4 |
+------+--------+---------+
First, can this be done? Second, how? I have searched, but cant find exactly what I am looking for.
I think the simplest way is to use UNION ALL with conditional aggregation using CASE EXPRESSION :
SELECT s.opt,
COUNT(CASE WHEN s.ind_from = 1 THEN 1 END) as jan_cnt,
COUNT(CASE WHEN s.ind_from = 2 THEN 1 END) as feb_cnt
FROM (
SELECT t1.jan as opt,1 as ind_from FROM YourTable t1
UNION ALL
SELECT t2.feb,2 FROM YourTable t2) s
GROUP BY s.opt
I would advise putting the values into a different format:
opt
month
cnt
You can do this as:
select opt, mon, count(*) as cnt
from ((select jan as opt, 'jan' as mon from t) union all
(select feb as opt, 'feb' as mon from t)
) o
group by opt, mon;
It is easy enough to switch this to your format:
select opt, sum(jan) as jan, sum(feb) as feb
from ((select jan as opt, 1 as jan, 0 as feb from t) union all
(select feb as opt, 0, 1, from t)
) o
group by opt;
I just prefer the first format. It is easier to generalize to more columns.
SELECT COALESCE(t1.JAN, t2.FEB), t1.JAN_cnt, t2.FEB_cnt
FROM
(
SELECT JAN, COUNT(*) AS JAN_cnt
FROM yourTable
GROUP BY JAN
) t1
FULL OUTER JOIN
(
SELECT FEB, COUNT(*) AS FEB_cnt
FROM yourTable
GROUP BY FEB
) t2
ON t1.JAN = t2.FEB

weekly aggregate with CTE not behaving as expected

I have this USERS table with users that can be of two different types (A and B). I need to show a report with the aggregate per type for each week. The query I have so far works well except some weeks are not grouping properly. In the example below, the week starting Jan 28th should have one line, not two.
Week Starts |Week| Type A | Type B
------------+----+--------+------
2013-02-04 | 14 | 2 | 26
2013-01-28 | 13 | 5 | 191
2013-01-28 | 13 | 0 | 24
2013-01-21 | 12 | 1 | 134
2013-01-21 | 12 | 0 | 20
2013-01-14 | 11 | 1 | 143
2013-01-14 | 11 | 0 | 2
2013-01-07 | 10 | 0 | 233
2013-01-07 | 10 | 0 | 23
2012-12-31 | 9 | 0 | 12
2012-12-31 | 9 | 4 | 164
2012-12-31 | 9 | 0 | 20
SQL
;with cte as
(
select DATEADD(m,-3,GETDATE()) firstday, DATEADD(m,-3,GETDATE()) + 6 - DATEDIFF(day, 0, DATEADD(m,-3,GETDATE())) %7 lastday, 1 week
union all
select lastday + 1, case when GETDATE() < lastday + 7 then GETDATE() else lastday + 7 end, week + 1
from cte
where lastday < GETDATE()
)
SELECT
cast(firstday as date) 'Week Starts',
cte.week as 'Week',
Sum(CASE WHEN USR_TYPE = 'A' THEN 1 ELSE 0 END) As 'Type A',
Sum(CASE WHEN USR_TYPE = 'B' THEN 1 ELSE 0 END) As 'Type B'
FROM cte left join USERS
ON cte.firstday <= USERS.CREATED
AND cte.lastday > USERS.CREATED
GROUP BY cte.week, cte.firstday, cte.lastday, DATEPART(YEAR,USERS.CREATED), DATEPART(wk,USERS.CREATED)
ORDER BY week desc
What am I doing wrong?
Without seeing any data from your users table I am going to take a guess.
The list of dates you are generating in the CTE includes the time.
You might need to cast() your firstday and lastday values as either a date or generate the list with no time.
See a SQL Fiddle Demo
Sample from your CTE and the new dates cast:
| CASTFIRSTDAY | CASTLASTDAY | WEEK | FIRSTDAY | LASTDAY |
---------------------------------------------------------------------------------------------------------
| 2012-11-05 | 2012-11-11 | 1 | November, 05 2012 20:08:10+0000 | November, 11 2012 20:08:10+0000 |
| 2012-11-12 | 2012-11-18 | 2 | November, 12 2012 20:08:10+0000 | November, 18 2012 20:08:10+0000 |
| 2012-11-19 | 2012-11-25 | 3 | November, 19 2012 20:08:10+0000 | November, 25 2012 20:08:10+0000 |
| 2012-11-26 | 2012-12-02 | 4 | November, 26 2012 20:08:10+0000 | December, 02 2012 20:08:10+0000 |
| 2012-12-03 | 2012-12-09 | 5 | December, 03 2012 20:08:10+0000 | December, 09 2012 20:08:10+0000 |
| 2012-12-10 | 2012-12-16 | 6 | December, 10 2012 20:08:10+0000 | December, 16 2012 20:08:10+0000 |
You might want to edit your CTE to return the date only values:
;with cte as
(
select
cast(DATEADD(m,-3,GETDATE()) as date) firstday,
cast(DATEADD(m,-3,GETDATE()) + 6 - DATEDIFF(day, 0, DATEADD(m,-3,GETDATE())) %7 as DATE) lastday,
1 week
union all
select
cast(DATEADD(DAY, 1, lastday) as date),
case
when cast(GETDATE() as date) < cast(DATEADD(DAY, 7, lastday) as date)
then cast(GETDATE() as date)
else cast(DATEADD(DAY, 7, lastday) as date)
end,
week + 1
from cte
where cast(lastday as date) < cast(GETDATE() as date)
)
select *
from cte
See SQL Fiddle with Demo

How in query result add 0-data for don't exist rows?

I have Table with columns: "Month" and "Year", and other data.
All row in Table have different values "Month" and "Year".
But for some Month and Year rows don't exist.
I want create SQL-query (... where year in (2010, 2011, 2012) ...), that in result this SQL-query have all Month for select Year and if some month don't exist else add it to result with 0 in other data columns.
Example:
Input: Table
data / month / year
+-----+---+------+
| 3.0 | 1 | 2011 |
| 4.3 | 3 | 2011 |
| 5.7 | 4 | 2011 |
| 2.2 | 5 | 2011 |
| 5.4 | 7 | 2011 |
+-----+---+------+
Output: SELECT ... WHERE year IN (2011)
+-----+----+------+
| 3.0 | 1 | 2011 |
| 0 | 2 | 2011 |
| 4.3 | 3 | 2011 |
| 5.7 | 4 | 2011 |
| 2.2 | 5 | 2011 |
| 0 | 6 | 2011 |
| 5.4 | 7 | 2011 |
| 0 | 8 | 2011 |
| 0 | 9 | 2011 |
| 0 | 10 | 2011 |
| 0 | 11 | 2011 |
| 0 | 12 | 2011 |
+-----+----+------+
Try Partition Outer Join:
SELECT
NVL(T.DATA, 0) DATA,
F.MONTH,
T.YEAR
FROM <your_table> T
PARTITION BY(T.YEAR)
RIGHT JOIN (SELECT LEVEL MONTH FROM DUAL CONNECT BY LEVEL <= 12) F ON T.MONTH = F.MONTH
Add your WHERE clause at the end or create a view with that definition and query against it.
select datecol,
nvl(val,0),
to_char(d.date_col,'MM') month,
to_char(d.date_col,'yyyy') year
from(
select add_months('1-Jan-2011',level-1) as datecol
from dual connect by level <= 12
) d
left join(
select sum(val) as val, month, year
from your_table
group by month, year
) S
on (to_char(d.date_col,'MM') = s.month and to_char(d.date_col,'yyyy') = s.year)
select nvl(t.data, 0), x.month, nvl(t.year, <your_year>) as year
from <your_table> t,
(select rownum as month from dual connect by level < 13) x
where (t.year is null or t.year = <your_year>)
and t.month(+) = x.month
order by x.month