Having issues with dates in SQL - sql

I'm writing a report that needs to collect data each day, between 0900hs and 1700hs.
I thought it would be fine as follows:
cast(convert(char(8),t.trxtime,112)as time)
between CONVERT(VARCHAR(5),getdate(),108) >= '09:00'
and CONVERT(VARCHAR(5),getdate(),108) < '17:00'
....BUT no cigar.
Thank you!!!

Hmmm, you could just use datepart():
where datepart(hour, t.trxtime) between 9 and 16 and
cast(t.trxtime as date) = cast(getdate() as date)
I'm not sure if the date comparison is actually necessary.

You could do something like this (assuming you mean actually for the current date, and not for every date in a range:
declare #startDate datetime
declare #endDate datetime
select #startDate = '2014-11-03 09:00:00',
#endDate = '2014-11-03 17:00:00'
select *
from table
where myDate between #startDate and #endDate
if you did mean between 0900 and 1700 for each day, you could do:
declare #startDate datetime
declare #endDate datetime
select #startDate = '2014-10-03',
#endDate = '2014-11-03' -- note i'm still limiting it to a range of ~1 month
select *
from table
where myDate between #startDate and #endDate
and datepart(hour, myDate) between 9 and 17

Related

Is there an efficient way to break a date range into hours per day?

In SQL Server I am attempting to break a date range into hours per day and have the following bit of code which is OK for a short time frame, but rather inefficient for longer periods of time. Could anyone suggest a more efficient approach?
DECLARE #StartDate datetime = '2015-01-27 07:32:35.000',
#EndDate datetime = '2015-04-29 14:39:35.000',
#TempDate datetime = '';
SET #TempDate = #StartDate;
DECLARE #dateTimeTable TABLE (dt datetime, minCol INT);
WHILE #TempDate < #EndDate
BEGIN
INSERT INTO #dateTimeTable VALUES (CONVERT(date,#TempDate), 1)
SET #TempDate = DATEADD(minute,1,#TempDate)
END
Select dt,
FORMAT(SUM(minCol) / 60.0,'F') as Hours
from #dateTimeTable
GROUP BY dt
Thanks,
Carl
The best way would be to use recursive cte :
DECLARE #StartDate datetime = '2015-01-27 07:32:35.000',
#EndDate datetime = '2015-04-29 14:39:35.000';
WITH cte AS (
SELECT CAST(#StartDate AS DATE) startdate,DATEDIFF(minute, #StartDate, DATEADD(DAY, 1, CAST(#StartDate AS DATE) ) ) / 60.0 hours
UNION ALL
SELECT DATEADD(DAY,1, startdate), DATEDIFF(minute, DATEADD(DAY,1, startdate), CASE WHEN DATEADD(DAY,2, startdate) > #EndDate
THEN #enddate ELSE DATEADD(DAY,2, startdate) END) / 60.0
FROM cte
WHERE startdate <> CAST(#EndDate AS DATE)
)
SELECT * FROM cte
db<>fiddle here

How to account for 31 days using SQL

I am trying to generate rolling data for month. However, I am having an issue where the query doesnt generate any data when the month has 31 days.
I am defining my days here:
declare #today datetime
set #today = getdate()
declare #enddate datetime
set #enddate = #today
declare #begindate datetime
set #begindate = dateadd(mm, datediff(m,0,#today),0)
Then I am calling the days in my query using:
PURCHDATE BETWEEN #begindate AND #enddate
I can see my issue where my begin date is first day of the month and end date is today. How can I make this work for 31 day months?
You could do this
WHERE YEAR(PURCHDATE) = YEAR(GETDATE()) AND MONTH(PURCHDATE) = MONTH(GETDATE())
I would recommend:
PURCHDATE >= CONVERT(date, DATEADD(day, 1 - day(getdate()), getdate())) AND
PURCHDATE < CONVERT(date, DATEADD(day, 1, EOMONTH(getdate())))

Floor() function on a datetime - SQL Server

http://sqlfiddle.com/#!18/639ec/2
CREATE TABLE TEST
(
ID INT,
OrderNo int,
DateReceived datetime
)
INSERT INTO TEST (ID,OrderNo,DateReceived)
VALUES ('1', '3454', '07-20-2018 00:00:00')
Query:
DECLARE #StartDate datetime,
#EndDate datetime,
#FlooredStart datetime ,
#FlooredEnd datetime
SET #StartDate = '07-20-18'
SET #EndDate = '07-20-18'
SET #FlooredStart = CAST(FLOOR(CAST(#startDate AS FLOAT)) AS DATETIME)
SET #FlooredEnd = DATEADD(d, 1, CAST(FLOOR(CAST(#endDate AS FLOAT)) AS DATETIME))
SELECT * FROM TEST
WHERE DateReceived = #FlooredStart and DateReceived < #FlooredEnd
In my live version if the date received is left blank it defaults to the time 12:00:00AM of that day. So for this example if i search for orders on the 07-20-18 it wont return the order that was placed at 12:00:00AM of the 07-20-18.
So i figure to add >= to the clause
DECLARE #StartDate datetime,
#EndDate datetime,
#FlooredStart datetime ,
#FlooredEnd datetime
SET #StartDate = '07-20-18'
SET #EndDate = '07-20-18'
SET #FlooredStart = CAST(FLOOR(CAST(#startDate AS FLOAT)) AS DATETIME)
SET #FlooredEnd = DATEADD(d, 1, CAST(FLOOR(CAST(#endDate AS FLOAT)) AS
DATETIME))
SELECT * FROM TEST
-- WHERE DateReceived BETWEEN #StartDate AND #EndDate
WHERE DateReceived >= #FlooredStart and DateReceived < #FlooredEnd
Result:
| ID | OrderNo | DateReceived |
|----|---------|----------------------|
| 1 | 3454 | 2018-07-20T00:00:00Z |
I was just wondering if i had this logic correct? And can anyone explain to me exactly what the floor() function is doing. I understand that its calculating the first day of year/month but is that needed here? I looked online and i could'nt find a definitive answer anywhere.
Thanks
Instead of using floor to "truncate" the time part, use the date-only date type :
DECLARE #StartDate date = '20180720', #EndDate date='20180720'
SELECT * FROM TEST
WHERE cast(DateReceived date) between #startdate and #enddate
or, for a single date only :
SELECT * FROM TEST
WHERE cast(DateReceived date) = #startdate
Note that I'm using the unseparated date literal. This is the only unambiguous date format. The other unambiguous format is the full ISO8601 format for the datetime types. Two-digit years are just begging for incorrect interpretation way.
cast(DateReceived date) will convert the datetime values in DateReceived into date values. Usually, it's a bad idea to apply functions to fields because it prevents the query optimizer to use any indexes. SQL Server understands cast( ... as date) though and converts :
cast(DateReceived date) = #startdate
To a range query equivalent to DateReceived between #startdate at 00:00 but before the next day at 00:00 , allowing it to use any indexes on DateReceived

get last date in tsql

i show you my logic is getting error only when month is December
declare #startDate datetime
declare #endDate datetime
set #startDate = convert(varchar(2),#month)+'/1/'+ convert(varchar(4),#year)
set #endDate = dateadd(DD,-1,(convert(varchar(2),#month+1)+'/1/'+convert(varchar(4),#year)))
while(#startDate < #endDate+1)
begin
insert into #tempday
select #startDate
set #startDate = dateadd(day, 1, #startDate )
end
please help
Once you have #startdate, use:
set #enddate = dateadd(day,-1,dateadd(month,1,#startdate))
And I don't think you mean plsql...
There are other things you could think about too, such as not using a while-loop. Why not query a table that has plenty of rows (such as sys.all_columns) and use:
insert #tempday
select top (datediff(day,#startdate,#enddate)+1)
dateadd(day,row_number() over (order by (select 1))-1,#startdate)
from sys.all_columns;
In case of December #month+1 will get you 13 which is not a valid month number

SQL Separate hours in Start and EndDate

I'm need a help to create a Query. My problem is I have a StartDate and EndDate and need separate this in blocs of 60 minutes.
DECLARE #STARTDATE AS SMALLDATETIME
DECLARE #ENDDATE AS SMALLDATETIME
SET #STARTDATE = '2012-11-21 11:03:00'
SET #ENDDATE = '2012-11-21 13:04:00'
I need the return:
Hour, Time
11 , 57
12 , 60
13 , 04
You could use a recursive CTE. For example:
declare #startDate datetime = '2012-11-21 22:05:00'
declare #endDate datetime = '2012-11-22 01:06:00'
; with TimeList as
(
select #startDate as dt
union all
select dateadd(hour, 1, dateadd(hour, datediff(hour, 0, dt), 0))
from TimeList
where dateadd(hour, 1, dt) < #endDate
)
select dt
from TimeList
union all
select #endDate
The snippet dateadd(hour, datediff(hour, 0, dt), 0) removes the hours and minutes from a date. It does so by calculating the number of hours since date 0 and then adding that number of hours to date 0.
Live example at SQL Fiddle.
I unsure if i understood you but this will return the hour and minute after your start date at 60 min intervals.
DECLARE #STARTDATE AS SMALLDATETIME
DECLARE #ENDDATE AS SMALLDATETIME
DECLARE #time AS TABLE(id int identity(1,1), [hour] int, [time] int)
SET #STARTDATE = '2012-11-21 11:03:00'
SET #ENDDATE = '2012-11-21 13:04:00'
WHILE #STARTDATE < #ENDDATE
BEGIN
SELECT #STARTDATE = DATEADD(MINUTE,60,#STARTDATE)
INSERT INTO #time (hour,time)
VALUES(DATEPART(HOUR,#STARTDATE),DATEPART(MINUTE,#STARTDATE))
END
SELECT * FROM #time
You coan do it in three pieces. First piece is for the first hour, 60 minus the minute value, 2nd piece is time=60 for all hours between start+1 and end, third piece is end minutes
and then insert them into a temp table, as abstractChaos has done.
Insert into temp table like AbstractChaos:
DECLARE #STARTDATE AS SMALLDATETIME
DECLARE #ENDDATE AS SMALLDATETIME
DECLARE #TIME AS TABLE(id INT IDENTITY(1,1), [HOUR] INT, [TIME] INT)
SET #STARTDATE = '2012-11-21 11:03:00'
SET #ENDDATE = '2012-11-21 13:04:00'
INSERT INTO #TIME (HOUR,TIME)
VALUES (datepart(HOUR,#startdate) ,60 - datepart(MINUTE,#startdate) )
WHILE #STARTDATE < #ENDDATE
BEGIN
SELECT #STARTDATE = DATEADD(MINUTE,60,#STARTDATE)
INSERT INTO #TIME (HOUR,TIME)
VALUES(datepart(HOUR,#STARTDATE) , 60)
END
INSERT INTO #TIME (HOUR,TIME)
VALUES(datepart(HOUR,#enddate) , datepart(MINUTE,#startdate))