SQL Server - check if store is open - sql

I have a table as below
StoreName | TimeOpens | DurationInMinutes | DayOfTheWeek
Dog Store '00:08:00.000' 400 1
Duck Store '00:08:00.000' 1300 1
Cat Store '00:08:00.000' 1440 1
I need to select all stores that are currently open. Times are stored in UTC so GETUTCDATE() can be used to get current time. The TimeOpens is a Time object and not a DATETIME, which is why I am a little confused how to write a query that will get me the correct answer. The scenarios are what if the time overlaps over midnight because the duration is that long as in case of Duck Store or what if the duration is 1440 minutes long meaning that its open 24 hours a day.
I thought about converting it to a DateTime first which would make things easier but then I'm not sure do I get today's date or yesterdays when calculating this. For example if I get today's date and its past midnight then I'm before the store opened which would mean its closed. I have the same problem with choosing the day of the week as if its past midnight its technically next day but it could still be open from previous day.
SELECT *
FROM Stores
WHERE GETUTCDATE() > CAST(LEFT(CONVERT(DATE, GETUTCDATE()), 10) + ' ' + LEFT(CONVERT(VARCHAR, S.[TimeOpens], 120), 12) AS DATETIME)
Edit, the query should somehow include dayoftheweek as well because each day could have a different schedule. its a 1-7 index.

You can do:
where (cast(getutcdate() as time) >= timeopens and
datediff(minute, cast(getutcdate() as time), timeopens) <= duration
) or
(cast(utcdate() as time) < dateadd(minute, duration, timeopens) and
day(dateadd(minute, duration, cast(timeopens as datetime))) > 0
)

you can use this condition to get if its still a valid opening hours.
SELECT *
FROM Stores
WHERE
datediff(mi,
dateadd(mi, DurationInMinutes,cast(concat(convert(DATE, getutcdate()), ' ', TimeOpens) as datetime))
, getutcdate()) < 0

Related

Needing to get first of month in SQL Server, but from yesterday's date

I am working on a report that will send daily with the full months data minus the current day as it sends early and won't have relevant data from today.
Currently the parameter for the start as =dateadd(“m”,1,dateserial(year(Today),month(Today),0))
and the end as =DateAdd("d",-1,Today())
The problem is that this will skip the last day of each month. I want to change the start date to the first of the month from today - 1 day, but I am not sure how to do this?
I could change it to send with the current days data as well but with it sending at 8 AM the data would still be missing.
Probably least amount of code is to (1) subtract a day from today, (2) take the eomonth of the previous month, and (3) add a day.
SELECT DATEADD(DAY, 1, EOMONTH(DATEADD(DAY, -1, GETDATE()),-1));
I'm not sure what language you're doing dateserial stuff in but I'd personally do this inside SQL Server.
Also the best way to drive a report for any period is to say:
col >= start of the period
AND
col < start of *next* period
For example, for a report to cover all of last month (I realize that's not quite what you're doing, it's just a simpler example), I would say:
DECLARE #start date = DATEADD(DAY, 1, EOMONTH(GETDATE(), -2));
...
WHERE date_column >= #start
AND date_column < DATEADD(MONTH, 1, #start);
This way you don't get bitten by all of the problems that can happen with various date/time types as you try to figure out what is the end of a day or month.
For month-to-date covering yesterday and not today, I would say:
DECLARE #today date = GETDATE();
DECLARE #start date = DATEADD(DAY, 1, EOMONTH(DATEADD(DAY, -1, #today),-1));
... WHERE date_column >= #start
AND date_column < #today;

Calculate time from begin date in SQL

I'm trying to tally up the number of unique ID accounts that are active for more than a year as well as include how long each one is active. The problem with the code I have is it's not including accounts that are currently active (ones that don't have an end date). For example, if the begin date was May 01 2018 but has no end date since it's currently active, it should also be included in this query. Here's what I have so far..
SELECT UniqueID,
DATEDIFF(yy, Begin_Date,End_Date) as timeactive
FROM TABLE
WHERE DATEDIFF(yy, Begin_Date,End_Date) > 1
I want my output to look like...
Unique ID Time active
F000012 2.5
F000031 1.5
This is what ended up working:
SELECT UniqueID,
CAST(ROUND(DATEDIFF(day, Begin_Date, COALESCE(End_Date, getdate()))/365.0, 1, 0) AS NUMERIC (10,1)) as timeactive
FROM TABLE
WHERE DATEDIFF(day, Begin_Date, COALESCE (End_Date, getdate())) >= 365
If the EndDate is null then the output of the DateDiff function will be null, and any null compared to anything (even another null) is a result of null (usually then interpreted as false)
I suggest you use COALESCE to convert your end date to today if it is null:
SELECT
UniqueID,
DATEDIFF(yy, Begin_Date,COALESCE(End_Date, GetUtcDate()) as timeactive
FROM TABLE
WHERE DATEDIFF(yy, Begin_Date,COALESCE(End_Date, GetUtcDate()) > 1
You should bear in mind that the DATEDIFF function as used here, in SQLserver does NOT return the amount of time that has passed between the two dates. It returns the number of times the named interval has changed between the two dates
For example, DATEDIFF(year, 2000-01-01, 2000-12-31 23:59:59) will return 0 because these are both year 2000 even though they're just one second short of being a year apart. If you do DATEDIFF(year, 2000-12-31 23:59:59, 2001-01-01 00:00:01) even though these dates are only two seconds apart datediff will report them as 1 year apart because the year number has changed from 2000 to 2001.
DATEDIFF counts up by one every time the clock rolls past an interval change and in this case the interval is Year
To get your dates to report as 1.5 years etc you should consider to datediff by a smaller interval and divide, such as asking for the DAYS diff between two dates and then dividing by 365.25- the average number of days in a year. The smaller the interval you ask datediff for the more accurate the result will be but it'll never be 100%. If you're only after results to one decimal place of a year then days will be accurate enough
To get 1 decimal place, cast to a numeric with 1 DP:
SELECT
UniqueID,
CAST(DATEDIFF(day, Begin_Date,COALESCE(End_Date, GetUtcDate())/365.25 AS NUMERIC(5,1)) as timeactive
FROM TABLE
WHERE DATEDIFF(day, Begin_Date,COALESCE(End_Date, GetUtcDate()) >= 365
If you want time active as fractional years, then you need to use a smaller unit of time and divide. For instance:
SELECT UniqueID,
DATEDIFF(month, Begin_Date, COALESCE(End_Date, GETDATE())) / 12.0 as timeactive
FROM TABLE
WHERE Begin_Date < DATEADD(YEAR, -1, COALESCE(End_Date, GETDATE()))
Note the change in the WHERE clause. DATEDIFF() counts the number of year boundaries between dates. So the difference in years between 2019-01-01 and 2020-12-31 is the same as the difference between 2019-12-31 and 2020-01-01 -- exactly 1.
Consider:
SELECT
UniqueID,
DATEDIFF(yy, Begin_Date, COALESCE(End_Date, getdate()) as timeactive
FROM TABLE
WHERE DATEDIFF(yy, Begin_Date, COALESCE(End_Date, getdate()) > 1
This works by using the current date as default value for empty End_Dates. So this allows records with empty end date if their start date is more than one year ago.

Averaging event start time from DateTime column

I'm calculating average start times from events that run late at night and may not start until the next morning.
2018-01-09 00:01:38.000
2018-01-09 23:43:22.000
currently all I can produce is an average of 11:52:30.0000000
I would like the result to be ~ 23:52
the times averaged will not remain static as this event runs daily and I will have new data daily. I will likely take the most recent 10 records and average them.
Would be nice to have SQL you're running, but probably you just need to format properly your output, it should be something like this:
FORMAT(cast(<your column> as time), N'hh\:mm(24h)')
The following will both compute the average across the datetime field and also return the result as a 24hr time notation only.
SELECT CAST(CAST(AVG(CAST(<YourDateTimeField_Here> AS FLOAT)) AS DATETIME) AS TIME) [AvgTime] FROM <YourTableContaining_DateTime>
The following will calculate the average time of day, regardless of what day that is.
--SAMPLE DATA
create table #tmp_sec_dif
(
sample_date_time datetime
)
insert into #tmp_sec_dif
values ('2018-01-09 00:01:38.000')
, ('2018-01-09 23:43:22.000')
--ANSWER
declare #avg_sec_dif int
set #avg_sec_dif =
(select avg(a.sec_dif) as avg_sec_dif
from (
--put the value in terms of seconds away from 00:00:00
--where 23:59:00 would be -60 and 00:01:00 would be 60
select iif(
datepart(hh, sample_date_time) < 12 --is it morning?
, datediff(s, '00:00:00', cast(sample_date_time as time)) --if morning
, datediff(s, '00:00:00', cast(sample_date_time as time)) - 86400 --if evening
) as sec_dif
from #tmp_sec_dif
) as a
)
select cast(dateadd(s, #avg_sec_dif, '00:00:00') as time) as avg_time_of_day
The output would be an answer of 23:52:30.0000000
This code allows you to define a date division point. e.g. 18 identifies 6pm. The time calculation would then be based on seconds after 6pm.
-- Defines the hour of the day when a new day starts
DECLARE #DayDivision INT = 18
IF OBJECT_ID(N'tempdb..#StartTimes') IS NOT NULL DROP TABLE #StartTimes
CREATE TABLE #StartTimes(
start DATETIME NOT NULL
)
INSERT INTO #StartTimes
VALUES
('2018-01-09 00:01:38.000')
,('2018-01-09 23:43:22.000')
SELECT
-- 3. Add the number of seconds to a day starting at the
-- day division hour, then extract the time portion
CAST(DATEADD(SECOND,
-- 2. Average number of seconds
AVG(
-- 1. Get the number of seconds from the day division point (#DayDivision)
DATEDIFF(SECOND,
CASE WHEN DATEPART(HOUR,start) < #DayDivision THEN
SMALLDATETIMEFROMPARTS(YEAR(DATEADD(DAY,-1,start)),MONTH(DATEADD(DAY,-1,start)),DAY(DATEADD(DAY,-1,start)),#DayDivision,0)
ELSE
SMALLDATETIMEFROMPARTS(YEAR(start),MONTH(start),DAY(start),#DayDivision,0)
END
,start)
)
,'01 jan 1900 ' + CAST(#DayDivision AS VARCHAR(2)) + ':00') AS TIME) AS AverageStartTime
FROM #StartTimes

Query to fetch from 6am present day to next day 6am

I have a requirement to fetch data from 6 am of current day 6 am of next day daily basis. I have to consider 6 am to 6 am as single day. I am able to fetch data from 6 am to 11:59 PM of a day if I want remaining 6 hours which comes next day should also be considered in previous day. Tried many possible ways to fetch the data, but nothing gave me solution.
SGHH.DateTime >= DATEADD(HOUR,6,CONVERT(VARCHAR(10), GETDATE(),120))
AND SGHH.DateTime <= DATEADD(HOUR,6,CONVERT(VARCHAR(10), GETDATE()+1,120))
tried this method but after 11:59 PM getdate() will bring the current data and getdate()+1 will fall to next day.
CAN ANY ONE PLEASE SHARE A SAMPLE QUERY TO FETCH DATA BETWEEN 6am-6am??
You need to Add 6 hours to Today's midnight time, you can achieve that by taking the date part only out of GETDATE():
DECLARE #TODAY DATETIME2 = DATEADD(HOUR, 6, CONVERT(DATE, GETDATE()));
SGHH.DateTime >= #TODAY
AND
SGHH.DateTime <= DATEADD(DAY, 1, #TODAY)
what you need is that the time 6 hours ago, was in the actual day that you are now using as a day, so anything before 6am with 6 whole hours deducted will fall in the previous day.
CONVERT(VARCHAR(10), GETDATE(),120) <= dateadd(hour,-6,SGHH.DateTime)
AND dateadd(hour,-6,SGHH.DateTime) < CONVERT(VARCHAR(10), dateadd(day,1,GETDATE()) ,120)

How to count number of records present in a date range between a fixed time in SQL Server?

I need to get the number of records present in my [RecordsTable] for the last 3 months.
However the catch is I need the records which are processed between 10PM and 2AM.
For example --
07/01/2015 10PM -- 07/02/2015 2AM
07/02/2015 10PM -- 07/03/2015 2AM
07/03/2015 10PM -- 07/04/2015 2AM
The below SQL gives me the records present on any particular day starting from May,2015.
But I am not able to get the timing(10PM-2AM of next day) embedded in the SQL and need some help.
SELECT CONVERT(VARCHAR(12), RecordDate, 101),count(RecordID)
FROM [RecordsTable](NOLOCK)
WHERE RecordDate > '2015-05-01'
GROUP BY CONVERT(VARCHAR(12), RecordDate, 101)
MSSQL Supports both Date and Time datatypes. You can break up your where statement to reflect both date and time conditions separately.
SELECT COUNT(Records)
FROM TABLE
WHERE CONVERT(Date,DateCol) BETWEEN 'MM/DD/YYYY' AND 'MM/DD/YYYY'
AND CONVERT(Time,DateCol) BETWEEN 'HH:MM:SS' AND 'HH:MM:SS'
Try the following:
SELECT count(1)
FROM RecordsTable
WHERE RecordDate > '2015-05-01'
AND NOT DATEPART(hour, RecordDate) BETWEEN 2 AND 21
I assume RecordDate is a datetime or datetime2 column. between 2 and 21 will return rows where the hour for RecordDate is between 2am and 9pm, inclusive. NOT between 2 and 21 will return the reverse, giving you data for 10pm, 11pm, 12pm, and 1am. This does not include any time between 2:00am and 2:59am. If you need to include events that occurred precisely at but not after 2:00am, things get a bit tricker, but similar code based on not between would apply.
To get records in the last 3 months you can use two ways -- one by month looks like this
WHERE MONTH(colname) >= MONTH(GETDATE()) -3
This will get you inclusive months but not partial months. To get partial months is a bit more tricky because you could mean (for example for today) the 9th day of 3 months ago or you could mean 90 days ago. In the first case this works
WHERE colname >= dateadd(month,-3, getdate())
and for 90 days ago
WHERE colname >= dateadd(day,-90, getdate())
To get between 10PM and 2AM use this
WHERE datepart(hour,colname) >= 22 OR datepart(hour,colname) <= 2
Use DATEPART
SELECT COUNT(1)
FROM Table1
WHERE RecordDate > '2015-05-01'
AND (DATEPART(HOUR, RecordDate) <= 2 OR DATEPART(HOUR, RecordDate) >= 22)
Try this
SELECT count(*) FROM tablename where created_at>='2015-03-17 07:15:10' and created_at<='2015-07-09 02:23:50';
You can even use between
SELECT count(*) FROM tablename where created_at between '2015-03-17 07:15:10' and '2015-07-09 02:26:50';
You can use curdate() to get today's date