Group By query for date based on Custom Time Period - sql

I'm building a WCF application for calculating total time spend between in and out time, to fetch data from database I'm using GROUP BY clause to group data by date, but I want my day to start & end at 6:00 AM so if anyone leaves at 3 in the morning, it'll be added in the current day only. I'm using the following command query
SELECT MIN([Swipedatetime]) AS [Entry]
, MAX([Swipedatetime]) AS [Exit]
, [UserID]
FROM [Database_Name].[dbo].[Table_Name]
where UserID = '100'
GROUP
BY UserID
, CAST (Swipedatetime as DATE)
ORDER
BY MIN([Swipedatetime])
Also If there is any way by which the difference between the two times can be calculated in the stored procedure only then please mention it, it'll be of great use.

How about deducting 6 hours from the Swipedatetime and grouping by that new value:
GROUP BY (Swipedatetime - INTERVAL '6 hours')
(this is postgresql, for sql-server I think you need the function dateadd(hour, -6, Swipedatetime), or something along this line)

Your solution need only simple DATEADD function:
SELECT MIN([Swipedatetime]) AS [Entry]
, MAX([Swipedatetime]) AS [Exit]
, [UserID]
FROM [dbo].[Table_Name]
WHERE UserID = '100'
GROUP
BY UserID
, CAST (DATEADD(HOUR,6,Swipedatetime) AS DATE)
ORDER
BY MIN([Swipedatetime])

To get only records between 6am and 6pm you can use this:
where datepart(hour,[Swipedatetime]) > 6
and datepart(hour,[Swipedatetime]) <=18
For the diff you can use this:
select DATEDIFF(minute, MIN([Swipedatetime]), MAX([Swipedatetime]))
So full query:
declare #StartDate datetime = dateadd(HH, 6, convert(datetime, convert(date, getdate())))
declare #EndDate datetime = dateadd(day,1,#Startdate)
SELECT MIN([Swipedatetime]) AS [Entry]
, MAX([Swipedatetime]) AS [Exit]
, DATEDIFF(minute, MIN([Swipedatetime]), MAX([Swipedatetime])) AS[Diff]
, [UserID]
FROM [Database_Name].[dbo].[Table_Name]
where UserID = '100'
and [Swipedatetime] >= #Startdate
and [Swipedatetime] < #EndDate
GROUP
BY UserID
, CAST (Swipedatetime as DATE)
ORDER
BY MIN([Swipedatetime])

Related

Showing dates by day wise, and having default date based on condition

am attaching a query to bring dates to a field,
if system time is before 01:00 pm then it should bring today date+1 else if system date is after or equal 01:00 pm it should bring today date +2
further , user can see the query result that will show him a date range along side with day names , and select one of them manually if the above cases wasn't what the user is looking for .
queries are avilable and working fine but separately,
one query bring date range , and one query set the date based on a condition , i need to make them one query , that bring the date ranges and set default date to be based on the conditions;
here are the queries :
Note : Q No 2 , I tried to make both queries as one query but its not bring the correct result , it keep bring today date +1 no matter what system time is.
1)
If DATEPART(Hour,Getdate())<12
SELECT GetDate()+1
If DATEPART(Hour,Getdate())>=12
SELECT GetDate()+2
2)
If DATEPART(Hour,Getdate())<12
DECLARE #Date1 DATE, #Date2 DATE
SET #Date1 = GetDate()+0
SET #Date2 = GetDate()+365
SELECT DATEADD(DAY,number+1,#Date1) [Date], DateName(Weekday,DATEADD(DAY,number+1,#Date1)) as dayname
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2
If DATEPART(Hour,Getdate())>=12
DECLARE #Date3 DATE, #Date4 DATE
SET #Date3 = GetDate()+2
SET #Date4 = GetDate()+365
SELECT DATEADD(DAY,number+1,#Date3) [Date], DateName(Weekday,DATEADD(DAY,number+1,#Date3)) as dayname
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date3) < #Date4
I've used a CTE to calculate the start date. The CTE uses a CASE statement to tell if it is being run before or after 12.
WITH Base AS
(
/* Returns the start date based on the current time.
* Before midday the start date is today.
* After midday it is the day after tomorrow.
*/
SELECT
CASE
-- Use current time to calculate start date.
WHEN DATEPART(HOUR, GETDATE()) < 12 THEN DATEADD(DAY, 0, GETDATE())
ELSE DATEADD(DAY, 2, GETDATE())
END AS StartDate
)
SELECT
DATEADD(DAY, sv.Number + 1, b.StartDate) AS [Date],
DATENAME(WEEKDAY, DATEADD(DAY, sv.Number + 1, b.StartDate)) AS [DateName]
FROM
master..spt_values AS sv
CROSS JOIN Base AS b
WHERE
sv.[type] = 'P'
AND DATEADD(DAY, Number + 1, b.StartDate) < DATEADD(YEAR, 1, b.StartDate)
;

How to find the total playing time per day for all the users in my sql server database

I have a table which contains following columns
userid,
game,
gameStarttime datetime,
gameEndtime datetime,
startdate datetime,
currentdate datetime
I can retrieve all the playing times but I want to count the total playing time per DAY and 0 or null if game not played on a specific day.
Take a look at DATEDIFF to do the time calculations. Your requirements are not very clear, but it should work for whatever you're looking to do.
Your end result would probably look something like this:
SELECT
userid,
game,
DATEDIFF(SS, gameStarttime, gameEndtime) AS [TotalSeconds]
FROM [source]
GROUP BY
userid,
game
In the example query above, the SS counts the seconds between the 2 dates (assuming both are not null). If you need just minutes, then MI will provide the total minutes. However, I imagine total seconds is best so that you can convert to whatever unit of measure you need accurate, such as hours that might be "1.23" or something like that.
Again, most of this is speculation based on assumptions and what you seem to be looking for. Hope that helps.
MSDN Docs for DATEDIFF: https://msdn.microsoft.com/en-us/library/ms189794.aspx
You may also look up DATEPART if you want the minutes and seconds separately.
UPDATED BASED ON FEEDBACK
The query below breaks out the hour breakdowns by day, splits time across multiple days, and shows "0" for days where no games are played. Also, for your output, I have to assume you have a separate table of users (so you can show users who have no time in your date range).
-- Define start date
DECLARE #BeginDate DATE = '4/21/2015'
-- Create sample data
DECLARE #Usage TABLE (
userid int,
game nvarchar(50),
gameStartTime datetime,
gameEndTime datetime
)
DECLARE #Users TABLE (
userid int
)
INSERT #Users VALUES (1)
INSERT #Usage VALUES
(1, 'sample', '4/25/2015 10pm', '4/26/2015 2:30am'),
(1, 'sample', '4/22/2015 4pm', '4/22/2015 4:30pm')
-- Generate list of days in range
DECLARE #DayCount INT = DATEDIFF(DD, #BeginDate, GETDATE()) + 1
;WITH CTE AS (
SELECT TOP (225) [object_id] FROM sys.all_objects
), [Days] AS (
SELECT TOP (#DayCount)
DATEADD(DD, ROW_NUMBER() OVER (ORDER BY x.[object_id]) - 1, #BeginDate) AS [Day]
FROM CTE x
CROSS JOIN CTE y
ORDER BY
[Day]
)
SELECT
[Days].[Day],
Users.userid,
SUM(COALESCE(CONVERT(MONEY, DATEDIFF(SS, CASE WHEN CONVERT(DATE, Usage.gameStartTime) < [Day] THEN [Day] ELSE Usage.gameStartTime END,
CASE WHEN CONVERT(DATE, Usage.gameEndTime) > [Day] THEN DATEADD(DD, 1, [Days].[Day]) ELSE Usage.gameEndTime END)) / 3600, 0)) AS [Hours]
FROM [Days]
CROSS JOIN #Users Users
LEFT OUTER JOIN #Usage Usage
ON Usage.userid = Users.userid
AND [Days].[Day] BETWEEN CONVERT(DATE, Usage.gameStartTime) AND CONVERT(DATE, Usage.gameEndTime)
GROUP BY
[Days].[Day],
Users.userid
The query above yields the output below for the sample data:
Day userid Hours
---------- ----------- ---------------------
2015-04-21 1 0.00
2015-04-22 1 0.50
2015-04-23 1 0.00
2015-04-24 1 0.00
2015-04-25 1 2.00
2015-04-26 1 2.50
2015-04-27 1 0.00
I've edited my sql on sql fiddle and I think this might get you what you asked for. to me it looks a little more simple then the answer you've accepted.
DECLARE #FromDate datetime, #ToDate datetime
SELECT #Fromdate = MIN(StartDate), #ToDate = MAX(currentDate)
FROM Games
-- This recursive CTE will get you all dates
-- between the first StartDate and the last CurrentDate on your table
;WITH AllDates AS(
SELECT #Fromdate As TheDate
UNION ALL
SELECT TheDate + 1
FROM AllDates
WHERE TheDate + 1 <= #ToDate
)
SELECT UserId,
TheDate,
COALESCE(
SUM(
-- When the game starts and ends in the same date
CASE WHEN DATEDIFF(DAY, GameStartTime, GameEndTime) = 0 THEN
DATEDIFF(HOUR, GameStartTime, GameEndTime)
ELSE
-- when the game starts in the current date
CASE WHEN DATEDIFF(DAY, GameStartTime, TheDate) = 0 THEN
DATEDIFF(HOUR, GameStartTime, DATEADD(Day, 1, TheDate))
ELSE -- meaning the game ends in the current date
DATEDIFF(HOUR, TheDate, GameEndTime)
END
END
),
0) As HoursPerDay
FROM (
SELECT DISTINCT UserId,
TheDate,
CASE
WHEN CAST(GameStartTime as Date) = TheDate
THEN GameStartTime
ELSE NULL
END As GameStartTime, -- return null if no game started that day
CASE
WHEN CAST(GameEndTime as Date) = TheDate
THEN GameEndTime
ELSE NULL
END As GameEndTime -- return null if no game ended that day
FROM Games CROSS APPLY AllDates -- This is where the magic happens :-)
) InnerSelect
GROUP BY UserId, TheDate
ORDER BY UserId, TheDate
OPTION (MAXRECURSION 0)
Play with it your self on sql fiddle.

Date Time conversion/ Date diff in SSRS

I have an SSRS report where I am using below query.
(This query works fine in SQL server, problem is only in SSRS report)
--DECLARE #Range Number = 10;
SELECT * FROM TBL1 WHERE
USERNAME = 'MIKE'
AND
(
#Range = '10'
and
Convert(datetime, MyDate, 120) <= GETDATE()
)
or
(
#Range IN ('20','30')
and
DATEDIFF(DD, Convert(datetime, MyDate, 120) , GETDATE()) <= #Range
)
Unfortunately the myDate Column coming from database is a varchar column.
The SSRS throws an out-of-range exception.
Then I tried converting Getdate to Convert(varchar(10), getdate(), 120) and compare with myDate (without conversion as myDate is already in YYYY-MM-DD format but as a varchar in database)it still throws error. I assume this time coz SSRS is not able to process datediff in varchar columns.
When I run these queries individually, it works fine. i.e -
Declare #Range Number = 10;
Select * from tbl1
where username = 'MIKE' and
(
#Range = '10'
and
convert(datetime, MyDate, 120) <= getdate()
Has anyone faced similar issue in SSRS ???
Try this!
SELECT * FROM TBL1 WHERE
USERNAME = 'MIKE'
AND
(
#Range = '10'
and
cast(myDate as datetime) <= GETDATE()
)
or
(
#Range IN ('20','30')
and
cast(myDate as datetime) , GETDATE()) <= #Range
)
Presumably, your problem is that some values of myDate are not in the correct format. If you are using a more recent version of SQL Server, the function try_convert() can be a big help.
The reason it is failing is because of your logic. The second condition after the or is looking at all records, not just Mike's. I think you intend for the where clause to be:
SELECT *
FROM TBL1 WHERE
WHERE USERNAME = 'MIKE' AND
((#Range = '10' and Convert(datetime, MyDate, 120) <= GETDATE()
) or
(#Range IN ('20','30') and
DATEDIFF(DD, Convert(datetime, MyDate, 120) , GETDATE()) <= #Range
)
);
Note the extra set of parentheses. Now, this will probably help for this particular query for MIKE. But it won't help overall. Finding the value(s) that fail conversion can be daunting. If you are lucky, they fail easily. You can look for them with starting with:
select MyDate
from tbl1
where MyDate not like '[0-9][0-9][0-9][0-9]-[0-2][0-9]-[0-3][0-9]%';
If you are lucky, then this will find the offending values. Otherwise, you'll have to dive more deeply into the date formats, looking at the particular month and day (and potentially hour, minute, and second) values.
Moral: Store dates as dates. Don't store them as varchar().

How can I obtain only MSSQL rows that have been created since Jan 1st?

I have a script that writes database information to a csv based on a SQL query I wrote. I was recently tasked with modifying the query to return only rows where the DateTime field has a date that is newer then Jan. 1 of this year. The following query does not work:
$startdate = "01/01/2013 00:00:00"
SELECT Ticket, Description, DateTime
FROM [table]
WHERE ((Select CONVERT (VARCHAR(10), DateTime,105) as [DD-MM-YYYY])>=(Select CONVERT (VARCHAR(10), $startdate,105) as [DD-MM-YYYY]))"
The format of the DateTime field in the database is in the same format as the $startdate variable. What am I doing wrong? Is my query incorrectly formated? Thanks.
DECLARE #startdate datetime
SELECT #startdate = '01/01/2013'
SELECT Ticket, Description, DateTime
FROM [table]
WHERE DateTime >= #startdate
you can get year based records through DATEDIFF
DECLARE #startdate datetime = '01/01/2013';
SELECT Ticket, Description, DateTime
FROM [table]
WHERE DATEDIFF(YEAR,#startdate,DateTime) = 0 //It gives 0 if years are the same and DateTime is your column
You can get the start of the current year using:
DateAdd( year, Year( GetDate() ) - 1900, 0 )
The query then becomes:
select Ticket, Description, [DateTime]
from [Table]
where [DateTime] >= DateAdd( year, Year( GetDate() ) - 1900, 0 )
If you really don't want rows from January first then change the comparison from >= to >.
DECLARE #startdate datetime
SELECT #startdate = '01/01/2013'
SELECT Ticket, Description, DateTime
FROM [table]
WHERE DateTime >= #startdate
Interesting question that says as much about interpreting the requirement as about the SQL. When next year comes you will want to select next year's tickets. So it should be
SELECT Ticket, Description, DateTime
FROM [table]
WHERE datePart(yy,DateTime)=datePart(yy,getDate())
DECLARE #startdate datetime = '01/01/2013';
SELECT Ticket, Description, DateTime
FROM [table]
WHERE DATEDIFF(DD,#startdate,DateTime) >= 0

SQL - Sum of minutes between two timestamps by month

I am looking for an SQL query for the sum of minutes between start and end date for a particular month.
Eg.
I'm looking for the amount of minutes used in February.
Start Date Time: 27-02-13 00:00:00
End Date Time: 05-03-13 00:00:00
Because im only looking for the sum of february it should only give me the sum of 3 days (in minutes) and not the extra 5 days going into march.
I have no way to validate it but it should looks like:
SELECT DATEDIFF(minute, startDate, CASE when endDate > EOMONTH(startDate) THEN EOMONTH(startDate) ELSE endDate END) FROM ...
GL!
I left it in steps to illustrate each process. You can of course easily collapse this down, but I'll leave it up to you to do that.
Here's my solution http://www.sqlfiddle.com/#!3/b4991/1/0
SELECT *
, DATEDIFF(minute, StartDAte, NewEndDate) AS TotalMinutes
FROM
(
SELECT *
, CASE WHEN TempDate > EndDate THEN EndDate ELSE TempDate END AS NewEndDate -- Either EOM or old EndDate, whichever is smaller
FROM
(
SELECT *
, DATEADD(month, 1, CAST(Year + '-' + Month + '-1' AS DATETIME)) AS TempDate -- first day of the next month
FROM
(
select *
, CAST(DATEPART(month, StartDate) AS char(2)) AS Month
, CAST(DATEPART(year, StartDate) AS char(4)) AS Year
from tbl
) t0
) t1
) t2
First I get the year and month from the original StartDate. I then construct a first-of-the-month date from that. I then add one month to that to get me the first-of-the-month of the next month. Then I check if that new date is > or < the previous EndDate. I take the smaller of the two dates. Then I use the original StartDate and whichever is smaller between the TempDate and EndDate to determine my total minutes.
See Also EOMONTH: http://msdn.microsoft.com/en-us/library/hh213020.aspx
Look into using DATEDIFF -- this will just help you to get started:
SELECT DATEDIFF(minute, starttime, endtime)
http://msdn.microsoft.com/en-us/library/ms189794.aspx
To get the last day of the start month, use DATEADD:
SELECT DATEADD(second,-1,DATEADD(month, DATEDIFF(month,0,starttime)+1,0))
SQL Fiddle Demo
I recently had to solve a similar problem, I added two new functions to help with this:
CREATE FUNCTION [dbo].[GREATESTDATE]
(
-- Add the parameters for the function here
#Date1 DATETIME,
#Date2 DATETIME
)
RETURNS DATETIME
AS
BEGIN
IF (#Date1 < #Date2)
RETURN #Date2
ELSE
RETURN #Date1
END
and...
CREATE FUNCTION [dbo].[LEASTDATE]
(
-- Add the parameters for the function here
#Date1 DATETIME,
#Date2 DATETIME
)
RETURNS DATETIME
AS
BEGIN
IF (#Date1 > #Date2)
RETURN #Date2
ELSE
RETURN #Date1
END
Then use them like:
DATEDIFF(D,dbo.GREATESTDATE(#StartDate1,#StartDate2),dbo.LEASTDATE(#EndDate1,#EndDate2))