I am trying to calculate churn of customers based on activity they could have done, opposed to churn by date that is the normal thing. We have events that is connected to a specific host, in my example all events are hosted by Alice but it could be different hosts.
All the people that follow a specific event should be placed in a category (new, active, churned and resurrected).
New: First time a person follow an event from the specific host.
Active: Follow again (and last event from specific host was also followed).
Churned: Follower had a chance to follow but didn't.
Resurrected: Follower that has churned has started to follow a previously followed host.
declare #events table (event varchar(50), host varchar(50), date date)
declare #eventFollows table (event varchar(50), follower varchar(50))
insert into #events values ('e_1', 'Alice', GETDATE())
insert into #events values ('e_2', 'Alice', GETDATE())
insert into #events values ('e_3', 'Alice', GETDATE())
insert into #events values ('e_4', 'Alice', GETDATE())
insert into #events values ('e_5', 'Alice', GETDATE())
insert into #eventFollows values ('e_1', 'Bob') --new
insert into #eventFollows values ('e_2', 'Bob') --active
--Bob churned
insert into #eventFollows values ('e_4', 'Megan') --new
insert into #eventFollows values ('e_5', 'Bob') --resurrected
insert into #eventFollows values ('e_5', 'Megan') --active
select * from #events
select * from #eventFollows
The expected outcome should be something like this
select 'e_1', 1 as New, 0 as resurrected, 0 as active, 0 as churned --First time Bob follows Alice event
union all
select 'e_2', 0 as New, 0 as resurrected, 1 as active, 0 as churned --Bob follows the next event that Alice host (considered as Active)
union all
select 'e_3', 0 as New, 0 as resurrected, 0 as active, 1 as churned --Bob churns since he does not follow the next event
union all
select 'e_4', 1 as New, 0 as resurrected, 0 as active, 0 as churned --First time Megan follows Alice event
union all
select 'e_5', 0 as New, 1 as resurrected, 1 as active, 0 as churned --Second time (active) for Megan and Bob is resurrected
I started with a query of something like below, but the problem is that I don't get all the events that the followers did not follow (but could have followed).
select a.event, follower, date,
LAG (a.event,1) over (partition by a.host, ma.follower order by date) as lag,
LEAD (a.event,1) over (partition by a.host, ma.follower order by date) as lead,
LAG (a.event,1) over (partition by a.host order by date) as lagP,
LEAD (a.event,1) over (partition by a.host order by date) as leadP
from #events a left join #eventFollows ma on ma.event = a.event order by host, follower, date
Any ideas?
This may seem a bit of an indirect approach, but it's possible to detect islands by checking for gaps in the numbers:
;with nrsE as
(
select *, ROW_NUMBER() over (order by event) rnrE from #events
), nrs as
(
select f.*,host, rnrE, ROW_NUMBER() over (partition by f.follower, e.host order by f.event ) rnrF
from nrsE e
join #eventFollows f on f.event = e.event
), f as
(
select host, follower, min(rnrE) FirstE, max(rnrE) LastE, ROW_NUMBER() over (partition by follower, host order by rnrE - rnrF) SeqNr
from nrs
group by host, follower, rnrE - rnrF --difference between rnr-Event and rnr-Follower to detect gaps
), stat as --from the result above on there are several options. this example uses getting a 'status' and pivoting on it
(
select e.event, e.host, case when f.FirstE is null then 'No participants' when f.LastE = e.rnrE - 1 then 'Churned' when rnrE = f.FirstE then case when SeqNr = 1 then 'New' else 'Resurrected' end else 'Active' end Status
from nrsE e
left join f on e.rnrE between f.FirstE and f.LastE + 1 and e.host = f.host
)
select p.* from stat pivot(count(Status) for Status in ([New], [Resurrected], [Active], [Churned])) p
The last 2 steps could be simplified, but getting the 'Status' this way might be reusable for other scenarios
This matches your desired result
SELECT
X.event, X.host, X.date,
IsNew = SUM(CASE WHEN X.FirstFollowerEvent = X.event THEN 1 ELSE 0 END),
IsActive = SUM(CASE WHEN X.lagFollowerEvent = X.lagEvent THEN 1 ELSE 0 END),
IsChurned = SUM(CASE WHEN X.follower IS NULL THEN 1 ELSE 0 END),
IsResurrected = SUM(CASE WHEN X.lagFollowerEvent <> X.lagEvent AND X.FirstFollowerEvent IS NOT NULL THEN 1 ELSE 0 END)
FROM
(
select
a.event, a.host, ma.follower, a.date,
FIRST_VALUE(a.event) over (partition by a.host, ma.follower order by a.date, a.event) as FirstFollowerEvent,
LAG (a.event,1) over (partition by a.host, ma.follower order by a.date, a.event) as lagFollowerEvent,
LAG (a.event,1) over (partition by a.host order by a.date, a.event) as lagEvent
FROM
#events a
LEFT join
#eventFollows ma on a.event = ma.event
) X
GROUP BY
X.event, X.host, X.date
ORDER by
X.event, X.host, X.date
Related
I'm trying to create an SSRS report that looks similar to the table below:
Report
Earliest Run
Recent Run
Runs Last 7 days
Runs YTD
Runs All Time
Report 1
3/3/19 1:30
7/8/22 2:45
8
86
233
I know how to query the last 3 columns individually, but is it possible to get all 3 columns using 1 query? I have tried the query below to show my line of thinking but its not working as desired.
SELECT Report
,Min(TimeStart) AS EarliestRun
,Max(TimeStart) AS RecentRun
,CASE WHEN TimeStart BETWEEN GETDATE()-7 AND GETDATE() THEN COUNT(Report) END AS RunsLast7Days
FROM ReportHistory
WHERE TimeStart BETWEEN '1/1/2019 00:00' AND GETDATE()
GROUP BY Report
Yes - use conditional aggregation. Don't filter the query at all since you need an "all time" value. Instead, use sum with a conditional expression for the periods of interest.
select ...
sum(case when TimeStart >= dateadd(day, -7, getdate()) then 1 else 0 end) as [Runs Last 7 days],
sum(case when TimeStart >= datefromparts(year(getdate()), 1, 1) then 1 else 0 end) as [Runs YTD],
...
from dbo.ReportHistory
order by ...;
I was going to propose using CROSS APPLY but SMor has done it with less code
CREATE TABLE #Reports (
ReportId INT NOT NULL,
ReportName VARCHAR(20) NOT NULL
);
INSERT INTO #Reports(ReportId, ReportName)
VALUES(1, 'Report 1');
CREATE TABLE #ReportRun (
ReportId INT,
RunDateTime DATETIME2(2)
);
INSERT INTO #ReportRun(ReportId, RunDateTime)
VALUES
(1, '20220508 10:00:00'),
(1, '20220502 10:00:00'),
(1, '20220101 10:00:00'),
(1, '20210501 10:00:00'),
(1, '20210209 10:00:00'),
(1, '20200509 10:00:00'),
(1, '20190509 10:00:00');
GO
-- SELECT * FROM #Reports
-- SELECT * FROM #ReportRun
SELECT R.ReportName, B.RunLast7Days, C.RunYearToDate, D.RunAllTime
FROM #Reports AS R
CROSS APPLY (
SELECT TOP 1 RunDateTime
FROM #ReportRun
WHERE ReportId = R.ReportId
ORDER BY RunDateTime DESC
) AS ER
CROSS APPLY (
SELECT COUNT(*) AS RunLast7Days
FROM #ReportRun
WHERE ReportId = R.ReportId
AND RunDateTime >= DATEADD(day, -7, CONVERT(date, GETDATE())) -- best to set it to the start of the day
GROUP BY ReportId
) AS B
CROSS APPLY (
SELECT COUNT(*) AS RunYearToDate
FROM #ReportRun
WHERE ReportId = R.ReportId
AND RunDateTime >= DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)
GROUP BY ReportId
) AS C
CROSS APPLY (
SELECT COUNT(*) AS RunAllTime
FROM #ReportRun
WHERE ReportId = R.ReportId
GROUP BY ReportId
) AS D
I have this table where I am storing the Sale Orders. The scenario is that once any sale order is punched it is not finalized, and requires editing later on so if any more items are added and saved again the sale order is updated with transaction number more than the previous one to keep the track of the changes. Here is a sample data that a sale order was punched and then 2 times more items were added and amount was changed and in the last row as shown items were cancelled and amount was changed.
I want to calculate the amount of the additions made in the sale order every time new items were added and the cancellations as well that how much worth of items were cancelled.
CREATE TABLE SaleOrder
(
TransactionNo Int,
SaleOrderDate DATE,
Code VARCHAR(25),
Quantity INT,
TotalAmount Numeric(18,2),
Remarks VARCHAR(25)
)
INSERT INTO SaleOrder VALUES (NULL, '2018-10-01', 'SO-001-OCT-18', 6, '2500', 'Hello');
INSERT INTO SaleOrder VALUES (1, '2018-10-01', 'SO-001-OCT-18', 8, '2600', 'Hello');
INSERT INTO SaleOrder VALUES (2, '2018-10-01', 'SO-001-OCT-18', 12, '3400', 'Hello');
INSERT INTO SaleOrder VALUES (3, '2018-10-01', 'SO-001-OCT-18', 9, '2900', 'Hello');
This will be the result that I am expected.
Code SaleOrderDate Quantity InitialAmount Addition Cancellation
SO-001-OCT-18 2018-10-01 9 2500.00 900.00 500.00
I have written this query but it's not helping that much.
;WITH CTE AS (
SELECT
[TransactionNo], [Code], [SaleOrderDate], [Quantity], [TotalAmount],
CAST('Oct 1 2018 10:16AM' AS DATE) AS [DateFrom], CAST('Oct 4 2018 10:16AM' AS DATE) AS [DateTo]
FROM [SaleOrder]
GROUP BY
[TransactionNo], [Code], [SaleOrderDate], [TotalAmount], Quantity
)
SELECT
[D].[TransactionNo], [D].[Code], [D].[SaleOrderDate], [D].[Quantity], [D].TotalAmount,
--CAST('Oct 4 2018 4:06PM' AS DATE) AS [DateFrom],
--CAST('Oct 4 2018 4:06PM' AS DATE) AS [DateTo],
[D].[Balance], [D].[Balance]-ISNULL(NULLIF([D].TotalAmount, 0),0) [Opening]
FROM(
SELECT *,
SUM(TotalAmount) OVER (PARTITION BY [Code] ORDER BY [TransactionNo], [SaleOrderDate]) AS [Balance]
FROM CTE
)D
WHERE [SaleOrderDate] BETWEEN CAST('Oct 1 2018 10:16AM' AS DATE) AND CAST('Oct 4 2018 10:16AM' AS DATE)
ORDER BY [SaleOrderDate]
use the LAG() window function to get previous value and compare to determine it is an addition or cancellation.
; WITH cte as
(
SELECT *,
row_no = ROW_NUMBER() OVER (PARTITION BY Code ORDER BY TransactionNo DESC),
Addition = CASE WHEN TotalAmount > LAG(TotalAmount) OVER (PARTITION BY Code ORDER BY TransactionNo)
THEN TotalAmount - LAG(TotalAmount) OVER (PARTITION BY Code ORDER BY TransactionNo)
ELSE 0
END,
Cancellation = CASE WHEN TotalAmount < LAG(TotalAmount) OVER (PARTITION BY Code ORDER BY TransactionNo)
THEN LAG(TotalAmount) OVER (PARTITION BY Code ORDER BY TransactionNo) - TotalAmount
ELSE 0
END
FROM SaleOrder
)
SELECT Code,
SaleOrderDate,
Quantity = MAX (CASE WHEN row_no = 1 then Quantity END),
InitialAmount = MAX (CASE WHEN TransactionNo IS NULL THEN TotalAmount END),
Addition = SUM (Addition),
Cancellation = SUM (Cancellation)
FROM cte
GROUP BY Code, SaleOrderDate
Are you trying to do this? :
SELECT
Code
, MAX(SaleOrderDate) SaleOrderDate
, MAX(Quantity) Quantity
, MAX(InitialAmount) InitialAmount
, SUM(Addition) Addition
, ABS(SUM(Cancellation)) Cancellation
FROM (
SELECT
Code
, CASE WHEN rn = cnt THEN SaleOrderDate END SaleOrderDate
, CASE WHEN rn = cnt THEN Quantity END Quantity
, InitialAmount
, CASE WHEN Diff > 0 THEN Diff ELSE 0 END Addition
, CASE WHEN Diff < 0 THEN Diff ELSE 0 END Cancellation
FROM (
SELECT *
, CASE WHEN TransactionNo IS NULL THEN TotalAmount END InitialAmount
, LEAD(TotalAmount) OVER(PARTITION BY Code ORDER BY TransactionNo) nxtPrice
, LEAD(TotalAmount) OVER(PARTITION BY Code ORDER BY TransactionNo) - TotalAmount Diff
, COUNT(*) OVER(PARTITION BY Code) cnt
, ROW_NUMBER() OVER(PARTITION BY Code ORDER BY SaleOrderDate) rn
FROM SaleOrder
) D
) C
GROUP BY
Code
I have a table in SSMS:
Id Date Value
111 1/1/18 x
111 1/2/18 x
111 1/3/18 y
111 1/4/18 y
111 1/5/18 x
111 1/6/18 x
222 1/3/18 z
222 1/6/18 y
222 1/8/18 y
I want to count for the frequency of latest value . So the output will be:
Id Value Days
111 x 2 *(for 1/5/18 & 1/6/18)*
222 y 3 *(for 1/6/18 & 1/8/18; Here I assume 1/7/18 is a weekend or holiday. Even though my table skips the weekend, we still want to count days for the weekend)*
How would this be done? Many thanks!
Use lag to get the previous row's value and then a running sum to assign groups. Thereafter count the number in the first group.
select id,val,datediff(day,min(date),max(date))+1 as days
from (select t.*,sum(case when val=prev_val then 0 else 1 end) over(partition by id order by date desc) as grp
from (select t.*,lag(val) over(partition by id order by date desc) as prev_val
from tbl t
) t
) t
where grp=1
group by id,val
Try:
SELECT COUNT(*) FROM Table1 WHERE Value =
(
SELECT Value FROM Table1 WHERE Id = MAX(Id)
)
I hope you want this
select Id, count(Date) as "Days", Value from SSMS
group by ID, Value
correct me if I'm wrong
This answer should account for the weekends and holiday assumptions you have made (with another test case).
SELECT
T.Id, T.val, DATEDIFF(DD, COALESCE(T.MaxSwitch, T.MinMatch, T.MaxDate), T.MaxDate) + 1 AS [Days]
FROM (
SELECT
T.Id,
MAX(CASE WHEN T.LastValue IS NULL THEN T.val ELSE '' END) AS [val],
MAX(T.Date) AS [MaxDate],
MAX(CASE WHEN t.val <> t.LastValue THEN T.RunningDate ELSE NULL END) AS [MaxSwitch],
MIN(CASE WHEN t.val = t.LastValue THEN T.[Date] ELSE NULL END) AS [MinMatch]
FROM (SELECT *, LAG(val) OVER (PARTITION BY Id ORDER BY DATE DESC) AS LastValue,
LAG([Date]) OVER (PARTITION BY Id ORDER BY DATE DESC) AS RunningDate FROM #T) T
GROUP BY
T.Id
) T
This approach uses LAG to track previous value and date so that it can determine (1) the last value to get running match, (2) the latest date when value switched to most recent value, and (3) the earliest date with value matching final date. It then calculates the date difference to account for skipping days in table from priority of (A) latest date value switched to recent value, (B) or if no switch occurred, then earliest date with value matching final date.
For the sample data below:
DECLARE #T TABLE (
Id INT, [Date] DATE, val VARCHAR(10)
)
INSERT #T VALUES
('111', '1/1/18', 'x'),
('111', '1/2/18', 'x'),
('111', '1/3/18', 'y'),
('111', '1/4/18', 'y'),
('111', '1/5/18', 'x'),
('111', '1/6/18', 'x'),
('222', '1/2/18', 'y'),
('222', '1/3/18', 'z'),
('222', '1/6/18', 'y'),
('222', '1/8/18', 'y'),
('333', '1/9/18', 'a')
The following output is given:
Id val Days
----------- ---------- -----------
111 x 2 (from OP example)
222 y 3 (from OP example)
333 a 1 (case of single value)
*Edit (Hopefully to be more clear)
Table below, I would like to count ids and count duplicate ids where the createddate has a gap of 3 months or more for that ID.
Query I have so far...
if object_id('tempdb..#temp') is not null
begin drop table #temp end
select
top 100
a.id, a.CreatedDate
into #temp
from tbl a
where 1=1
--and year(CreatedDate) = '2015'
if object_id('tempdb..#temp2') is not null
begin drop table #temp2 end
select t.id, count(t.id) as Total_Cnt
into #temp2
from #temp t
group by id
select distinct #temp2.Total_Cnt, #temp2.id, #temp.CreatedDate, DENSE_RANK() over (partition by #temp.id order by createddate) RK
from #temp2
inner join #temp on #temp2.id = #temp.id
where 1=1
order by Total_Cnt desc
Results:
Total_cnt id createddate rk
3 1 01-01-2015 1
3 1 03-02-2015 2
3 1 01-02-2015 3
2 2 05-01-2015 1
2 2 05-02-2015 2
1 3 06-01-2015 1
1 4 07-01-2015 1
Count ids and only count duplicate ids when the createddate from the id is greater than 3 months.
Something like this...
Total_cnt id Countwith3monthgap
3 1 2
2 2 1
1 3 1
1 4 1
You can use a cte and ROW_NUMBER to get your order and self join the cte based on the order..
WITH cte AS
( SELECT
*,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY CreatedDate) Rn
FROM
Test
)
SELECT
c1.ID,
COUNT(CASE WHEN c2.CreatedDate IS NULL THEN 1
WHEN c1.CreatedDate >= DATEADD(month,3,c2.CreatedDate) THEN 1
END)
FROM
cte c1
LEFT JOIN cte c2 ON c1.ID = c2.ID
AND c1.RN = c2.RN + 1
GROUP BY
c1.ID
You also need to use a conditional count where the Previous CreatedDate is null or if the Current CreatedDate is >= the Previous CreatedDate + 3 months
If you happen to be using SQL 2012+ you can also use LAG here to get the same result
SELECT
ID,
COUNT(*)
FROM
(SELECT
ID,
CreatedDate CurrentDate,
LAG(CreatedDate) OVER (PARTITION BY ID ORDER BY CreatedDate) PreviousDate
FROM
Test
) T
WHERE
PreviousDate IS NULL
OR CurrentDate >= DATEADD(month, 3, PreviousDate)
GROUP BY
ID
You can use a lag to get the previous date, Null for the first in the list
SELECT
id,
lag(CreatedDate,1) OVER (PARTITION BY Id ORDER BY CreatedDate) AS PreviousCreateDate,
CreatedDate
FROM #t
You can use that as a subquery and get the difference in months using DATEDIFF
SELECT sub.id,DATEDiff(month, sub.PreviousCreateDate ,sub.CreatedDate)
FROM (SELECT
id,
lag(CreatedDate,1) OVER (PARTITION BY Id ORDER BY CreatedDate) AS PreviousCreateDate,
CreatedDate
FROM #t) sub
WHERE DATEDiff(month, sub.PreviousCreateDate ,sub.CreatedDate) >=3
OR sub.PreviousCreateDate IS NULL
You can then take your totals
SELECT sub.id,COUNT(sub.id) as cnt
FROM (SELECT
id,
lag(CreatedDate,1) OVER (PARTITION BY Id ORDER BY CreatedDate) AS PreviousCreateDate,
CreatedDate
FROM #t) sub
WHERE DATEDIFF(month, sub.PreviousCreateDate ,sub.CreatedDate) >=3
OR sub.PreviousCreateDate IS NULL
GROUP BY sub.id
Note that using datediff the last day of january is three months before the first day of march. That appears to be the logic you were after.
You might want to define your three month gap criteria as
WHERE sub.PreviousCreateDate <= DATEADD(month, -3, sub.CreatedDate)
OR sub.PreviousCreateDate IS NULL
or
WHERE sub.CreatedDate >= DATEADD(month, +3, sub.PreviousCreateDate )
OR sub.PreviousCreateDate IS NULL
I'm guessing that your desired definition of three-month gap doesn't coincide with datediff()'s. Most of the logic here is to look back at the previous date and decide if the gap is big enough to qualify.
When datediff() counts three months difference we still need to make sure the day of month is later than the first one (per example and ID 5). If difference is more than three months then we're good automatically.
But I'm also assuming that you would want to treat the distance from November 30th to February 28th (or 29th in a leap year) as a full three months because the end date falls on the final day of the month. By adjusting the end date by an extra day this is an easy scenario to snag as it will bump the date into the following month and increase the month difference by one as well. If that's not what you want then just remove the dateadd(day, 1, ...) portion and use only the raw CreatedDate value.
You sample data is limited so I'm also making the assumption that the gaps are measure between consecutive dates. If you're wanting to find blocks of runs that don't span more than three months across the set, then that's a different problem and you should clarify with more information.
Since you've indicated that you're probably on SQL Server 2008 you'll have to do without the lag() function. Although the first query could be adjusted for that it's likely easier to go with the second approach at the end.
with diffs as (
select
ID,
row_number() over (partition by ID order by CreatedDate) as RN,
case when
datediff(
month,
lag(CreatedDate, 1) over (partition by ID order by CreatedDate),
CreatedDate
) = 3
and
datepart(
day,
lag(CreatedDate, 1) over (partition by ID order by CreatedDate)
) <= datepart(day, CreatedDate)
or
datediff(
month,
lag(CreatedDate, 1) over (partition by ID order by CreatedDate),
/* adding one day to handle gaps like Nov30 - Feb28/29 and Jan31 - Apr30 */
dateadd(day, 1, CreatedDate)
) >= 4
then 1
else 0
end as GapFlag
from <T> /* <--- your table name here */
), gaps as (
select
ID, RN,
sum(1 + GapFlag) over (partition by ID order by RN) as Counter
from diffs
)
select ID, count(distinct Counter - RN) as "Count"
from gaps
group by ID
The rest of the logic is a typical gaps and islands scenario looking for holes in the sum(1 + GapCount) sequence with the offset of 1 acting pretty much like row_number().
http://sqlfiddle.com/#!6/61b12/3
JamieD77's approach is also valid. I was originally thinking your problem involved more than looking at the rows in sequence. Here's how I would tweak it for the gap definition I've been running with:
with data as (
select ID, CreatedDate, row_number() over (partition by ID order by CreatedDate) as RN
from T
)
select ID, count(*) as "Count"
from data d1 left outer join data d0
on d0.ID = d1.ID and d0.RN = d1.RN - 1 /* connect to the one before */
where
datediff(month, d0.CreatedDate, d1.CreatedDate) = 3
and datepart(day, d0.CreatedDate) <= datepart(day, d0.CreatedDate)
or datediff(month, d0.CreatedDate, dateadd(day, 1, d0.CreatedDate)) >= 4
or d0.ID is null
group by ID
Edit: You have changed the question since yesterday.
Change this line in the first query to include the total count:
...
select count(*) as TotalCnt, ID, count(distinct Counter - RN) as GapCount
...
Second would look like:
with data as (
select ID, CreatedDate, row_number() over (partition by ID order by CreatedDate) as RN
from T
)
select
count(*) as TotalCnt, ID,
count(case when
datediff(month, d0.CreatedDate, d1.CreatedDate) = 3
and datepart(day, d0.CreatedDate) <= datepart(day, d0.CreatedDate)
or datediff(month, d0.CreatedDate, dateadd(day, 1, d0.CreatedDate)) >= 4
or d0.ID is null then 1 end
) as GapCount
from data d1 left outer join data d0
on d0.ID = d1.ID and d0.RN = d1.RN - 1 /* connect to the one before */
where
group by ID
I have staff members that are assigned tasks, I need to find the percentage of tasks that a staff member has completed year-to-date... of those that were assigned to him. If John is assigned 10 tasks, and completed 5 I need to show John has closed .50 (50%).
I have two tables:
Tasks and Tasks_cstm
Tasks t
| ID | STATUS |Date_Completed|
The statuses are 'In Progress', 'Not Started', 'Completed'
Tasks_cst tc
| ID_C|Staff_Member|
The tables are joined on t.id = tc.id_c
This returns the number completed:
(select count(*)as Completed from tasks_CSTM tc
join tasks t
on t.id = tc.id_c
where status = 'completed'
group by staff_member_C )
This returns the total number of tasks:
(select count(*)as Total from tasks_CSTM tc
join tasks t
on t.id = tc.id_c
group by staff_member_C )
This is what I've come up with, but it errors: Subquery returned more than 1 value.
select staff_member_c,((select count(*)as Completed from tasks_CSTM tc
join tasks t
on t.id = tc.id_c
where status = 'completed'
group by staff_member_C )/(select count(*)as Total from tasks_CSTM tc
join tasks t
on t.id = tc.id_c
group by staff_member_C ))
from tasks t
join tasks_CSTM tc
on t.id = tc.id_C
group by staff_member_C
Any help is appreciated.
Something like this I think:
select staff_member_c, sum(case when status='completed' then 1.0 end)/count(*) as pctCompleted
from tasks_cstm tc
join tasks t
on t.id = tc.id_c
group by staff_member_c
You might need "else 0.0" in the case statement (but don't in MSSQL), and you might need nullif(count(*),0) in the denominator (but probably not in any DBMS).
There's a couple issues here to grapple with. One of which is dealing with the "year-to-date" part. Right now, with a Date_Completed column, there's no way to know when a task was assigned/created, which invalidates our ability to know year-to-date info. Barring that part of the question, here's my query which should work. I have a WHERE clause commented out which can easily be adapted to use a Date_Assigned column for year-to-date info.
select
staff_member
, sum(case t.status when 'Completed' then 1.0 else 0 end) [Completed]
, count(*) [Total]
, sum(case t.status when 'Completed' then 1.0 else 0 end) / count(*) [CompletedPercent]
from
tasks t
inner join tasks_cstm tc
on t.id = tc.id_C
--where
-- dateadd(year, datediff(year, 0, Date_Assigned), 0) = dateadd(year, datediff(year, 0, getdate()), 0)
group by
staff_member
And here's the setup code I used to (un-comprehensibly) test it out:
create table tasks (ID int, Status varchar(50), Date_Completed date)
create table tasks_cstm (ID_C int, Staff_Member varchar(50))
insert into tasks
select 1, 'Not Started', null
union all
select 2, 'Completed', '2011-04-15'
union all
select 3, 'In Progress', null
insert into tasks_cstm
select 1, 'Cadaeic'
union all
select 2, 'Cadaeic'
union all
select 3, 'Cadaeic'
Resulting in this:
staff_member Completed Total CompletedPercent
------------------- -------------------- ----------- -----------------------
Cadaeic 1.0 3 0.333333
-- Tasks
declare #T table(ID int, Status varchar(20))
-- Tasks_cst
declare #TC table(ID_C int, Staff_Member varchar(20))
insert into #TC values (1, 'Staff 1')
insert into #TC values (2, 'Staff 2')
insert into #TC values (3, 'Staff 3')
insert into #T values (1, 'Completed')
insert into #T values (1, 'Completed')
insert into #T values (1, 'In Progress')
insert into #T values (2, 'Completed')
insert into #T values (2, 'In Progress')
insert into #T values (3, 'In Progress')
select *
from #TC as TC
cross apply
(select sum(case T.Status when 'Completed' then 1.0 else 0.0 end) / count(*)
from #T as T
where T.ID = TC.ID_C) as C(PrecentCompleted)
Result
ID_C Staff_Member PrecentCompleted
----------- -------------------- ---------------------------------------
1 Staff 1 0.666666
2 Staff 2 0.500000
3 Staff 3 0.000000