SQL server 2005 - query aribitrary time interval in date range - sql-server

I have a DateTime column. I want to extract all records, lets say, from 8:30 to 16:15 within a certain date range. My problem is that I need to compare hour and minute as a single time value. I can test the DATEPART for Greater or Less than some hours value, but if I then do that for minutes my query will fail if the later-in-the-day time has a smaller minutes value.
I have looked at INTERVAL, BETWEEN, DATEPART, DATEDIFF etc, but don't see quite how to to this without a "TimeOfDay" value that I can use across records of different dates.
I have tried subtracting the year, month and day parts of the date so that I can compare just the time of day, but when attmpting to subract, say, the year part of a date I get an overlfow error:
This part works:
select - cast( DATEPART(YEAR, CallTime) as integer) from history
This fails:
select DATEADD(YEAR, - cast( DATEPART(YEAR, CallTime) as integer), CallTime)
from history where calltime is not null
I have also tried casting the hours and minutes parts to chars, concatenating them and comparing to my target range, but this also fails.
I believe newer versions of SQL server may have a function to deal with this situation, but that's not available to me.
I hope and imagine there is a simple, obvious solution to this, but it's eluding me.

Try creating a "MinuteOfDay" function that calculates how many minutes have passed in the day based on a datetime.
CREATE FUNCTION dbo.[MinuteOfDay]
(
#dt datetime
)
RETURNS int
AS
BEGIN
RETURN (datepart(hh,#dt)*60) + datepart(mi,#dt)
END
then use the result of that function to filter.
select *
from MyTable t
where dbo.MinuteOfDay(t.SomeDateTimeColumn) between dbo.MinuteOfDay('1900-1-1 08:30:00') and dbo.MinuteOfDay('1900-1-1 16:15:00')

give this a shot:
DECLARE #StartDateTime datetime
,#EndDateTime datetime
--date range is ALL of January 1st up to & including 31st
SELECT #StartDateTime='2011/01/01'
,#EndDateTime='2011/01/31'
SELECT
*
FROM TableName t
WHERE
t.ColumnDate>=#StartDateTime AND t.ColumnDate<#EndDateTime+1 --date range
AND LEFT(RIGHT(CONVERT(char(19),t.ColumnDate(),120),8),5)>='08:30' --time range start
AND LEFT(RIGHT(CONVERT(char(19),t.ColumnDate(),120),8),5)<='16:15' --time range end
if you have an index on t.ColumnDate, this should be able to take advantage of it.
the "date range" part of the WHERE throws away rows that are not within the intended date range. The "time range start" part of the WHERE throws away rows that are to early in time and the "time range end" throws away rows that are to late.

DATETIME values can be cast as FLOAT. Actually, a DATETIME is stored as a FLOAT.
The whole part of the FLOAT is the days since '12/31/1899' (or something close). The fractional part is the number of hours divided by 24. So 0.5 = 12 Noon.
08:30 is 0.3541666667
16:15 is 0.6770833333
SELECT CAST(CAST('2011-03-25 08:30:00' AS DATETIME) AS FLOAT) = 40625.3541666667
SELECT CAST(CAST('2011-03-25 16:15:00' AS DATETIME) AS FLOAT) = 40625.6770833333
So you could write
SELECT * FROM users WHERE hire_date < 40625.3541666667
Using a DATETIME as FLOAT you can use whichever mathematical functions work best for your query.

Related

Formatting Y Axis with Day, Hour, Minute, and Second on a SSRS Report

Background of the problem:
I have this data set that provides me the solution I need for the chart. However, I have been struggling to find a solution for the report to correct produce the answer I need below.
The y axis is supposed to be something moving beyond the 24 hour time mark per vertical axis legend. The data set calculations are correct on the category groups, but the y axis is giving me a hard time to produce the formatting vertical label axis.
Query Used for this Report:
SELECT
-- Category Group for Chart
[Vendor_Name]
-- Date of call
[Call_Date],
-- Call Time in Seconds
[Call_Time],
-- DATE ADD function that does the conversion to days, hours, minutes, and seconds from Call_Time column beyond the 24 hour period.
DATEADD(SECOND, Call_Time, '00:00:00') [Call_Time_Test]
FROM Phone_Records
Methods Used:
Used T-SQL DATEADD() function to calculate the day, hour, minute, and hour, but it doesn't give the correct output that I am expecting.
Had changed the interval to Days but it didn't work for my case.
Used min and max values for the chart and did nothing for me.
Is there a better way to solve this problem without breaking any of code that I've made into the report so far?
You need to pass in a datetime to DATEADD, not just a time. With this in mind you can do something like this..
declare #t table (CallID int identity (1,1), CallDate DATETIME, CallTime int)
insert into #t (CallDate, CallTime) VALUES
('2022-08-16 23:50:00', 100),
('2022-08-16 23:50:00', 1000),
('2022-08-17 12:00:00', 1000),
('2022-08-17 13:00:00', 1000)
select
*
, CallDuration =cast(dateadd(s, CallTime , cast(cast(getdate() as date) as datetime)) as time)
from #t
It looks a bit messy and there probably is a better way to do it but it does work.
First to do GetDate() (any date will do) and cast it to Date so it ignores the time, we then cast this to DateTime to give use todays date at 00:00:00. Then we add the number of seconds to it and finally cast it back to a time so it chops the date off.
The above give the following results...

How to compare column values to declared variable

I was asked this interview question.
--Without modifying the following code:
DECLARE #StartDateInput SMALLDATETIME = '1/1/2018',
#EndDateInput SMALLDATETIME = '1/1/2018'
--Modify the following query so that it will return contacts modified at any time on January 1st, 2018
SELECT *
FROM dbo.Contacts
I tried the following query but this was not correct. I'm sure that I'm supposed to use the #EndDateInput variable as well but I wasn't sure how to use it. I don't think that this is the right way to approach this in general either.
SELECT *
FROM dbo.Contacts
WHERE ModifiedDate = SMALLDATETIME
It looks like the question is probing your understanding of date and datetime types, namely that a date with a time is after a date without a time (if there is even such a thing; most timeless dates are considered to be midnight on the relevant date, which is a time too.. in the same way that 1.0 is the same thing as 1, and 1.1 is after 1.0)
I'd use a range:
SELECT *
FROM dbo.Contacts
WHERE ModifiedDate >= #StartDateInput AND ModifiedDate < DATEADD(DAY, 1, #EndDateInput)
Why?
This caters for datetimes that have a time component.
It doesn't modify the row data (always a bad idea, e.g. to cast a million datetimes to a date just to strip the time off, every time you query - precludes using an index on the column and is a massive waste of resources) just to perform the query.
It converts the apparent "end date is inclusive" implied by both #variables being the same, to a form that allows the exclusive behavior of < to work inclusively (adds a day and then gets rows less than the following day, thereby including 23:59:59.999999 ...)
The only thing I would say is that strictly, the spec only calls for one day's records, which means it's not mandatory to use the #EndDateInput at all. It seems logical to use it, but it could be argued that if the spec is that this query will only ever return one day, the #End variable could be discarded and a DATEADD performed on the #Start instead
It is saying "any time" meaning consider the time component. With T-SQL the only reliable way is to use >= and < range query (exclusive upper range):
SELECT *
FROM dbo.Contacts
WHERE ModifiedDate >= #StartDateInput and
ModifiedDate < dateadd(d, 1, #EndDateInput);
PS: Initial declaration of #StartDateInput and #ENdDateInput is not robust and probably by chance pointing to Jan 1st, 2018. If it were '1/2/2018' then it would be ambiguous between Jan 2nd and Feb 1st. Better use ODBC canonical and\or ISO 8601 strings like '20180101'.

SQL update based on query results with two parameters

I've created a virtual table in SQL Server that has 28 days from the current date and each date has rows for time that range from 12-10 pm incremented by 15 min and another value to indicate that it's turned on/off for availability, so it would be something like this:
date time onoff
-------------------------------------------------
2015-04-08 12:00 1
2015-04-08 12:15 1
....continue until 22:00 then start next day
2015-04-09 12:00 1
..... continue for 28 days
I'd like to update the availability based on a query from another table which would return the date, start and end time...
So far I came up with this
update table1
set onoff = 0
where tbl1date in (select tbl2date from table2 where userid = 1)
The problem I'm having is adding in the between certain hours part of the equation and I'm not sure how to do something like this in SQL or how to even search for the answer based on not being able to word it properly...
Can someone help or point me in the right direction?
use a DATETIME, don't use separate DATE and TIME fields.
I think you should take a look at DATEDIFF (https://technet.microsoft.com/pl-pl/library/ms189794(v=sql.110).aspx) function.
Your where query could look like this:
update table1 set onoff = 0
where
DATEDIFF(minute, <MIN_RANGE>, tbl1date) >= 0 and
DATEDIFF(minute, tbl1date, <MAX_RANGE>) >= 0
How you calculate MIN_RANGE and MAX_RANGE depends on your table2 structure.
As suggested, if you have control over the structure, use datetime fields as they are easier to do the comparisons on. I'm going to assume you don't have control over the structure.
In order to compare the datetimes you need to create them from your separate date and times. You can either parse the time field for the hours and minutes and use DATEADD to add the appropriate offsets to the date, or you can use CONVERT to interpret a date time string as a date. Something like
CONVERT(datetime, SUBSTRING(CONVERT(varchar, tbl1date, 121), 1, 10) + ' ' + tbl1time, 121)
What this does is to convert the date to odbc cannonical format and throwaway the time part as it takes only the first 10 characters. Then it appends the time and interprets the whole string as a odbc cannonical datetime string. That format is yyyy-mm-dd hh:mi:ss.mmm. The hours are based on 24 hours. So if your times are in AM/PM format you're going to have to convert them.
If your other table has separate date and times you'd use a similar expression to combine them.
Once you have the datetimes you can do something like this
UPDATE table1
SET onoff = 0
WHERE <expression above> BETWEEN (SELECT min_value FROM table2) AND (SELECT max_value FROM table2)

TSQL Determine every other Friday from a "seed" date

Greetings StackOverflow Wizards.
SQL datetime calculations always give me trouble. I am trying to determine if an employee's hiredate fell between the last payday of that month and the first of the next month. (I.e. did they get a paycheck in their hire month.
Knowns:
I know our paydays are every other Friday.
I know 01/02/1970 was a Payday, and that date precedes the longest
active employee we have.
I know the hire date of each active employee (pulled from table).
I know (can calculate) the first of the month following the hire
date.
What I cannot seem to wrap my head around is how to use that seed date (01/02/1970) with datediff, dateadd, datepart, etc. to determine if there is a pay date between the hire date in question and the first of the following month.
In pseudo-code, here is what I'm trying to do:
declare #seedDate datetime = '01/02/1970' -- Friday - Payday seed date from which to calculate
declare #hireDate datetime = '09/26/2008' -- this date will actually be pulled from ServiceTotal table
declare #firstOfMonth datetime = DATEADD(MONTH, DATEDIFF(MONTH, 0, #hireDate) + 1, 0) -- the first of the month following #hireDate
declare #priorPayDate datetime -- calculate the friday payday immediately prior to #firstOfMonth
if #priorPayDate BETWEEN #hireDate AND #firstOfMonth
begin
-- do this
end
else
begin
-- do that
end
Using the hard-coded #hireDate above, and the #seedDate to determine every-other-Friday paydays, I know that there was a payday on 9/19/2008 and not another one until 10/03/2008, so the boolean above would be FALSE, and I will "do that" rather than "do this." How do I determine the value of #priorPayDate?
In all my databases where there is a lot going on with dates I create a table with colums for date,day, weekday,month,weeknr,dayof month, etc etc. I then use a procedural programming language or a bunch of handwritten sql to populate this table with every day for a large range of years say 1970 to 2200.
I pack this table 100% and index it heavily. You can then simply join any date to this table and do complex date stuff with simple where clause. So basically you pre calculate a helper table. maybe in you case you add a column to the date helper table with friday since seed column.
hope that makes sense.
Taking a DATEDIFF for days between your #seedDate and #firstOfMonth will give you a total number of days, which you can modulus by number of days between pay periods (14) to get number of days from the last pay period to the #firstOfMonth. You'll run into problems when the 1st is a payday (e.g. next month), which makes a CASE statement necessary:
DECLARE #priorPayDate DATETIME
SET #priorPayDate = CASE
WHEN DATEDIFF(dd, #seedDate, #firstOfMonth) % 14 = 0
THEN DATEADD(dd, -14, #firstOfMonth)
ELSE DATEADD(dd, -(DATEDIFF(dd, #seedDate, #firstOfMonth) % 14), #firstOfMonth)
END

TSQL Query - return all seconds between two dates

I need a TSQL query which returns all seconds since a given begin date. I am going to use this to left outer join against another table to see how many bytes were downloaded each second. I am using Sql Server 2008.
I'm shooting from the hip here, but here's a start:
DECLARE #STARTDATE DATETIME
DECLARE #ENDDATE DATETIME
SET #STARTDATE = '05/01/2010'
SET #ENDDATE = GETDATE()
-- Find the seconds between these two dates
SELECT DATEADD(SECOND, Number, #StartDate) AS N
FROM Numbers
WHERE Number < DATEDIFF(SECOND, #STARTDATE, #ENDDATE)
This assumes a table called Numbers, with a column named Number containing values from 1 up. The be able to get results for an entire month, you're going to need to have values up to around 2.5 million. I'd keep the query down to perhaps a day, meaning the Numbers table can get away with values less than 100,000.
Here's a great article on numbers tables: http://www.sqlservercentral.com/articles/Advanced+Querying/2547/
Registration is required, but it's free. If you do any serious SQL Server programming this site is quite useful.
You will likely need an Auxiliary numbers table for this. Do you need all seconds represented or can you just round to the nearest second and group by that?
Also how many seconds are we talking about here and what format do you currently have them stored in. Are they already rounded?
If not then maybe to avoid the overhead of rounding them or doing a BETWEEN type query every time (as well as the repeated DATEADDs) maybe you could use Marc's DATEDIFF answer at insert/update time to store the seconds from some base date then just join onto the numbers table using the calculated numeric column.
Code to create Numbers table from here http://web.archive.org/web/20150411042510/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-numbers-table.html Just to add to Brad's answer.
CREATE TABLE dbo.Numbers
(
Number INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
)
WHILE COALESCE(SCOPE_IDENTITY(), 0) <= 1000000
BEGIN
INSERT dbo.Numbers DEFAULT VALUES
END
DATEDIFF ( s , #FirstDate , #SecondDate )

Resources