SQL Count of Agents providing profit every month - sql

There are 2 columns in a table. AgentID and BusinessDate.
Query to find the count of Agents everymonth. But the count should only include agents having businessDate in every previous month.
Eg.
AgentID
BusinessDate
1
Jan 2020
2
Jan 2020
3
Jan 2020
4
Jan 2020
5
Feb 2020
1
Feb 2020
2
Feb 2020
3
Feb 2020
6
March 2020
7
March 2020
1
March 2020
2
March 2020
2
March 2020
Output
Month
Count
Jan
4
Feb
3
Mar
2
Only the agents providing business in every continuous month are counted.
I hope that I have explained the problem fine.
I have tried below methods:
select
Case
when (BusDate Between '01/01/2020' and '01/31/2020') and (BusDate Between '02/01/2020' and '02/28/2020') then ID end as 'Month'
,BusDate
from #Temp
group by Case
when (BusDate Between '01/01/2020' and '01/31/2020') and (BusDate Between '02/01/2020' and '02/28/2020') then ID end
BusDate
select ID
from #Temp where BusDate between '01/01/2020' and '01/31/2020' and BusFlg =1
intersect
select ID
from #Temp where BusDate between '02/01/2020' and '02/28/2020' and BusFlg =1
select ID
from #Temp where BusDate between '01/01/2020' and '01/31/2020' BusDate between '02/01/2020' and '02/28/2020' and BusFlg =1
select Month(BusDate), Count(ID) Over (partition by Month(BusDate)) from #Temp
group by BusDate,ID
select Month(BusDate), Sum(case when BusDate between '01/01/2020' and '01/31/2020' then 1
when Month(BusDate)=2 and BusDate between '01/01/2020' and '01/31/2020' and
)
from #Temp where BusFlg=1
Group by Month(BusDate)
select distinct ID, Count(Distinct ID) AS Cnt from #Temp where Month(BusDate)=2 and
ID in (Select Distinct ID from #Temp where Month(BusDate)=1 and BusFlg=1)
And BusFlg = 1 Group by ID

You can go for recursive CTE and only consider the already existing agents in the recursive part of the CTE. I am also going for rank to consider months in order: January, February ,March.
declare #table table(agentId int, businessdate varchar(10))
insert into #table values
(1 ,'Jan 2020')
,(2 ,'Jan 2020')
,(3 ,'Jan 2020')
,(4 ,'Jan 2020')
,(5 ,'Feb 2020')
,(1 ,'Feb 2020')
,(2 ,'Feb 2020')
,(3 ,'Feb 2020')
,(6 ,'March 2020')
,(7 ,'March 2020')
,(1 ,'March 2020')
,(2 ,'March 2020')
,(2 ,'March 2020');
;
with
cte_months as(
SELECT 1 as rnk, 'Jan 2020' as mon
union all
select 2, 'Feb 2020'
union all
select 3, 'March 2020'
),
cte_agents as(
SELECT cm.rnk, agentid, businessdate
from
#table as t
inner join cte_months as cm
on cm.mon = t.businessdate
where cm.rnk = 1
union all
-- consider only new months
SELECT cm.rnk, t.agentid, t.businessdate
from
#table as t
inner join cte_Agents as c
on c.agentid = t.agentid
--
inner join cte_months as cm
on
cm.mon = t.businessdate
and cm.rnk = c.rnk + 1
)
-- SELECT * FROM cte_agents
SELECT
businessdate,
count(distinct AgentId) as countOfAgents
FROM CTE_AGENTS
group by businessdate
businessdate
countOfAgents
Feb 2020
3
Jan 2020
4
March 2020
2

You can dense rank the months and the agents by month. Then take only rows where the ranks are equal. Then aggregate:
select v.yyyymm, count(distinct agentid)
from (select t.*, v.yyyymm,
dense_rank() over (order by yyyymm) as seqnum,
dense_rank() over (partition by agentid order by yyyymm) as seqnum_agentid
from t cross apply
(values (datefromparts(year(businessdate), month(businessdate), 1))
) v(yyyymm)
) t
where seqnum = seqnum_agentid

Related

Subtract value from group of same table

Year Month Code Value
2021 January 201 100.00
2021 February 201 250.00
2021 January 202 300.00
2021 February 202 200.00
2021 March 201 50.00
2021 March 202 150.00
Need to subtract code 201 from 202 , grouping both code by month.
Output :
Year Month Value
2021 january 200
2021 february -50
2021 March 100
I was trying to get desired output but its not working..
SELECT Code,Value
,
(
SELECT sum(Value) as Value
FROM [TestD]
Where Code = '202'
GROUP BY Code
)-(
SELECT sum(Value) as Value
FROM [TestD]
where Code = '201'
GROUP BY Code
) AS item_total
FROM [TestD] group by Code,Value
You can use a case expression inside sum(...), with group by Year, Month:
select Year,
Month,
sum(case when Code = '201' then -Value when Code = '202' then Value end) as Value
from TestD
group by Year, Month
Fiddle
You can try this provided only 2 values are going to be subtracted
declare #tbl table (year int, [month] varchar(50),code int, [value] int)
insert into #tbl values(2021, 'January', 201, 100.00),(2021, 'Feb', 201, 250.00)
,(2021, 'January', 202, 300.00),(2021, 'Feb', 202, 200.00)
select year,[month],[value], row_number()over(partition by [month] order by [value] desc) rownum
into #temp
from #tbl
select year,
month
,case when rownum = 1 then value else (
(select value from #temp t1 where t1.[month] = t.[month] and t1.rownum = 1) -
value) end as diff
from #temp t
where t.rownum = 2
order by month desc
drop table #temp

Left Join returns NULL on matching data

I perform simple LEFT JOIN between two tables:
A:
YR QTR MTH DAY DEPT SALES
2017 2 04 2017-04-01 B xxxxxx
2017 1 03 2017-03-31 A xxxxxxxx
2017 1 03 2017-03-31 B xxxxx
2017 1 03 2017-03-30 A xxxx
Second table (B) I use to bring QTR_ALT number
YEAR MONTH QTR QTR_ALT
2016 12 4 12
2017 01 1 12
2017 02 1 12
2017 03 1 11
2017 04 2 11
Following LEFT JOIN B ON A.YR = B.YEAR AND A.QTR = B.QTR AND A.MTH=B.MONTH returns NULL for QTR_ALT for A.DAY BETWEEN '2016-12-01' AND '2017-03-31'
YR QTR QTR_ALT MTH DAY DEPT SALES
2017 2 11 04 2017-04-02 A xxxxxx
2017 2 11 04 2017-04-01 A xxxxxx
2017 2 11 04 2017-04-01 B xxxxxx
2017 1 NULL 03 2017-03-31 A xxxxxxxx
2017 1 NULL 03 2017-03-31 B xxxxx
2017 1 NULL 03 2017-03-30 A xxxx
I tried moving WHERE condition to JOIN but no luck. How is it possible these dates don't get join even though corresponding record exists in table B?
Full code:
SELECT YEAR(A.DAY) as YR,
QUARTER(A.DAY) as QTR,
B.QTR_ALT,
(REPEAT(0, 2-LENGTH(MONTH(A.DAY))) || MONTH(A.DAY)) MTH,
A.DAY,
A.DEPT,
SUM(A.VAL) as SALES
FROM A
LEFT JOIN (SELECT TO_CHAR(ADD_MONTHS(a.DT, - b.Y), 'YYYY') as YEAR,
TO_CHAR(ADD_MONTHS(a.DT, - b.Y), 'MM') as MONTH,
CEIL(TO_NUMBER(TO_CHAR(add_months(a.dt, -b.y), 'MM')) / 3) as QTR,
CEIL(b.y/3) as QTR_ALT
FROM (SELECT TRUNC(CURRENT_DATE, 'MONTH') as DT) a
CROSS JOIN (SELECT SEQ8()+1 as Y FROM TABLE(GENERATOR(ROWCOUNT => 36)) ORDER BY 1) b
ORDER BY YEAR, MONTH) B
ON QUARTER(A.DAY) = B.QTR
AND (REPEAT(0, 2-LENGTH(MONTH(A.DAY))) || MONTH(A.DAY)) = B.MONTH
WHERE (YEAR(A.DAY) = B.YEAR)
AND (A.DAY BETWEEN '2016-12-01' AND '2017-03-31')
AND A.DEPT in ('A', 'B')
GROUP BY A.DAY, YEAR(A.DAY),QUARTER(A.DAY),B.QTR_ALT,(REPEAT(0, 2-LENGTH(MONTH(A.DAY))) || MONTH(A.DAY)), DEPT
ORDER BY A.DAY DESC
CREATE TEMP TABLE A (yr number, qtr number, mth text, day date, dept text, sales number);
INSERT INTO A values (2017,2,'04','2017-04-01','B', 10),
(2017,1,'03','2017-03-31','A', 11),
(2017,1,'03','2017-03-31','B', 20),
(2017,2,'03','2017-03-30','A', 6);
WITH
sub_b AS (
SELECT
TRUNC(CURRENT_DATE, 'MONTH') AS dt,
SEQ8() AS s,
ROW_NUMBER() OVER (ORDER BY s) AS y,
ADD_MONTHS(dt, - y) as tmp_date,
TO_CHAR(tmp_date, 'YYYY') AS year,
--TO_CHAR(tmp_date, 'MM') AS month, -- NOT USED
--QUARTER(tmp_date) as QTR, -- NOT USED
CEIL(y/3) as qtr_alt -- this value seems broken
FROM TABLE(GENERATOR(ROWCOUNT => 36))
)
SELECT a.yr,
a.qtr,
b.qtr_alt,
a.mth,
a.day,
a.dept,
SUM(a.sales) AS sales
FROM a
LEFT JOIN sub_b AS b
ON a.yr = b.year AND date_trunc('month',a.day) = b.tmp_date
WHERE a.day BETWEEN '2016-12-01' AND '2017-03-31'
AND a.dept in ('A', 'B')
GROUP BY 1,2,3,4,5,6
ORDER BY a.day DESC;
seems to work as given how I read your code/intent.

How to get all month names and need to show month data

Pnum Fdate description
==== ========== ===========
1024 2018-02-17 A
1024 2018-05-17 B
1024 2018-05-17 C
1024 2018-09-17 D
MY table PW have fields looks like this.
--> I want to show the result as
**Month Name Description**
January -
February A
March -
April -
May B
June -
July -
August C
September D
October -
November -
December -
Please help me how to achive this.
Join with a list of month names, there is only twelve of them:
SELECT monthname, description
FROM (VALUES
(1, 'January'),
(2, 'February'),
(3, 'March'),
(4, 'April'),
(5, 'May'),
(6, 'June'),
(7, 'July'),
(8, 'August'),
(9, 'September'),
(10, 'October'),
(11, 'November'),
(12, 'December')
) AS va(monthnumber, monthname)
LEFT JOIN yourdata ON DATEPART(MONTH, fdate) = va.monthnumber
ORDER BY monthnumber
Try this
;WITH CTE(Pnum, Fdate,description)
AS
(
SELect 1024,'2018-02-17','A' union all
SELect 1024,'2018-05-17','B' union all
SELect 1024,'2018-08-17','C' union all
SELect 1024,'2018-09-17','D'
)
SELECT MonthNames,ISNULL([Description],'-') AS [Description]
FROM CTE RIGHT JOIN
(
SELECT DATENAME(MONTH,DATEADD(MONTH,number-datepart(month,GETDATE()),GETDATE())) as MonthNames
FROM MASTER.DBO.spt_values
WHERE TYPE ='P'
AND number BETWEEN 1 AND 12
) dt
ON dt.MonthNames=DATENAME(MONTH,Fdate)
Result
MonthNames Description
--------------------------
January -
February A
March -
April -
May B
June -
July -
August C
September D
October -
November -
December -
You can try below
DEMO
with cte1 as (
select cast('2018-01-01' as date) dt
union all
select dateadd(month, 1, dt)
from cte1
where dateadd(month, 1, dt) < cast('2018-12-31' as date)
)
select DateName(month,dt),coalesce(Description,'-') as Description from cte1 a left join yourtable b
on month(a.dt)=month(b.Fdate)
This solution will allow an index on Fdate to be used (MONTH(column) will force a scan every time).
DECLARE #year int = 2018;
;WITH m AS
(
SELECT m = 1 UNION ALL SELECT m + 1 FROM m WHERE m < 12
),
months(b,e) AS
(
SELECT b = DATEFROMPARTS(#year, m, 1)
FROM m
)
SELECT DATENAME(MONTH, m.b), PW.Description
FROM months AS m
LEFT OUTER JOIN dbo.PW
ON PW.Fdate >= m.b AND PW.Fdate < DATEADD(MONTH, 1, m.b)
ORDER BY m.b;
Try the following query:
SELECT MONTHNAME(fdate), description FROM table
For more reference go through
https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_month

recursive common table expression in SQL Server

I have a table that contains two pieces of information BusinessDate and DailyPerf where DailyPerf shows percentage change in the value of a price I am tracking. The first few rows of the data looks like this
BusinessDate DailyPerf
Jan 3, 2017 -0.0356%
Jan 4, 2017 -0.4325%
Jan 5, 2017 -0.3953%
Jan 6, 2017 -0.8469%
Jan 9, 2017 -0.5050%
What I am trying to do is to calculate another column YearFac that shows the current percentage of my initial value I have left
Done in Excel the data would look like this:
BusinessDate DailyPerf YearFac
Jan 2, 2017 NULL 100%
Jan 3, 2017 -0.0356% 99.9644%
Jan 4, 2017 -0.4325% 99.5321%
Jan 5, 2017 -0.3953% 99.1386%
Jan 6, 2017 -0.8469% 98.2989%
Jan 9, 2017 -0.5050% 97.8025%
So, I need to prime my query with a YearFac of 100% and then recursively calculate the factor. The one caveat is that the dates are not necessarily sequential (there are no entries over weekends or on holidays) - so I cannot assume that next day = this day + 1 just that the next day is the next larger date
I tried the following
WITH cte
AS (
SELECT cast(1 as int) RowCnt,
cast('3 jan 2017' as date) BusinessDate,
cast(1.0 as float) YearFac -- anchor member
UNION ALL
select cast(ROW_NUMBER() OVER(ORDER BY p.BusinessDate ASC) + 1 as int) as RowCnt,
p.BusinessDate, -- recursive member
cast(cte.YearFac*(1.0 + p.DailyPerc) as float) YearFac
from cte
inner join dbo.MsfsDailyPnl p
on
cte.RowCnt = ROW_NUMBER() OVER(ORDER BY p.BusinessDate ASC) + 1
where p.BusinessDate < sysutcdatetime()
)
SELECT RowCnt,BusinessDate, YearFac
FROM cte
But, of course, this fails because I cannot reference the row number in the join. Could anyone suggest a modification that will get this query to work?
Use a cumulative sum:
select BusinessDate, DailyPerf,
1 - exp(sum(log(1 + DailyPerf)) over (order by BusinessDate desc)
from (select cast('2017-01-03' as date) as BusinessDate, cast(0 as float) as DailyPerf
union all
select BusinessDate, DailyPerf from dbo.MsfsDailyPnl
) p;
Use window function
select *, sum(DailyPerf) over (order by BusinessDate) + 100 YearFac
from (
select dateadd(day, -1, min(BusinessDate)) BusinessDate, 0 DailyPerf from data
union all
select * from data) t
dbfiddle demo
RESULT
BusinessDate DailyPerf YearFac
-------------------------------------------
02/01/2017 00:00:00 0.0000 100.0000
03/01/2017 00:00:00 -0.0356 99.9644
04/01/2017 00:00:00 -0.4325 99.5319
05/01/2017 00:00:00 -0.3953 99.1366
06/01/2017 00:00:00 -0.8469 98.2897
09/01/2017 00:00:00 -0.5050 97.7847
Here's what you might want to try. I attached also definition of table, so you know how it looks like and what can be done easily to accomplish your task.
declare #x table(BusinessDate date, DailyPerf float)
insert into #x values
('Jan 2, 2017', NULL),
('Jan 3, 2017', -0.0356),
('Jan 4, 2017', -0.4325),
('Jan 5, 2017', -0.3953),
('Jan 6, 2017', -0.8469),
('Jan 9, 2017', -0.5050)
select *,
100 + SUM(isnull(DailyPerf,0)) over (partition by (select null) order by
BusinessDate rows between unbounded preceding and current row)
from #x

How to convert rows to column in T-SQL, and write it in a temp table?

This is a question maybe already asked.
My query is
SELECT Year, Month, Line, SUM(value) as total FROM myTable
I've the following query result table:
Year Month Line Total
-------------------------------------------
2011 2 B1 5203510.00
2011 3 B1 2228850.00
2011 4 B1 7258075.00
2011 5 B1 6305370.00
2011 6 B1 5540180.00
2011 7 B1 7624430.00
2011 8 B1 4042300.00
2011 9 B1 3308870.00
2011 10 B1 4983875.00
2011 11 B1 4636500.00
2011 12 B1 3987350.00
2012 1 B1 518400.00
I would like the following:
Year Line Jan Feb Mar Apr ..... December
2011 B1 0 52035 2228 725 ..... 3987350
2012 B1 51840 ... ... ....
Please, can you help me how to translate query SQL from rows to columns?
Essentially, you need to PIVOT your data. There are several examples on SO on how to do this. The tricky part is to convert the month number to a month name.
This is accomplished in the example with DATENAME(month, DateAdd(month, [Month], 0)-1)
SQL Statement
SELECT *
FROM (
SELECT Year, Line, Total, mnt = DATENAME(month, DateAdd(month, [Month], 0)-1)
FROM myTable
) mt
PIVOT (MAX(Total) FOR [mnt] IN ([January],[February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December])) AS PVT
Test script
;WITH myTable AS (
SELECT * FROM (VALUES
(2011 , 2 , 'B1', 5203510.00)
, (2011 , 3 , 'B1', 2228850.00)
, (2011 , 4 , 'B1', 7258075.00)
, (2011 , 5 , 'B1', 6305370.00)
, (2011 , 6 , 'B1', 5540180.00)
, (2011 , 7 , 'B1', 7624430.00)
, (2011 , 8 , 'B1', 4042300.00)
, (2011 , 9 , 'B1', 3308870.00)
, (2011 , 10 , 'B1', 4983875.00)
, (2011 , 11 , 'B1', 4636500.00)
, (2011 , 12 , 'B1', 3987350.00)
, (2012 , 1 , 'B1', 518400.00)
) AS myTable (Year, Month, Line, Total)
)
SELECT *
FROM (
SELECT Year, Line, Total, mnt = DATENAME(month, DateAdd(month, [Month], 0)-1)
FROM myTable
) mt
PIVOT (MAX(Total) FOR [mnt] IN ([January],[February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December])) AS PVT
What you're trying to do is pivot the data. I will just use the Month and Total (relevant) columns.
If you're using MS SQL 2008 or up:
SELECT [1] AS Jan, [2] AS Feb, .. [12] AS Dec,
Total
FROM ( SELECT Month, Total FROM tableA ) AS SOURCE
PIVOT
( MAX(Total) AS Total
FOR
Month IN ([1],[2],...[12]) ) AS PIVOT
PIVOT is the T-SQL keyword you seek.