SQL Server query for date format - sql

Suppose if my web service has parameter year with value 201718 as string. Then in my query, I want to find something in between 01-04-2017 and 31-03-2018. The date is a datetime in the database. In my query I will use something like this
select *
from table
where date >= '01-04-2017' and date <= '31-03-18'
How should I change the format to accept date as '31-03-18'?

When representing a date using strings in SQL Server, it's best to use yyyyMMdd.
For datetime values, it's best to use yyyy-MM-ddTHH:mm:ss.
The reason for this is that SQL Server will always treat these formats the same way, regardless of local settings of the server.
However, working with string representations of date/datetime is still a bad idea.
So suppose you have a stored procedure that will get the Years as string, what you want to do is to get date values from this string. Something like this:
CREATE PROCEDURE stp_SelectByYears)
(
#Years as char(6)
)
AS
DECLARE #DateFrom date, #DateTo date;
SELECT #DateFrom = DATEFROMPARTS(CAST(LEFT(#Years, 4) as int), 4, 1),
#DateTo = DATEFROMPARTS(CAST(LEFT(#Years, 2) + RIGHT(#Years, 2) as int), 3, 31)
SELECT *
FROM table
WHERE date >= #DateFrom and date <= #DateTo
GO
Please note that datefromparts was introduced in 2012 version, so if you are working on an older version you need to replace it with cast:
SELECT #DateFrom = CAST(LEFT(#Years, 4) + '0401' as date),
#DateTo = CAST(LEFT(#Years, 2) + RIGHT(#Years, 2) +'0331' as date)

Related

String to Date in SQL

Is there a way to quickly convert this date format to DATE in SQL?
{ “date_from”:”22112017”,”date_to”:”22112017”}
This is needed to filter the data between these dates
(There are a lot of conversion entries on the web, but I haven't found that format)
EDIT:
DECLARE #EndDate DATE = CONVERT(VARCHAR, '22112017', 103)
PRINT #EndDate
Error: Conversion failed when converting date and/or time from character string.
WHAT I HAVE:
#StartDate = '22112017'
#EndDate = '22112020'
WHAT I NEED TO DO:
SELECT * from tblMy WHERE ReceivedDate BETWEEN #StartDate AND #EndDate
If you fix your JSON to not use stylised double quotes (”) and use standard ones (") then you can parse this as JSON. Once you extract the values, you can inject a couple of / characters in and then convert to a date with the style code 103 (dd/MM/yyyy):
DECLARE #String nvarchar(MAX) = N'{ "date_from":"22112017","date_to":"22112017"}';
SELECT CONVERT(date,STUFF(STUFF(OJ.date_from,5,0,'/'),3,0,'/'),103) AS date_from,
CONVERT(date,STUFF(STUFF(OJ.date_to,5,0,'/'),3,0,'/'),103) AS date_to
FROM (VALUES(#String))V(S)
CROSS APPLY OPENJSON(V.S)
WITH (date_from varchar(8),
date_to varchar(8)) OJ;
Edit:
Seems the OP has moved their goal posts, this has nothing to do with JSON.
The problem here is your literal strings. When using literal strings for a date and time data type use either yyyyMMdd or yyyy-MM-ddThh:mm:ss.nnnnnnn as they are both unambiguous regardless of language and data type:
DECLARE #StartDate date,
#EndDate date;
SET #StartDate = '20171222';
SET #EndDAte = '20201122';
SELECT *
FROM tblMy
WHERE ReceivedDate BETWEEN #StartDate AND #EndDate;
I would suggest converting the value to a standard SQL Server date value. This is pretty simple:
select convert(date, left(val, 4) + substring(val, 3, 2) + right(val, 2))
The standard date format is YYYYMMDD. Yours is DDMMYYYY, so string operations can convert it to the correct format. Of course, what you should probably do is to convert the value to a date in the application layer and pass the date value in as a parameter.
This should fix the error "Error: Conversion failed when converting date and/or time from character string."
DECLARE #EndDate VARCHAR(MAX) = '22112017'
DECLARE #datevar date = CAST(SUBSTRING(#EndDate, 3, 2) + '/' + SUBSTRING(#EndDate,
1, 2) + '/' + SUBSTRING(#EndDate, 5, 4) AS date);
SELECT #datevar;

Yesterday into YYYYMMDD format in SQL Server

I'm looking to add in a condition to my already badly performing SQL code. I want to add in a filter where the date = yesterday (data type as INT).
For example
Select *
From table
Where Date = 20190930
How do I do this using GETDATE() - 1?
We need to be very clear about what data type you're using.
If you have a simple date value (no time component as part of the data type), things are pretty easy:
Select *
from table
where Date = DATEADD(day, -1, cast(current_timestamp as date))
If you have a DateTime or DateTime2 value, it's important to understand that all DateTime values have a time component that goes all the way down to milliseconds. This is true even when you expect the time component to always be at or near midnight. That can make straight equality comparisons difficult. Instead, you almost always need check within a specific range:
Select *
from table
where Date >= DATEADD(day, -1, cast(current_timestamp as date))
AND Date < cast(current_timestamp as date)
Here's your query.
Firstly, you need to cast your int date to a varchar before covenrting to datetime, to avoid an arithmetic flow error during conversion.
Secondly, you need to cast getdate() - 1 as date to truncate time to match your date field.
select *
from table
where cast((cast(date as varchar(8))as datetime) = cast(getdate() - 1 as date)
or
select *
from table
where cast((cast(date as varchar(8)) as date) = cast(dateadd(day,datediff(day,1,GETDATE()),0) as date)

How to filter only the date from a string stored in a varchar

Ii have values stored in the SQL Server in the following manner : 02-Jul-12 12:00:00 AM here the time and minutes, seconds can be anything like 02-Jul-12 12:15:52 PM ,02-Jul-12 6:02:12 AM so on.
I want to have a where condition which will omit the time and take the data based on the date like the following where some_Date='02-Jul-12'
How would I do this?
SELECT * FROM whatever WHERE some_Date LIKE '02-Jul-12%';
If you are on SQL2008 or later, you can cast your DATETIME to DATE.
See this post: http://blog.sqlauthority.com/2012/09/12/sql-server-get-date-and-time-from-current-datetime-sql-in-sixty-seconds-025-video/
But in a WHERE-clause it is better to search between dates, like this:
DECLARE #startDate DATETIME = '02-Jul-2012'
DECLARE #endDate DATETIME = DATEADD(DAY, 1, #startDate)
SELECT * FROM [table] WHERE [some_Date] BETWEEN #startDate AND #endDate
SELECT * FROM dbo.tbl_MyTable
WHERE
REPLACE(CONVERT(VARCHAR(9), DateTimeValueColumn, 6), ' ', '-')='02-Jul-12'
or
On chage in code is instead of using getdate function voncert you datestring in datetime format and do compare this follow query will work for you
SELECT * FROM dbo.tbl_MyTable
WHERE
CAST(CONVERT(CHAR(10), DateTimeValueColumn, 102) AS DATE) =
CAST(CONVERT(CHAR(10),GETDATE(),102) AS DATE)
If you are storing dates as characters -- which is not recommended -- you should at least use ISO format: YYYY-MM-DD hh:mm:ss. This makes the date useful for sorting and comparisons ("<" works, ">" works, "between" works as well as equals).
To extract the date, you can then use left(datestr, 10). In your format, you would use:
where left(datestr, 9) = '01-Jan-13'
If you are storing the fields as a datetime or smalldatetime, you may think they are stored as a string. They are not. They are stored as some number of days since some particular date, with day parts stored as fractional days. If you are using SQL Server 2005 or greater, then the best way is:
where cast(datetime as date) = '2013-01-01' -- I recommend ISO formats, even for constants. '20130101' is even better
To select rows with today's date (not time)
select * from myTable where datediff(dd, dateColumn, getdate()) = 0

Best practice: Searching table against day, month or year

I've got a table with a "date" column, where a user input will be queried against (using stored procedure)..and results will be shown on a datagrid..
now a user can either enter a year, Year/month , Year/month/day.. (from drop down lists)
i know there r many possible ways to handle the different queries.. however i am trying to figure out which would be best practice:
Solution 1: having 3 different stored procedures , one for every case.
Solution 2: having 1 stored procedure, with 1 extra parameter as searchlvl , then using IF ELSE statements to decide what lvl of search should be applied.
Solution 3: having 1 stored procedure, and sending the datetime as 3 different parameters , then checking IF parameter is null , and using that to decide search lvl
Solution 4: your suggestions :)
NOTE: i know how to do partial search(using datepart), my question is about best practice among the 3 solutions i offered or any other solution offered in the answers..
Like which would be faster, lighter on database and such..
and which would be slower, heavier..
There are no levels.
When user selects year 2009, you search rows where date >= '2009.01.01 00:00' and < '2010.01.01 00:00'.
When he selects month 01 of year 2009 you search where date >= '2009.01.01 00:00' and < '2009.02.01 00:00'.
Of course you don't pass dates as strings, you should use CONVERT() or pass dates as DATETIME type. This is universal solution and will be fast, because it will use indexes. You can create stored procedure that takes two dates, it will allow to search by every date range, not only year/month/day.
I'd do none of the above.
You should design you stored procedure to take three different ints, one for day, one for month and one for year. Leave the parameters nullable, but establish a convention so only meaningful parameter combinations are used. Then you construct a MINDATE and MAXDATE from the parameters.
Searching Datetime columns based on day/year/month requires a query like:
SELECT * FROM table WHERE date > MINDATE AND date < MAXDATE
which is pretty inefficient but not a definite problem.
Another approach (if the table is huge) would be to create an indexed view with year/month/day integer columns and search for exact matches there. To create such a view use DATEPART().
You can use datepart to get the parts of you date you want to filter against as
declare #table table(
DateVal DATETIME
)
INSERT INTO #table SELECT GETDATE()
DECLARE #Year INT,
#Month INT,
#Day INT
SELECT #Year = 2009
SELECT DATEPART(YY, DateVal) DateYear,
DATEPART(MM, DateVal) DateMonth,
DATEPART(DD, DateVal) DateDay,
*
FROM #table
WHERE (DATEPART(YY, DateVal) = #Year OR #Year IS NULL)
AND (DATEPART(MM, DateVal) = #Month OR #Month IS NULL)
AND (DATEPART(DD, DateVal) = #Day OR #Day IS NULL)
I'd pass in year/month/date as separate parameters into one stored proc, say default of NULL.
Then, I'd use DATEADD to build up from/to datetimes and use that
...
SELECT
#ToYear = ISNULL(#ToYear, DATEPART(year, GETDATE()), --or some base value, such as "1900"
#ToMonth = ...
...
SELECT
#DateTo = DATEADD(year, #ToYear, DATEADD(month, #ToMonth, DATEADD(day, #ToDay, 0), 0), 0)
....
SELECT * FROM myTable WHERE DateColumn >= #DateFrom AND DateColumn <= #DateTo
I would not use any functions on columns or conditional logic to switch between selects

How can I compare time in SQL Server?

I'm trying to compare time in a datetime field in a SQL query, but I don't know if it's right. I don't want to compare the date part, just the time part.
I'm doing this:
SELECT timeEvent
FROM tbEvents
WHERE convert(datetime, startHour, 8) >= convert(datetime, #startHour, 8)
Is it correct?
I'm asking this because I need to know if 08:00:00 is less or greater than 07:30:00 and I don't want to compare the date, just the time part.
Thanks!
Your compare will work, but it will be slow because the dates are converted to a string for each row. To efficiently compare two time parts, try:
declare #first datetime
set #first = '2009-04-30 19:47:16.123'
declare #second datetime
set #second = '2009-04-10 19:47:16.123'
select (cast(#first as float) - floor(cast(#first as float))) -
(cast(#second as float) - floor(cast(#second as float)))
as Difference
Long explanation: a date in SQL server is stored as a floating point number. The digits before the decimal point represent the date. The digits after the decimal point represent the time.
So here's an example date:
declare #mydate datetime
set #mydate = '2009-04-30 19:47:16.123'
Let's convert it to a float:
declare #myfloat float
set #myfloat = cast(#mydate as float)
select #myfloat
-- Shows 39931,8244921682
Now take the part after the comma character, i.e. the time:
set #myfloat = #myfloat - floor(#myfloat)
select #myfloat
-- Shows 0,824492168212601
Convert it back to a datetime:
declare #mytime datetime
set #mytime = convert(datetime,#myfloat)
select #mytime
-- Shows 1900-01-01 19:47:16.123
The 1900-01-01 is just the "zero" date; you can display the time part with convert, specifying for example format 108, which is just the time:
select convert(varchar(32),#mytime,108)
-- Shows 19:47:16
Conversions between datetime and float are pretty fast, because they're basically stored in the same way.
convert(varchar(5), thedate, 108) between #leftTime and #rightTime
Explanation:
if you have varchar(5) you will obtain HH:mm
if you have varchar(8) you obtain HH:mm ss
108 obtains only the time from the SQL date
#leftTime and #rightTime are two variables to compare
If you're using SQL Server 2008, you can do this:
WHERE CONVERT(time(0), startHour) >= CONVERT(time(0), #startTime)
Here's a full test:
DECLARE #tbEvents TABLE (
timeEvent int IDENTITY,
startHour datetime
)
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 0, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 1, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 2, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 3, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 4, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 5, GETDATE())
--SELECT * FROM #tbEvents
DECLARE #startTime datetime
SET #startTime = DATEADD(mi, 65, GETDATE())
SELECT
timeEvent,
CONVERT(time(0), startHour) AS 'startHour',
CONVERT(time(0), #startTime) AS '#startTime'
FROM #tbEvents
WHERE CONVERT(time(0), startHour) >= CONVERT(time(0), #startTime)
Just change convert datetime to time that should do the trick:
SELECT timeEvent
FROM tbEvents
WHERE convert(time, startHour) >= convert(time, #startHour)
if (cast('2012-06-20 23:49:14.363' as time) between
cast('2012-06-20 23:49:14.363' as time) and
cast('2012-06-20 23:49:14.363' as time))
One (possibly small) issue I have noted with the solutions so far is that they all seem to require a function call to process the comparison. This means that the query engine will need to do a full table scan to seek the rows you are after - and be unable to use an index. If the table is not going to get particularly large, this probably won't have any adverse affects (and you can happily ignore this answer).
If, on the other hand, the table could get quite large, the performance of the query could suffer.
I know you stated that you do not wish to compare the date part - but is there an actual date being stored in the datetime column, or are you using it to store only the time? If the latter, you can use a simple comparison operator, and this will reduce both CPU usage, and allow the query engine to use statistics and indexes (if present) to optimise the query.
If, however, the datetime column is being used to store both the date and time of the event, this obviously won't work. In this case if you can modify the app and the table structure, separate the date and time into two separate datetime columns, or create a indexed view that selects all the (relevant) columns of the source table, and a further column that contains the time element you wish to search for (use any of the previous answers to compute this) - and alter the app to query the view instead.
Using float does not work.
DECLARE #t1 datetime, #t2 datetime
SELECT #t1 = '19000101 23:55:00', #t2 = '20001102 23:55:00'
SELECT CAST(#t1 as float) - floor(CAST(#t1 as float)), CAST(#t2 as float) - floor(CAST(#t2 as float))
You'll see that the values are not the same (SQL Server 2005). I wanted to use this method to check for times around midnight (the full method has more detail) in which I was comparing the current time for being between 23:55:00 and 00:05:00.
Adding to the other answers:
you can create a function for trimming the date from a datetime
CREATE FUNCTION dbo.f_trimdate (#dat datetime) RETURNS DATETIME AS BEGIN
RETURN CONVERT(DATETIME, CONVERT(FLOAT, #dat) - CONVERT(INT, #dat))
END
So this:
DECLARE #dat DATETIME
SELECT #dat = '20080201 02:25:46.000'
SELECT dbo.f_trimdate(#dat)
Will return
1900-01-01 02:25:46.000
Use Datepart function: DATEPART(datepart, date)
E.g#
SELECT DatePart(#YourVar, hh)*60) +
DatePart(#YourVar, mi)*60)
This will give you total time of day in minutes allowing you to compare more easily.
You can use DateDiff if your dates are going to be the same, otherwise you'll need to strip out the date as above
You can create a two variables of datetime, and set only hour of date that your need to compare.
declare #date1 datetime;
declare #date2 datetime;
select #date1 = CONVERT(varchar(20),CONVERT(datetime, '2011-02-11 08:00:00'), 114)
select #date2 = CONVERT(varchar(20),GETDATE(), 114)
The date will be "1900-01-01" you can compare it
if #date1 <= #date2
print '#date1 less then #date2'
else
print '#date1 more then #date2'
SELECT timeEvent
FROM tbEvents
WHERE CONVERT(VARCHAR,startHour,108) >= '01:01:01'
This tells SQL Server to convert the current date/time into a varchar using style 108, which is "hh:mm:ss". You can also replace '01:01:01' which another convert if necessary.
I believe you want to use DATEPART('hour', datetime).
Reference is here:
http://msdn.microsoft.com/en-us/library/ms174420.aspx
I don't love relying on storage internals (that datetime is a float with whole number = day and fractional = time), but I do the same thing as the answer Jhonny D. Cano. This is the way all of the db devs I know do it. Definitely do not convert to string. If you must avoid processing as float/int, then the best option is to pull out hour/minute/second/milliseconds with DatePart()
I am assuming your startHour column and #startHour variable are both DATETIME; In that case, you should be converting to a string:
SELECT timeEvent
FROM tbEvents
WHERE convert(VARCHAR(8), startHour, 8) >= convert(VARCHAR(8), #startHour, 8)
below query gives you time of the date
select DateAdd(day,-DateDiff(day,0,YourDateTime),YourDateTime) As NewTime from Table
#ronmurp raises a valid concern - the cast/floor approach returns different values for the same time. Along the lines of the answer by #littlechris and for a more general solution that solves for times that have a minute, seconds, milliseconds component, you could use this function to count the number of milliseconds from the start of the day.
Create Function [dbo].[MsFromStartOfDay] ( #DateTime datetime )
Returns int
As
Begin
Return (
( Datepart( ms , #DateTime ) ) +
( Datepart( ss , #DateTime ) * 1000 ) +
( Datepart( mi , #DateTime ) * 1000 * 60 ) +
( Datepart( hh , #DateTime ) * 1000 * 60 * 60 )
)
End
I've verified that it returns the same int for two different dates with the same time
declare #first datetime
set #first = '1900-01-01 23:59:39.090'
declare #second datetime
set #second = '2000-11-02 23:56:39.090'
Select dbo.MsFromStartOfDay( #first )
Select dbo.MsFromStartOfDay( #second )
This solution doesn't always return the int you would expect. For example, try the below in SQL 2005, it returns an int ending in '557' instead of '556'.
set #first = '1900-01-01 23:59:39.556'
set #second = '2000-11-02 23:56:39.556'
I think this has to do with the nature of DateTime stored as float. You can still compare the two number, though. And when I used this approach on a "real" dataset of DateTime captured in .NET using DateTime.Now() and stored in SQL, I found that the calculations were accurate.
TL;DR
Separate the time value from the date value if you want to use indexes in your search (you probably should, for performance). You can: (1) use function-based indexes or (2) create a new column for time only, index this column and use it in you SELECT clause.
Keep in mind you will lose any index performance boost if you use functions in a SQL's WHERE clause, the engine has to do a scan search. Just run your query with EXPLAIN SELECT... to confirm this. This happens because the engine has to process EVERY value in the field for EACH comparison, and the converted value is not indexed.
Most answers say to use float(), convert(), cast(), addtime(), etc.. Again, your database won't use indexes if you do this. For small tables that may be OK.
It is OK to use functions in WHERE params though (where field = func(value)), because you won't be changing EACH field's value in the table.
In case you want to keep use of indexes, you can create a function-based index for the time value. The proper way to do this (and support for it) may depend on your database engine. Another option is adding a column to store only the time value and index this column, but try the former approach first.
Edit 06-02
Do some performance tests before updating your database to have a new time column or whatever to make use of indexes. In my tests, I found out the performance boost was minimal (when I could see some improvement) and wouldn't be worth the trouble and overhead of adding a new index.