I have the following SQL query which Display 12 months of summary report from the given date. Output shows starting from January to December for whatever the given date. I want the outcome to start from the given date's month.
If the given date is '2016-05-01' I want the output to be like this
May 16 |June 16| July 16| ........... | Jan 17 | Feb 17 | March 17 | April 17 |
How can I achieve this?
Can some one suggest?
SELECT Name,SUM(Amount) AS PremiumTot,TotType,
sum(case when month(Dates) = 1 then Tot else 0 end) Jan,
sum(case when month(Dates) = 2 then Tot else 0 end) Feb,
sum(case when month(Dates) = 3 then Tot else 0 end) March,
sum(case when month(Dates) = 4 then Tot else 0 end) April,
sum(case when month(Dates) = 5 then Tot else 0 end) May,
sum(case when month(Dates) = 6 then Tot else 0 end) June,
sum(case when month(Dates) = 7 then Tot else 0 end) July,
sum(case when month(Dates) = 8 then Tot else 0 end) Aug,
sum(case when month(Dates) = 9 then Tot else 0 end) Sep,
sum(case when month(Dates) = 10 then Tot else 0 end) Oct,
sum(case when month(Dates) = 11 then Tot else 0 end) Nov,
sum(case when month(Dates) = 12 then Tot else 0 end) Dece
FROM
(
SELECT InvoiceMasterID,Dates ,Name,CompanyCommission AS Tot ,0 AS Expences,Amount,1 as TotType
FROM CommissionView
UNION ALL
SELECT InvoiceMasterID,Dates,Name, 0 AS Tot ,-AgentCommission AS Expences,Amount,2 as TotType
FROM CommissionViewCredit
) a
WHERE Dates between #fromDates AND Datesadd(yy,1,#fromDates)
GROUP BY Name,TotType
Seems, you want to pivot data. So, use PIVOT table!
If you want to create dynamic columns from given date, use CTE (Common Table Expressions)!
--declare variables for given date range
DECLARE #startDate DATE = '2016-05-01'
DECLARE #endDate DATE = DATEADD(mm,11,#startDate)
--declare variable to store months as: [month1], [month2], [etc.]
DECLARE #Months VARCHAR(1000) = ''
--use cte to create range of dates
;WITH MyDates AS
(
SELECT #startDate AS MyDate
UNION ALL
SELECT DATEADD(mm,1,MyDate) AS MyDate
FROM MyDates
WHERE MyDate<#endDate
)
SELECT #Months = STUFF((SELECT '],[' + CONVERT(VARCHAR(7), MyDate, 121)
FROM MyDates
FOR XML PATH('')), 1, 2, '') + ']'
--PRINT #Months:
-- prints: [2016-05],[2016-06], ... ,[2017-04]
DECLARE #qry NVARCHAR(MAX) = ''
SET #qry = N'SELECT ' + #Months +
' FROM ( ' +
' SELECT CONVERT(VARCHAR(7), Dates, 121) AS MyDate, CompanyCommission AS Tot ' +
'FROM CommissionView ' +
') AS DT ' +
'PIVOT (SUM(Tot) FOR MyDate IN(' + #Months + ')) AS PT'
EXEC (#qry)
For further information, please see:
Dynamic PIVOT
Pivots with dynamic columns
CAST and CONVERT
Good luck!
Related
This question already has answers here:
SQL Server dynamic PIVOT query?
(9 answers)
Closed 12 months ago.
So I have 36 month worth of data in my CTE, and the database is still active storing new data daily.
In my analysis, I just need 6 months worth of data from getdate()
So my question is that how do I make the month derived from calendar month the column of my table?
So for end of March 2022, this is the view that I should see:
id
name
10/01/21
11/01/21
12/01/21
01/01/22
02/01/22
03/01/2022
1
John
3
0
1
0
0
2
2
Mary
6
1
2
1
1
2
3
Angelo
1
5
3
2
2
0
4
Diane
3
2
0
1
0
6
So for the end of April 2022, this is the view that I should see:
id
name
11/01/21
12/01/21
01/01/22
02/01/22
03/01/2022
04/01/22
1
John
0
1
0
0
2
7
2
Mary
1
2
1
1
2
2
3
Angelo
5
0
0
0
0
3
4
Diane
2
0
1
0
6
4
You can't have a dynamic PIVOT without dynamic SQL. I find it's easiest to break it up into parts.
Get the last 6 months
DECLARE #thisMonth date, #firstMonth date;
SET #thisMonth = DATEFROMPARTS(YEAR(getdate()), MONTH(getdate()), 1);
SET #firstMonth = DATEADD(MONTH, -5, #thisMonth);
;WITH m(m) AS
(
SELECT #firstMonth
UNION ALL
SELECT DATEADD(MONTH, 1, m) FROM m
WHERE m < #thisMonth
)
SELECT m FROM m ORDER BY m DESC;
Output:
m
2022-03-01
2022-02-01
2022-01-01
2021-12-01
2021-11-01
2021-10-01
Figure out what manual query you need. Given this sample data:
CREATE TABLE dbo.JetSales
(
ID int,
Name nvarchar(32),
SalesDate date
);
INSERT dbo.JetSales(ID, Name, SalesDate) VALUES
(1,N'John','20211005'),(1,N'John','20211016'),(1,N'John','20211031'),
(2,N'Mary','20211007'),(2,N'Mary','20211013'),
(3,N'Tank','20211009');
I think you want a query like this (yes, you can accomplish this specific task with PIVOT too, but PIVOT doesn't cover some other scenarios, and it also requires pre-aggregation in this case... so I think conditional aggregation is better):
SELECT ID, Name,
[10/01/2021] = SUM(CASE WHEN SalesDate >= '20211001'
AND SalesDate < '20211101' THEN 1 ELSE 0 END),
[11/01/2021] = SUM(CASE WHEN SalesDate >= '20211101'
AND SalesDate < '20211201' THEN 1 ELSE 0 END),
[12/01/2021] = SUM(CASE WHEN SalesDate >= '20211201'
AND SalesDate < '20220101' THEN 1 ELSE 0 END),
[01/01/2022] = SUM(CASE WHEN SalesDate >= '20220101'
AND SalesDate < '20220201' THEN 1 ELSE 0 END),
[02/01/2022] = SUM(CASE WHEN SalesDate >= '20220201'
AND SalesDate < '20220301' THEN 1 ELSE 0 END),
[03/01/2022] = SUM(CASE WHEN SalesDate >= '20220301'
AND SalesDate < '20220401' THEN 1 ELSE 0 END)
FROM dbo.JetSales AS js GROUP BY ID, Name;
Which you can build as follows:
DECLARE #thisMonth date, #firstMonth date;
SET #thisMonth = DATEFROMPARTS(YEAR(getdate()), MONTH(getdate()), 1);
SET #firstMonth = DATEADD(MONTH, -5, #thisMonth);
DECLARE #sql nvarchar(max) = N'SELECT ID, Name';
;WITH m(m) AS
(
SELECT #firstMonth
UNION ALL
SELECT DATEADD(MONTH, 1, m) FROM m
WHERE m < #thisMonth
)
SELECT #sql += N',
' + QUOTENAME(CONVERT(char(10), m, 101))
+ N' = SUM(CASE WHEN SalesDate >= '
+ QUOTENAME(CONVERT(char(8), m, 112), char(39)) + N'
AND SalesDate < '
+ QUOTENAME(CONVERT(char(8), DATEADD(MONTH, 1, m), 112), char(39))
+ N' THEN 1 ELSE 0 END)'
FROM m;
SET #sql += N'
FROM dbo.JetSales AS js GROUP BY ID, Name;';
SELECT #sql;
EXEC sys.sp_executesql #sql;
Working example: db<>fiddle
If you really want to use PIVOT explicitly, you can, it's just a lot more cumbersome. Here's the query you want to end up with:
;WITH src AS
(
SELECT ID, Name, m = CONVERT(char(10),
DATEFROMPARTS(YEAR(SalesDate), Month(SalesDate), 1), 101)
FROM dbo.JetSales
WHERE SalesDate >= #firstMonth
),
agg AS
(
SELECT ID, Name, m, c = COUNT(*)
FROM src GROUP BY ID, Name, m
)
SELECT ID, Name,
[10/01/2021] = COALESCE([10/01/2021], 0),
[11/01/2021] = COALESCE([11/01/2021], 0),
[12/01/2021] = COALESCE([12/01/2021], 0),
[01/01/2022] = COALESCE([01/01/2022], 0),
[02/01/2022] = COALESCE([02/01/2022], 0),
[03/01/2022] = COALESCE([03/01/2022], 0)
FROM agg PIVOT (SUM(c) FOR m IN (
[10/01/2021],[11/01/2021],[12/01/2021],
[01/01/2022],[02/01/2022],[03/01/2022]
)) AS p;
To get there:
DECLARE #thisMonth date, #firstMonth date;
SET #thisMonth = DATEFROMPARTS(YEAR(getdate()), MONTH(getdate()), 1);
SET #firstMonth = DATEADD(MONTH, -5, #thisMonth);
DECLARE #col1 nvarchar(max) = N'',
#col2 nvarchar(max) = N'',
#sql nvarchar(max) = N';WITH src AS
(
SELECT ID, Name, m = CONVERT(char(10),
DATEFROMPARTS(YEAR(SalesDate), Month(SalesDate), 1), 101)
FROM dbo.JetSales
WHERE SalesDate >= #firstMonth
),
agg AS
(
SELECT ID, Name, m, c = COUNT(*)
FROM src GROUP BY ID, Name, m
)
SELECT ID, Name,';
;WITH m(m) AS
(
SELECT #firstMonth
UNION ALL
SELECT DATEADD(MONTH, 1, m) FROM m
WHERE m < #thisMonth
),
x(x) AS
(
SELECT QUOTENAME(CONVERT(char(10), m, 101)) FROM m
)
SELECT
#col1 += STRING_AGG(CONCAT(N'
', x, N' = COALESCE(', x, ',0)'),N','),
#col2 += STRING_AGG(x, N',
')
FROM x;
SET #sql += #col1 + N'
FROM agg PIVOT (SUM(c) FOR m IN ('
+ #col2 + N'
)) AS p;';
SELECT #sql;
EXEC sys.sp_executesql #sql, N'#firstMonth date', #firstMonth;
Another fiddle here: db<>fiddle
I'm learning about PIVOT function and I want to try it in my DB, in the table DDOT I have events (rows) made by users during X month Y year in the YYYYMM format.
id_ev iddate id_user ...
------------------------
1 201901 321
2 201902 654
3 201903 987
4 201901 321
5 201903 987
I'm basing my query on the MS Documentation and I'm not getting errors but I'm not able to fill it with the SUM of those unique events (users). In simple words I want to know how many users (unique) checked up each month (x axis) in the year (y axis). However, I'm getting NULL as result
YYYY jan feb mar
----------------------------
2019 NULL NULL NULL
I'm expecting a full table with what I mentionted before.
YYYY jan feb mar
----------------------------
2019 2 1 1
In the code I've tried with different aggregate functions but this block is the closest to a result from SQL.
CREATE TABLE ddot
(
id_ev int NOT NULL ,
iddate int NOT NULL ,
id_user int NOT NULL
);
INSERT INTO DDOT
(
[id_ev], [iddate], [id_user]
)
VALUES
(
1, 201901, 321
),
(
2, 201902, 654
),
(
3, 201903, 987
),
(
4, 201901, 321
),
(
5, 201903, 987
)
GO
SELECT *
FROM (
SELECT COUNT(DISTINCT id_user) [TOT],
DATENAME(YEAR, CAST(iddate+'01' AS DATETIME)) [YYYY], --concat iddate 01 to get full date
DATENAME(MONTH, CAST(iddate+'01' AS DATETIME)) [MMM]
FROM DDOT
GROUP BY DATENAME(YEAR, CAST(iddate+'01' AS DATETIME)),
DATENAME(MONTH, CAST(iddate+'01' AS DATETIME))
) AS DOT_COUNT
PIVOT(
SUM([TOT])
FOR MMM IN (jan, feb, mar)
) AS PVT
Ideally you should be using an actual date in the iddate column, and not a string (number?). We can workaround this using the string functions:
SELECT
CONVERT(varchar(4), LEFT(iddate, 4)) AS YYYY,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '01' THEN 1 END) AS jan,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '02' THEN 1 END) AS feb,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '03' THEN 1 END) AS mar,
...
FROM DDOT
GROUP BY
CONVERT(varchar(4), LEFT(iddate, 4));
Note that if the iddate column already be text, then we can remove all the ugly calls to CONVERT above:
SELECT
LEFT(iddate, 4) AS YYYY,
COUNT(CASE WHEN RIGHT(iddate, 2) = '01' THEN 1 END) AS jan,
COUNT(CASE WHEN RIGHT(iddate, 2) = '02' THEN 1 END) AS feb,
COUNT(CASE WHEN RIGHT(iddate, 2) = '03' THEN 1 END) AS mar,
...
FROM DDOT
GROUP BY
LEFT(iddate, 4);
I have a following table:-
CREATE TABLE #TmpData (
RType Varchar(10),
Client VARCHAR(10),
TRequest INT,
Due INT,
Month INT
);
INSERT INTO #TmpData VALUES ('P', 'Name1',54,67,1),('P', 'Name5',5,47,1),('A', 'Name6',3,90,1 ),('A', 'Name3',4,43,1 ),('P', 'Name1',0,10,2)
,('P', 'Name2',1,78,2),('P', 'Name4',54,67,3),('P', 'Name3',5,47,3),('A', 'Name6',3,90,3 ),('P', 'Name8',3,233,3 ),('P', 'Name1',23,457,4)
,('P', 'Name7',3,6,4 ),('P', 'Name8',65,548,4 ),('P', 'Name2',3,56,5 ),('P', 'Name3',65,87,6 ),('P', 'Name4',3,56,7 ),('P', 'Name9',65,87,7 )
SELECT * FROM #TmpData
I want to Convert its data row wise to column and show it monthwise. I am attaching the screenshot of my resultset which I need.
Result Set
Here for example I have taken 7 months but it can go upto 12 months.
Please Help.
Thanks
You can try to use CASE WHEN with SUM function.
CASE WHEN compare the month then SUM that TRequest or Due group by by
RType,Client
SELECT RType,
Client,
SUM(CASE WHEN Month = 1 THEN TRequest ELSE 0 END) 'Jan Totle',
SUM(CASE WHEN Month = 1 THEN Due ELSE 0 END) '#of Request',
SUM(CASE WHEN Month = 2 THEN TRequest ELSE 0 END) 'Feb Totle',
SUM(CASE WHEN Month = 2 THEN Due ELSE 0 END) '#of Request',
SUM(CASE WHEN Month = 3 THEN TRequest ELSE 0 END) 'March Totle',
SUM(CASE WHEN Month = 3 THEN Due ELSE 0 END) '#of Request',
SUM(CASE WHEN Month = 4 THEN TRequest ELSE 0 END) 'April Totle',
SUM(CASE WHEN Month = 4 THEN Due ELSE 0 END) '#of Request',
SUM(CASE WHEN Month = 5 THEN TRequest ELSE 0 END) 'May Totle',
SUM(CASE WHEN Month = 5 THEN Due ELSE 0 END) '#of Request',
SUM(CASE WHEN Month = 6 THEN TRequest ELSE 0 END) 'June Totle',
SUM(CASE WHEN Month = 6 THEN Due ELSE 0 END) '#of Request',
SUM(CASE WHEN Month = 7 THEN TRequest ELSE 0 END) 'July Totle',
SUM(CASE WHEN Month = 7 THEN Due ELSE 0 END) '#of Request',
SUM(CASE WHEN Month = 8 THEN TRequest ELSE 0 END) 'Aug Totle',
SUM(CASE WHEN Month = 8 THEN Due ELSE 0 END) '#of Request',
SUM(CASE WHEN Month = 9 THEN TRequest ELSE 0 END) 'Sep Totle',
SUM(CASE WHEN Month = 9 THEN Due ELSE 0 END) '#of Request',
....
FROM TmpData
GROUP BY RType,
Client
sqlfiddle
EDIT
if you want to make month column dynamic, you can use dynamic pivot.
DECLARE #cols AS NVARCHAR(MAX),
#Pivotcols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #Pivotcols = STUFF((SELECT DISTINCT
',SUM(CASE WHEN Month = ' + CAST(Month AS VARCHAR(5)) + ' THEN TRequest ELSE 0 END) as ''' + FORMAT(DATEADD(mm,Month,'1900-01-01'),'MMM') + ' Totle ''',
',SUM(CASE WHEN Month = ' + CAST(Month AS VARCHAR(5)) + ' THEN Due ELSE 0 END) as ''' + FORMAT(DATEADD(mm,Month,'1900-01-01'),'MMM') + ' Totle '''
FROM TmpData
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
set #query = 'SELECT RType,Client, '+ #Pivotcols + ' FROM TmpData GROUP BY RType,Client'
execute(#query)
SQLFIDDLE dynamic pivot
I am trying to group tickets per week in a year when data field X are between that week. The final result should be something like Week,Datefield1,Datefield2..
What I reached so far:
DECLARE #YearStartDate datetime
SET #YearStartDate = '2016-01-04 00:00:00.000'
DECLARE #YearEndDate datetime
SET #YearEndDate = '2016-04-24 00:00:00.000'
SELECT
Year(T.CreateDate) as 'Year',
'Week ' + cast(datepart(wk, CreateDate) as varchar(2)) as 'Week',
Sum(CASE WHEN CreateDate BETWEEN #YearStartDate and #YearEndDate
THEN 1 Else 0 End) 'Created'
FROM mytable AS T
GROUP BY Year(T.CreateDate),datepart(wk,T.CreateDate)
ORDER BY Year(T.CreateDate),datepart(wk, T.CreateDate)
Result:
Year Week Created
----------- ------- -----------
2016 Week 1 0
2016 Week 2 5
2016 Week 3 3
2016 Week 4 2
Goal:
Year Week Created Schuduled Closed
----------- ------- ----------- ----------- -----------
2016 Week 1 0 0 0
2016 Week 2 5 3 2
2016 Week 3 3 2 2
2016 Week 4 2 2 0
Suggestion?
SELECT
Year(..) as 'Year',
'Week ' + cast(datepart(wk, ...) as varchar(2)) as 'Week',
Sum(CASE WHEN CreateDate BETWEEN #YearStartDate and #YearEndDate
THEN 1 Else 0 End) 'Created'
Sum(CASE WHEN ScheduledDate BETWEEN #YearStartDate and #YearEndDate
THEN 1 Else 0 End) 'Scheduled'
Sum(CASE WHEN ClosedDate BETWEEN #YearStartDate and #YearEndDate
THEN 1 Else 0 End) 'Closed'
FROM mytable AS T
GROUP BY Year(...)
ORDER BY Year(...)
Constraints:
I can count the entries per week for one datafield but for several datafields (created,scheduled,pending,closed) I think I need to change the logic. Maybe create a inner join or left join against the same table.
I was wondering if you could give me some guidance on finding the right path. Thanks for taking the time to help me.
All the best.
Right, join on other similar SELECTs for scheduled and closed should work. Something as
DECLARE #YearStartDate datetime = '2016-01-04 00:00:00.000';
DECLARE #YearEndDate datetime = '2016-04-24 00:00:00.000';
SELECT CLR.*, SCH.Schuduled, CLS.Closed FROM
(SELECT
Year(T.CreateDate) as 'Year',
'Week ' + cast(datepart(wk, CreateDate) as varchar(2)) as 'Week',
Sum(CASE WHEN CreateDate BETWEEN #YearStartDate and #YearEndDate THEN 1 Else 0 End) 'Created'
FROM mytable AS T
GROUP BY Year(T.CreateDate),datepart(wk,T.CreateDate)
) CRT
JOIN
(SELECT
Year(T.ScheduleDate) as 'Year',
'Week ' + cast(datepart(wk, ScheduleDate) as varchar(2)) as 'Week',
Sum(CASE WHEN ScheduleDate BETWEEN #YearStartDate and #YearEndDate THEN 1 Else 0 End) 'Schuduled'
FROM mytable AS T
GROUP BY Year(T.ScheduleDate),datepart(wk,T.ScheduleDate)
) SCH
ON CRT.Year = SCH.Year
AND CRT.Week = SCH.Week
JOIN
(SELECT
Year(T.ClosedDate) as 'Year',
'Week ' + cast(datepart(wk, ClosedDate) as varchar(2)) as 'Week',
Sum(CASE WHEN ClosedDate BETWEEN ClosedDate and #YearEndDate THEN 1 Else 0 End) 'Closed'
FROM mytable AS T
GROUP BY Year(T.ClosedDate),datepart(wk,T.ClosedDate)
) CLS
ON CRT.Year = CLS.Year
AND CRT.Week = CLS.Week
ORDER BY Year, Week
You have three different date columns. One method would pre-aggregation along the dimensions and then use full outer join. Alternatively, you can use union all and aggregation:
SELECT Year(dte) as [Year],
'Week ' + datename(wk, dte) as [Week],
Sum(Created) as Created,
Sum(Scheduled) as Scheduled,
Sum(Closed) as Closed
FROM ((SELECT createddate as dte, 1 as created, 0 as schedule, 0 as closed
FROM mytable
) UNION ALL
(SELECT scheduleddate as dte, 0 as created, 1 as schedule, 0 as closed
FROM mytable
) UNION ALL
(SELECT closeddate as date, 1 as created, 0 as schedule, 0 as closed
FROM mytable
)
) t
GROUP BY Year(date), datename(wk, dte)
ORDER BY Year(date), datename(wk, dte);
I would encourage a couple of things:
Do not use single quotes for column aliases. Only use single quotes for string and date names.
Do not use reserved works such as "year" for column or table names.
Also note the use of datename(). This returns a string so no conversion is needed.
my table looks like this:
select clocktime, for_UID, in1_out0 from timeclockentries
clocktime for_UID in1_out0
2011-08-07 15:13:58.390 user193 1
2011-08-07 21:09:45.093 user193 0
2011-08-09 14:10:00.000 user193 1
2011-08-09 20:10:00.000 user193 0
I want the results to look like (assuming start of week is Saturday), separated by COLUMNS named 'day1', 'day2', etc.... (but for readability, i've typed them out with linefeeds) :
day1 day2 day3
1900-01-01 00:00:00.000 1900-01-01 05:55:46.700 1900-01-01 00:00:00.000
day4 day5 day6
1900-01-01 06:00:00.000 1900-01-01 00:00:00.000 1900-01-01 00:00:00.000
day7
1900-01-01 00:00:00.000
(i'm using sql2005)
below is what i'm using for a single day:
CREATE PROCEDURE [dbo].[sp_gethoursbyday]
#whichforUID varchar(20),
#whichdate datetime
AS
BEGIN
;WITH CTE as(
SELECT
DENSE_RANK() over (Partition by for_UID , in1_out0 Order by clocktime) id,
clocktime,
for_UID,
in1_out0
FROM
kdhcastle.dbo.timeclockentries tc
WHERE
tc.for_UID = #whichforUID
and month(tc.[clocktime]) = month(#whichdate)
and day(tc.[clocktime]) = day(#whichdate)
and year(tc.[clocktime]) = year(#whichdate)
)
SELECT
Cast(cast(sum(
cast(outTime.clocktime as float) - cast(inTime.clocktime as float)
)as datetime) as datetime) as 'hoursbydy'
FROM
CTE inTime
INNER JOIN CTE outTime
ON inTime.for_UID = outTime.for_UID
AND inTime.id = outTime.id
AND inTime.in1_out0 = 1
and outTime.in1_out0 = 0
END
SELECT
SUM(CASE WHEN DayOfWeek = 1 THEN Duration ELSE 0 END) AS Day1,
SUM(CASE WHEN DayOfWeek = 2 THEN Duration ELSE 0 END) AS Day2,
SUM(CASE WHEN DayOfWeek = 3 THEN Duration ELSE 0 END) AS Day3,
SUM(CASE WHEN DayOfWeek = 4 THEN Duration ELSE 0 END) AS Day4,
SUM(CASE WHEN DayOfWeek = 5 THEN Duration ELSE 0 END) AS Day5,
SUM(CASE WHEN DayOfWeek = 6 THEN Duration ELSE 0 END) AS Day6,
SUM(CASE WHEN DayOfWeek = 7 THEN Duration ELSE 0 END) AS Day7
FROM
(
SELECT
DATEDIFF(DAY, '2011 Jan 01', clocktime) % 7 + 1 AS DayOfWeek,
CAST(MAX(clocktime) - MIN(clocktime) AS FLOAT) AS Duration
FROM
yourTable
GROUP BY
for_UID,
DATEDIFF(DAY, '2011 Jan 01', clocktime)
)
AS [data]
This is more verbose but my focus was (a) to avoid repeating expressions and (b) to simulate all of the input parameters intended to be fed to the stored procedure so that the results are filtered on the desired user / date. Note that the #whichdate parameter is reeled back to the preceding Saturday at midnight, regardless of which day of the week it is or what time is associated with it.
Input parameters:
DECLARE #whichdate DATETIME;
SET #whichdate = '2011-08-08T12:34:00';
DECLARE #whichforUID VARCHAR(32);
SET #whichforUID = 'user193';
Body (just comment out the DECLARE #t / INSERT #t lines, and change #t in the first CTE to the real table name:
SET #whichdate = DATEADD(DAY, -DATEPART(WEEKDAY, #whichdate), #whichdate);
SET #whichdate = DATEADD(DAY, 0, DATEDIFF(DAY, 0, #whichdate));
DECLARE #t TABLE(clocktime DATETIME, for_UID VARCHAR(32), in1_out0 BIT);
INSERT #t SELECT '2011-08-07 15:13:58.390','user193',1
UNION ALL SELECT '2011-08-07 21:09:45.093','user193',0
UNION ALL SELECT '2011-08-09 14:10:00.000','user193',1
UNION ALL SELECT '2011-08-09 20:10:00.000','user193',0;
WITH s(dw, ct, in1_out0) AS
(
SELECT 1 + (DATEDIFF(DAY, '2011-01-01', clocktime) % 7),
clocktime, in1_out0 FROM #t
where for_UID = #whichforUID
AND clocktime >= #whichdate
AND clocktime < DATEADD(DAY, 7, #whichdate)
),
d(dw, min_ct, max_ct) AS
(
SELECT dw,
MIN(CASE WHEN in1_out0 = 1 THEN ct ELSE NULL END),
MAX(CASE WHEN in1_out0 = 0 THEN ct ELSE NULL END)
FROM s GROUP BY dw
),
x AS
(
SELECT d = DATEADD(MILLISECOND, DATEDIFF(MILLISECOND, min_ct, max_ct), 0),
dw FROM d
),
pvt AS (
SELECT * FROM x PIVOT
(MAX(d) FOR dw IN ([1],[2],[3],[4],[5],[6],[7])) AS p
)
SELECT
day1 = COALESCE([1], '19000101'),
day2 = COALESCE([2], '19000101'),
day3 = COALESCE([3], '19000101'),
day4 = COALESCE([4], '19000101'),
day5 = COALESCE([5], '19000101'),
day6 = COALESCE([6], '19000101'),
day7 = COALESCE([7], '19000101')
FROM pvt;