Changing timesheet punch in and out query to a crosstab query - sql-server

I am looking to create a crosstab query for a timeclock website I've made. Currently I'm just displaying the result in a table with each day as it's own line.
This is an example of the data.
id type user time
4941 1 user1 2017-10-03 11:24:00
4954 2 user1 2017-10-03 14:43:00
4955 1 user1 2017-10-03 14:43:00
4967 2 user1 2017-10-03 16:00:00
And here is the query that calculates the data for the current week.
SELECT user as UserName, CONVERT (varchar(10), time, 101) AS Date,
SUM(
CASE WHEN type = 1
THEN - 1 * (datepart(hh , [time]) + datepart(mi , [time]) / 60.0)
ELSE datepart(hh , [time]) + datepart(mi , [time]) / 60.0 END) AS Total
FROM table WHERE (username LIKE 'user1')
and datepart(wk, [time]) = datepart(wk, GETDATE())
GROUP BY user, CONVERT (varchar(10), time, 101) ORDER BY Date DESC
This displays data just like I wanted.
UserName Date Total
user1 08/21/2019 4.316667
user1 08/20/2019 9.366666
user1 08/19/2019 8.283333
However now I want to take that data and put it into a crosstab query so I can show the weeks on each row with the daily data in columns on that row. So far I've tried using a pivot to accomplish this.
SELECT Year, Month, Week, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
FROM (SELECT DATENAME(WEEKDAY, CONVERT (varchar(10), time, 101)) DAY, DATEPART(wk, CONVERT (varchar(10), _time, 101)) as Week
, DATEPART(M, CONVERT (varchar(10), time, 101)) as Month, DATEPART(YYYY, CONVERT (varchar(10), time, 101)) as Year
, SUM(
CASE
WHEN type = 1
THEN - 1 * (datepart(hh , [time]) + datepart(mi , [time]) / 60.0)
ELSE datepart(hh , [time]) + datepart(mi , [time]) / 60.0
END) AS Total
FROM table WHERE (username LIKE 'user1')
GROUP BY CONVERT(varchar(10), time, 101) ) p
PIVOT (SUM(Total)
FOR DAY IN (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday) ) pvt
ORDER BY Year, Month, Week DESC
And I have some success. The data seems to be displaying correctly. However when I try to ORDER BY more than one variable the display order is out of whack.
Year Month Week Monday Tuesday Wednesday Thursday Friday Saturday Sunday
2017 10 40 NULL 4.600000 0.000000 NULL NULL NULL NULL
2018 3 11 0.966667 NULL NULL NULL NULL NULL 5.150000
2019 7 31 NULL NULL 8.116667 NULL NULL NULL NULL
2019 8 34 8.283333 9.366666 -7.483333 NULL NULL NULL NULL
2019 8 33 8.166667 8.266667 8.083333 8.616668 8.883334 NULL NULL
2019 8 32 8.350000 8.333333 8.466666 7.883333 7.516666 NULL NULL
2019 8 31 NULL NULL NULL 8.833334 8.000001 NULL NULL
It should be sorting by Year, Week and Month. When I do just the year it works orders fine. But If I add in Week and Month it jumbles up the data in the wrong order. What am I doing wrong? It should look like this.
Year Week Month Monday Tuesday Wednesday Thursday Friday Saturday Sunday
2019 34 8 8.283333 9.366666 -7.483333 NULL NULL NULL NULL
2019 33 8 8.166667 8.266667 8.083333 8.616668 8.883334 NULL NULL
2019 32 8 8.350000 8.333333 8.466666 7.883333 7.516666 NULL NULL
2019 31 8 NULL NULL NULL 8.833334 8.000001 NULL NULL
2019 31 7 NULL NULL 8.116667 NULL NULL NULL NULL
2018 11 3 0.966667 NULL NULL NULL NULL NULL 5.150000
2017 40 10 NULL 4.600000 0.000000 NULL NULL NULL NULL
Also, how can I sum the week value to a totals column?

Related

Find totals for weektodate,monthtodate from sql server tables

I'm trying to get total number of applications made for current day (Monday) to (Last Monday) , and Tuesday to Last week Tuesday and so on. Also, I would like to get applications made for this Month (Month to date ). Please help
I'm using SQL Server 2008 R2
Category Date Day Applications
CASS 16/09/2019 Monday 1
CASS 16/09/2019 Monday 3
RBS 16/09/2019 Monday 1
RBS 16/09/2019 Monday 3
RBS 16/09/2019 Monday 14
RBS 16/09/2019 Monday 15
CASS 23/09/2019 Monday 2
CASS 23/09/2019 Monday 1
CASS 23/09/2019 Monday 2
CASS 23/09/2019 Monday 1
CASS 23/09/2019 Monday 8
RBS 23/09/2019 Monday 3
RBS 23/09/2019 Monday 3
Output:
23/09/2019
CASS: 14
Previous week: 14-4=10
RBS: 6
Previous week: 6-33= -27
Month to date(Total aplications till date): 57
Using LEAD() function you could achieve what you looking for, have a look at following example, you may want adjust PARTITION BY clause and sub-query as per your requirements:
Query Result..
SELECT T.*,
LEAD (AppCount) OVER (PARTITION BY cDay ORDER BY weekNum) as PrevAppCount,
T.AppCount - LEAD (AppCount) OVER (PARTITION BY cDay ORDER BY weekNum)
FROM (
select Category,
datepart(week,cDate) as weekNum,
cDay,
sum (AppCount) as AppCount
from MyTable
Group by Category, Datepart(week,cDate),cDay
) AS T
You can write query like this:
;with cte as (
select *, rown = ROW_NUMBER() over(partition by category order by [Date]) from (
select category, [Date], sum(applications) appcnt from #table
group by category, [Date]
) t
)
Select case when nextweek.Category is null then curweek.Category else
curweek.Category + ' Previous week' end ,
case when nextweek.Category is not null then nextweek.appcnt - curweek.appcnt else curweek.appcnt end
from cte curweek
left join cte nextweek on curweek.rown = nextweek.rown -1
and curweek.Category = nextweek.Category
union all
select 'Month to date', sum(applications) from #table
code:
https://rextester.com/ABI84558

Determine which of four date ranges a single date range overlaps

I have Subscriptions that can last anywhere (as far as my output should be concerned) from 1 to 4 fiscal quarters (could also be seen as four date ranges)
I'm using Arizona Fiscal Quarters:
Q1: Jul 1st - Sep 30th
Q2: Oct 1st - Dec 31st
Q3: Jan 1st - Mar 31st
Q4: Apr 1st - Jun 30th
What I need to figure out is how many subscriptions are active in each quarter based on the subscriptions start and end dates.
For example, using (YYYY-MM-DD), I have:
A subscription that starts on 2016-07-06 and ends on 2017-02-22 I should be able to see I have a subscription active in Q1, Q2, and Q3.
Another subscription that starts on 2016-10-18 and ends on 2016-10-24 would only be seen as active for Q2
Finally, a subscription that starts on 2016-09-28 but has no end date would be seen as active for Q1, Q2, Q3, and Q4 (so whatever starting quarter all the way to Q4)
Below is my current SQL Server Script, and here it is on SQL Fiddle:
WITH SubscriptionInfo AS
(
SELECT
[Subscriptions].[Customer_Id]
,[DistributorTypes].[Name] AS [Distributor Type]
,[Customers].Zip_Id
,[Subscriptions].[UnsubscribeReason_Id]
,[Subscriptions].[Id] AS [Subscription ID]
,CONVERT(DATE, [Subscriptions].[StartDate]) AS [Subscription Start Date]
,CONVERT(DATE, [Subscriptions].[EndDate]) AS [Subscription End Date]
,[PriorityLevels].PriorityLevel AS [Priority Level]
,CONVERT(DATE, [SubscriptionPriorityLevels].StartDate) AS [Priority Level Start Date]
,CONVERT(DATE, [SubscriptionPriorityLevels].EndDate) AS [Priority Level End Date]
,[FundingSources].[Name] AS [Funding Source]
,CONVERT(DATE, [SubscriptionFundingSources].StartDate) AS [SubscriptionFundingSources Start Date]
,CONVERT(DATE, [SubscriptionFundingSources].EndDate) AS [SubscriptionFundingSources End Date]
FROM [Subscriptions]
LEFT JOIN [SubscriptionPriorityLevels]
ON [SubscriptionPriorityLevels].Subscription_Id = Subscriptions.Id
LEFT JOIN [PriorityLevels]
ON [PriorityLevels].Id = SubscriptionPriorityLevels.PriorityLevel_Id
LEFT JOIN [SubscriptionFundingSources]
ON [SubscriptionFundingSources].Subscription_Id = Subscriptions.Id
LEFT JOIN [FundingSources]
ON [FundingSources].Id = [SubscriptionFundingSources].FundingSource_Id
LEFT JOIN [Customers]
ON [Customers].Id = [Subscriptions].Customer_Id
LEFT JOIN [DistributorTypes]
ON [DistributorTypes].Id = Customers.DistributorType_Id
WHERE
([Subscriptions].StartDate >= '2016-07-01') -- Dummy dates, would later be parameters
AND ([Subscriptions].EndDate <= '2017-06-30'
OR [Subscriptions].EndDate IS NULL)
AND ([PriorityLevels].PriorityLevel IN (2, 3)) -- Only care about these two levels
AND ([Customers].DistributorType_Id = 1) -- Distributor Type: Number One Distrubition
AND ([SubscriptionFundingSources].FundingSource_Id = 2) -- Funding Source: First Bank
)
SELECT
[SubscriptionInfo].Customer_Id
,[SubscriptionInfo].[Subscription ID]
,MAX([SubscriptionInfo].[Priority Level]) AS [Highest Priority Level]
,CASE -- Determine which fiscal quarter each Subscription Start Date belongs to
WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (7, 8, 9) THEN 1 -- July, August, September
WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (10, 11, 12) THEN 2 -- October, November, December
WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (1, 2, 3) THEN 3 -- January, Feburary, March
ELSE 4 -- April, May, June
END AS [Fiscal Quarter Start Date]
,CASE -- Determine which fiscal quarter each Subscription Start Date belongs to
WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (7, 8, 9) THEN 1 -- July, August, September
WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (10, 11, 12) THEN 2 -- October, November, December
WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (1, 2, 3) THEN 3 -- January, Feburary, March
ELSE 4 -- April, May, June
END AS [Fiscal Quarter End Date]
FROM [SubscriptionInfo]
GROUP BY
[SubscriptionInfo].Customer_Id
,[SubscriptionInfo].[Subscription ID]
,CASE -- Group Subscription Start Date's into Fiscal Quarters
WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (7, 8, 9) THEN 1 -- July, August, September
WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (10, 11, 12) THEN 2 -- October, November, December
WHEN MONTH([SubscriptionInfo].[Subscription Start Date]) IN (1, 2, 3) THEN 3 -- January, Feburary, March
ELSE 4 -- April, May, June
END
,CASE -- Group Subscription End Date's into Fiscal Quarters
WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (7, 8, 9) THEN 1 -- July, August, September
WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (10, 11, 12) THEN 2 -- October, November, December
WHEN MONTH([SubscriptionInfo].[Subscription End Date]) IN (1, 2, 3) THEN 3 -- January, Feburary, March
ELSE 4 -- April, May, June
END
ORDER BY
[SubscriptionInfo].Customer_Id
So far I'm able to identify in what fiscal quarter a subscription started and what fiscal quarter it ended.
I want to be able to count how many subscriptions were active in each quarter.
Desired Output:
| FirstQuarter | SecondQuarter | ThirdQuarter | FourthQuarter |
|--------------|---------------|--------------|---------------|
| 2 | 1 | 3 | 3 |
Once you have a table describing the quarters' start and end dates as #Sean Lange suggested, you just need to compare your subscriptions' start and end date to the quarters' start and end date and then count them up where they overlap. Basically, two ranges have some overlap if A.Start < B.End and A.End > B.Start.
Here's a complete example of the pattern.
-------------------------
-- construct the quarters table (taken from the SQL Fiddle in comments above)
DECLARE #FiscalDate Table
(
Id INT NOT NULL,
FiscalYear INT NOT NULL,
FiscalQuarter INT NOT NULL,
QuarterStartDate DATE NOT NULL,
QuarterEndDate DATE NOT NULL
PRIMARY KEY(Id)
);
INSERT INTO #FiscalDate
(Id, FiscalYear, FiscalQuarter, QuarterStartDate, QuarterEndDate)
VALUES
( 1, 2016, 1, '2016-07-01', '2016-09-30')
,(2, 2016, 2, '2016-10-01', '2016-12-31')
,(3, 2017, 3, '2017-01-01', '2017-03-31')
,(4, 2017, 4, '2017-04-01', '2017-06-30')
;
-------------------------
--Get some random test data to imitate subscriptions ranges
DECLARE #tempSet Table(a date, b date)
INSERT INTO #tempSet SELECT TOP 15
dateadd(dd, ROUND(365 * RAND(convert(varbinary, newid())), 0), '2016-07-01') as a
, dateadd(dd, ROUND(365 * RAND(convert(varbinary, newid())), 0), '2016-07-01') as b
FROM sysobjects
-------------------------
-- Fix our random data a little (start date needs to be before end date)
DECLARE #DateRanges Table(StartDate date, EndDate date)
INSERT INTO #DateRanges
SELECT a, b FROM #tempset WHERE a <= b UNION SELECT b, a FROM #tempset WHERE b < a
-------------------------
-- Show our Date ranges in a useful order for review
SELECT * FROM #DateRanges ORDER BY StartDate, EndDate
-------------------------
-- Show our by-quarter counts.
SELECT
fd.FiscalQuarter
, count(*) as ActiveSubsCount
FROM
#FiscalDate fd
JOIN
#DateRanges dr
on fd.QuarterStartDate < dr.EndDate
and fd.QuarterEndDate >= dr.StartDate
GROUP BY
fd.FiscalQuarter
-------------------------
-- and in your desired output (without your quarters table)
SELECT
COUNT(CASE WHEN '2016-07-01' < dr.EndDate AND '2016-09-30' >= dr.StartDate THEN 1 ELSE NULL END) AS FirstQuarter
, COUNT(CASE WHEN '2016-10-01' < dr.EndDate AND '2016-12-31' >= dr.StartDate THEN 1 ELSE NULL END) AS SecondQuarter
, COUNT(CASE WHEN '2017-01-01' < dr.EndDate AND '2017-03-31' >= dr.StartDate THEN 1 ELSE NULL END) AS ThirdQuarter
, COUNT(CASE WHEN '2017-04-01' < dr.EndDate AND '2017-06-30' >= dr.StartDate THEN 1 ELSE NULL END) AS FourthQuarter
FROM
#DateRanges dr

Determine number of Days in month returns different count for Month of March

Here is a weird one for you all.
I need to determine the number of days in a Month
;WITH cteNetProfit AS
(
---- NET PROFIT
SELECT DT.CreateDate
, SUM(DT.Revenue) as Revenue
, SUM(DT.Cost) as Cost
, SUM(DT.GROSSPROFIT) AS GROSSPROFIT
FROM
(
SELECT CAST([createDTG] AS DATE) as CreateDate
, SUM(Revenue) as Revenue
, SUM(Cost) as Cost
, SUM(REVENUE - COST) AS GROSSPROFIT
FROM [dbo].[CostRevenueSpecific]
WHERE CAST([createDTG] AS DATE) > CAST(GETDATE() - 91 AS DATE)
AND CAST([createDTG] AS DATE) <= CAST(GETDATE() - 1 AS DATE)
GROUP BY createDTG
UNION ALL
SELECT CAST([CallDate] AS DATE) AS CreateDate
, SUM(Revenue) as Revenue
, SUM(Cost) as Cost
, SUM(REVENUE - COST) AS GROSSPROFIT
FROM abc.PublisherCallByDay
WHERE CAST([CallDate] AS DATE) > CAST(GETDATE() - 91 AS DATE)
AND CAST([CallDate] AS DATE) <= CAST(GETDATE() - 1 AS DATE)
GROUP BY CALLDATE
) DT
GROUP BY DT.CreateDate
)
select distinct MONTH(CREATEDATE), DateDiff(Day,CreateDate,DateAdd(month,1,CreateDate))
FROM cteNetProfit
For some reason it is returning two different results for the month of March 2016 one result is 30 and the other 31(which of course is correct) I validate that the underlying data only has 31 days worth of data for the Month of March. Since Feb is a leap year can this affect the DATEDIFF function. The remaining months return the correct #.
2 29
3 31
3 30
4 30
5 31
Thanks for the input, however, I found the solution elsewhere
select Distinct MONTH(CREATEDATE), Day(EOMONTH(CreateDate))
FROM cteNetProfit
The difference comes when you hit the 2016-03-31 date. If you run the query below for 2016-03-30 and 2016-03-31, the results of adding 1 MONTH using DATEADD, in both instances, is 2016-04-30. It returns the last day of the next month.
SELECT DATEADD(MONTH,1,'2016-03-30') , DATEADD(MONTH,1,'2016-03-31')
This syntax seemed to work (courtesy of https://raresql.com/2013/01/06/sql-server-get-number-of-days-in-month/).
SELECT DAY(DATEADD(ms,-2,DATEADD(MONTH, DATEDIFF(MONTH,0,#DATE)+1,0))) AS [Current Month]

Performing operations in a Pivot

I am trying to implement a PIVOT but I am having trouble performing basic operations in it.
Current table:
week_no username days pick_count duration
------------------------------------------------
Week 50 Beck W Wednesday 227 7978
Week 50 Beck W Friday 320 7481
Week 50 Beck W Friday 282 5718
Week 50 Cockram D Thursday 165 10478
Week 50 Cowell P Thursday 145 14403
Week 50 Cowell P Thursday 159 7450
Week 50 Cowell P Friday 217 13101
...
Expected result:
week_no username monday tuesday wednesday thursday friday saturday sunday
--------------------------------------------------------------------------
Week 50 Beck W NULL NULL 102 NULL 164 NULL NULL
Week 50 Cockram D NULL NULL NULL 56 NULL NULL NULL
Week 50 Cowell P NULL NULL NULL 50 59 NULL NULL
...
The expected result should be calculated as followed: pick_count * 3600 / duration. That is the calculation I am having problem computing. When PIVOTing like so
SELECT
*
FROM
(
SELECT
week_no,
username,
days,
pick_count,
duration
FROM
table
) AS src
PIVOT
(
SUM(pick_count) * 3600 / SUM(duration) FOR days IN (monday, tuesday, wednesday, thursday, friday, saturday, sunday)
) AS pvt
I get Incorrect syntax near '*'.
PIVOT is not very flexible.
You can just use the old style cross tab approach.
SELECT week_no,
username,
SUM(CASE WHEN days = 'monday' then pick_count end) * 3600
/ SUM(CASE WHEN days = 'monday' then duration end) as monday,
SUM(CASE WHEN days = 'tuesday' then pick_count end) * 3600
/ SUM(CASE WHEN days = 'tuesday' then duration end) as tuesday
/*TODO: Add other five days*/
FROM YourTable
GROUP BY week_no,
username
I eventually found what I was looking for. Indeed, all I needed to do was to compute the calculation in the SELECT instead of the PIVOT.
SELECT
*
FROM
(
SELECT
week_no,
username,
days,
SUM(pick_count) * 3600 / SUM(duration) AS pick_rate
FROM
table
GROUP BY
week_no,
username,
days
) AS src
PIVOT
(
MAX(pick_rate) FOR days IN (monday, tuesday, wednesday, thursday, friday, saturday, sunday)
) AS pvt

get total orders for the last 1 month every week

I have an "Orders" table containing column CreatedDate (datetime) on when the order took place.
Today is Feb 13, 2014.
How to get total records for the last 4 weeks for every week.
it should return something like this:
PerDate Total
2014-01-26 13 <--- sunday
2014-02-02 24 <--- sunday
2014-02-09 33 <--- sunday
2014-02-13 35 <--- this is today
13 from the first record is the total record from 2014-01-20 00:00:00 AM (monday) to 2014-01-26 12:00:00 PM (sunday)
24 from the 2nd record is the total record from 2014-01-27 00:00:00 AM (monday) to 2014-02-02 12:00:00 PM (sunday)
33 from the 3rd record is the total record from 2014-02-03 00:00:00 AM (monday) to 2014-02-09 12:00:00 PM (sunday)
35 from the 4th record is the total record from 2014-02-10 00:00:00 AM (monday) to 2014-02-13 (today)
so they are in Ascending order.
You should use DATEPART(dw,CreatedDate) function to calculate date of begin and end of the week for each CreatedDate and then just group by this field:
WITH T AS
(
SELECT
cast(floor(cast(CreatedDate as float)) as datetime)
-DATEPART(dw,CreatedDate)+1 as BeginOfweek,
cast(floor(cast(CreatedDate as float)) as datetime)
-DATEPART(dw,CreatedDate)+8 as EndOfWeek
FROM ORDERS
WHERE cast(floor(cast(CreatedDate as float)) as datetime) BETWEEN
DATEADD(WEEK,-4,GETDATE())
AND
GETDATE()
)
SELECT
BeginOfWeek,
MIN(CASE WHEN GETDATE()<EndOfWeek
THEN GETDATE()
ELSE EndOfWeek
END) as EndOfWeek,
Count(*) as OrdersCount
FROM T
Group by BeginOfWeek
SQLFiddle demo

Resources