How to use between clause for getting data between two dates? - sql

I have date field in the database in the format 2012-03-17 19:50:08.023.
I want to create a select query which gives me the data collected in the March month.
But I am not able to achieve this.
I am trying following query.
select * from OrderHeader where
Convert(varchar,UploadDt,103) between '01/03/2013' and '31/03/2013'
and DistUserUniqueID like '6361%'
This query gives me data for all the dates.
select * from OrderHeader where
UploadDt between '01/03/2013' and '31/03/2013' and DistUserUniqueID like '6361%'
This query gives me the error as Msg 242, Level 16, State 3, Line 1
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
Please help me resolve this.
Thanks in advance

The first query returns all dates because you are converting your column to a string. Not sure why you are doing this. So when you say BETWEEN '01/anything' AND '31/anything', when you consider it is now just a string, that is going to match all "dates" in the column, regardless of month and year, since your WHERE clause will cover every single day possible (well, with the exception of the 31st of months other than March, and the first day in January and February - so not all the data but a very large percentage). '15/11/2026', for example, is BETWEEN '01\03\2013' AND '31/03/2013'.
Think about that for a second. You have datetime data in your database, and you want to convert it to a string before you query it. Would you also convert salary to a string before comparing it? If so, the person making $70,000 will look like they are making more than the person making $690,000, since character-based sorting starts at the first character and doesn't consider length.
The second query fails because you are using a regional, ambiguous format for your dates. You may like dd/mm/yyyy but clearly your server is based on US English formatting where mm/dd/yyyy is expected.
The solution is to use a proper, unambiguous format, such as YYYYMMDD.
BETWEEN '20130301' AND '20130313'
However you shouldn't use BETWEEN - since this is a DATETIME column you should be using:
WHERE UploadDt >= '20130301'
AND UploadDt < '20130401'
(Otherwise you will miss any data from 2013-03-31 00:00:00.001 through 2013-03-31 23:59:59.997.)
If you really like BETWEEN then on 2008+ (you didn't tell us your version) you can use:
WHERE CONVERT(DATE, UploadDt) BETWEEN '20130301' AND '20130331'
More date-related tips here:
Dating Responsibly
Finally, when converting to VARCHAR (or any variable-length data types), always specify a length.

Rewrite the query as
select * from OrderHeader where
UploadDt between '01/03/2013' and '01/04/2013'
and DistUserUniqueID like '6361%'
or
select * from OrderHeader where
UploadDt between Convert(datetime,'01/03/2013', 103) and Convert(datetime,'01/04/2013',103)
and DistUserUniqueID like '6361%'

Related

SQL Server Not Casting String YYYYMMdd to Date

I have a query that was working fine before a server migration and now is not working. I'm trying to convert all dates to a specific month and year, but I keep getting this error:
Conversion failed when converting date and/or time from character string.
Looking into the data, there are no null values in InputDate, which is a date data type column. When I run the Concat() function everything is formatted as 'YYYYMMdd', yet both CAST and CONVERT fail with the same error.
Is there an issue with my query that I'm not seeing?
SELECT RandoSTUFF,
DATEADD(day,2,CAST(CONCAT('2023','02',FORMAT(InputDate,'dd')) AS date)) AS MovedDate
FROM a_table_
I expect the issue is you have date values near the end of their months, and you're trying to compose the equivalent values for February, which is shorter.
So if you have an InputDate value of, say, 2022-12-31 and run the code in the question, it will extract the 31 and concat it with the other values, and you'll end up trying to do this:
CAST('20230231' as Date)
Of course, there is no such date.
As it is, it's not clear whether you want such an input to map to February 28 or March 3. To fix this, you'll need to rethink the problem so you only try to map to valid dates, and ensure the final result is more clearly defined. This is one of the many reasons it's almost always better to use Date/time functions instead of composing dates from strings.

Strange behaviour of Sql query with between operator

There is this strange error in sql query.
The query is something like this.
select * from student where dob between '20150820' and '20150828'
But in the database the column of dob is varchar(14) and is in yyyyMMddhhmmss format,Say my data in the row is (20150827142545).If i fire the above query it should not retrive any rows as i have mentioned yyyyMMdd format in the query.But it retrives the row with yesterday date (i.e 20150827112535) and it cannot get the records with today's date (i.e 20150828144532)
Why is this happening??
Thanks for the help in advance
You can try like this:
select * from student
where convert(date,LEFT(dob,8)) between
convert(date'20150820') and convert(date,'20150828'))
Also as others have commented you need to store your date as Date instead of varchar to avoid such problems in future.
As already mentioned you would need to use the correct date type to have between behave properly.
select *
from student
where convert(date,LEFT(dob,8)) between '20150820' and '20150828'
Sidenote: You don't have to explicitly convert your two dates from text as this will be done implicitly as long as you use an unambiguous date representation, i.e. the ISO standard 'YYYYMMDD' or 'YYYY-MM-DD'. Of course if you're holding the values in variables then use date | datetime datatype
declare #startdate date
declare #enddate date
select *
from student
where convert(date,LEFT(dob,8)) between #startdate and #enddate
Sidenote 2: Performing the functions on your table dob column would prevent any indexes on that column from being used to their full potential in your execution plan and may result in slower execution, if you can, define the correct data type for the table dob column or use a persistent computed column or materialised view if your performance is a real issue.
Sidenote 3: If you need to maintain the time portion in your data i.e. date and time of birth, use the following to ensure all records are captured;
select *
from student
where
convert(date,LEFT(dob,8)) >= '20150820'
and convert(date,LEFT(dob,8)) < dateadd(d,1,'20150828')
All you have to do is to convert first the string to date.
select *
from student
where dob between convert(date, '20150820') and convert(date, '20150828')
Why is this happening?
The comparison is executed from left to right and the order of characters is determined by the codepage in use.
Sort Order
Sort order specifies the way that data values are sorted, affecting
the results of data comparison. The sorting of data is accomplished
through collations, and it can be optimized using indexes.
https://msdn.microsoft.com/en-us/library/ms143726.aspx
There are problems with between in T-SQL.
But if you want a fast answer convert to date first and use >= <= or even datediff to compare - maybe write a between function yourself if you want the easy use like between and no care about begin and start times ...
What do BETWEEN and the devil have in common?

Why my selection between two dates in the same table doesn't work?

I use SQL server 2014, i try the following query to select between two dates in the same table, the datatype is nvarchar, i executed the following query it just shows me three rows such('30/03/2015','30/04/2015','30/04/2015'),but in reality there is('29/02/2015','30/03/2015','31/04/2015','30/04/2015','30/04/2015')
select RegisteredDate
from Student
where Student.RegisteredDate between convert(nvarchar, '30/01/2014', 103)
and convert(nvarchar, '30/04/2015', 103)
Cast the other way round, other way you are comparing strings:
select RegisteredDate from Student
where convert(date, Student.RegisteredDate, 103) between '20140130' and '20150430'
The fact is that those dates saved as strings are ordered as:
'29/02/2015',
'30/03/2015',
'30/04/2015',
'30/04/2015',
'31/04/2015'
Now imagine where would you add filter values?
'29/02/2015',
'30/01/2014' --start date
/-------------\
|'30/03/2015',|
|'30/04/2015',|
|'30/04/2015',|
\-------------/
'30/04/2015' --end date
'31/04/2015'
So between will return you those three rows.
Also you have in your data 29/02/2015. In 2015 February ends on 28(you have incorrect data in tables already). You will never be able to insert such values if you choose types correctly.So the conclusion is:
Use Appropriate data types for your data!
As i have read the other answers and comments, i could recommend you to firstly change the datatype of "RegisteredDate" from "nvarchar" to "date". Secondly use this standard 'yyyy-MM-dd' below code is what you need
select RegisteredDate
from Student
where Student.RegisteredDate between '2014-01-30' and '2015-04-30'
you will not be in need of any conversions, this is how i do it for myself
Try to cast your string to date instead of casting all the dates in the table to string.
You are casting all the records in the table to string and it might be millions of them. This way not only your performance will be better but more important you will compare them to dates, not as String.
SELECT RegisteredDate
FROM Student
WHERE Student.RegisteredDate BETWEEN Convert(Date, '30/01/2014',103) AND Convert(Date, '30/04/2015', 103)
As I said in the comments, a string comparation uses alphabetical order one char after another starting from the left. For example: 20/01/1302 will be after 01/12/4016 as the "2" char goes after the "1" char in ASCII.
Update: convert Student.RegisteredDate to date if it is still in nvarchar type. I would recommend you to change the type if you can to this. It might be a source of bugs and performance problems if you do not do this.
SQL Server 2014 makes the string to dates conversion automagically but only if it needs to. Comparing a '30/04/2015' string with a nvarchar is just a String comparation.

Why this Query is returning different results for different date formats?

I have a table named book_data with batch_dt as column name of type varchar in sql server.
when I pass the query
SELECT DISTINCT batch FROM book_data
it gives me the following results
batch_dt
-------------
2012-10-31
-------------
2012-11-01
-------------
2012-11-02
-------------
2012-11-03
-------------
.
.
.
Now what I am doing is getting the total count of records between two dates. Fairly a simple query.
SELECT COUNT(*) FROM book_data WHERE CONVERT(varchar(12),CONVERT(datetime,batch_dt),101) BETWEEN '11/02/2012' and '10/31/2012'
the result is
112
and just by changing the month from 02 to 2 the query gives me 218 results
SELECT COUNT(*) FROM book_data WHERE CONVERT(varchar(12),CONVERT(datetime,batch_dt),101) BETWEEN '11/2/2012' and '10/31/2012'
why this different behaviour?
Use CAST(batch_dt AS DATE) instead, and pass the date in a language neutral manner, in the format YYYYMMDD. This way it will be comared as a date not as a varchar:
SELECT COUNT(*)
FROM book_data
WHERE CAST(batch_dt AS DATE)
BETWEEN '20121102' and '20121130'
But, this is not safe, if there was any value in barch_dt in a wrong format, you will get a casting error. In this case you can add ISDATE(batch_dt) = 1 to ensure that it is a valid data time. But it is better to make this column of datatype DateTime.
Another thing to note: is that BETWEEN is asymmetric in SQL Server, meaning that BETWEEN '11/02/2012' and '10/31/2012' is evaluated as:
DATE >= '11/02/2012'
AND
DATE <= '10/31/2012'
which will never be true, the reason it works for you is that the dates were be compared as strings not as a dates. But you have to keep it like BETWEEN the small value and the biggest value.
You compare string with BETWEEN. If you do so you need to make sure that you compare in the correct order => YYYYMMDD MM:SS would be a correct order.
If you can, add columns with type datetime and store real date time values in your database. If you can not do that you can split up the values and build a date value yourself. This is much slower then just use a CONVERT() or CAST() but you can make sure that it works even with wrong date-strings.
You can use PATINDEX(),LEFT(),RIGHT() keywords to get the values you need or you use a split() function (you can find many version google it, e.g. https://codereview.stackexchange.com/questions/15125/sql-server-split-function-optimized). If you use the split function, then split by / and then get year, month, day from the positions.
But best would be still to have correct datetime values stored in your db.
You get this different behavior because you don't compare the dates but the strings/varchars.
For a Date (or DateTime), 10/2/2012 is the same as 10/02/2012.
But for string, these values are (of course) different. It's just as if you'd compare 'abcd' with 'ab0cd'
SELECT COUNT(*)
FROM book_data
WHERE CONVERT(DATETIME,batch_dt,101) BETWEEN '11/2/2012' and '10/31/2012'
This would be more appropriate

SQL: Counting dates formatted as varchar

The task:
I need to count every record in a table that was added after 07/01/2011.
Info:
There is a column in the table "Date_added" which is formatted as varchar and contains dates in format mm/dd/yyyy.
What I've tried:
I tried the following query:
SELECT count(date_added) FROM Abatements_Doc WHERE date_added >= '07/01/2011'
Unfortunately it counted any date where the month was greater than 07 regardless of the year or day. I'm at a loss as to how I should format my query to get it to read the dates properly instead of by ascii sort. In my research I discovered the CAST and CONVERT options, but am not sure how to use them, or which one I should use if any. I'm extremely new to SQL, and want to make sure I don't mess up the data in the table. I just need to get the count as specified above without altering the data in the table in any way. Any help in this regard will be very much appreciated. Thanks in advance.
I didn't test. But try convert statement.
SELECT count(date_added)
FROM Abatements_Doc
WHERE convert(datetime,date_added,1) >= convert(datetime,'07/01/2011',1)
(1) don't use mm/dd/yyyy as your date format. Use a safe and unambiguous format; the only one I trust for DATETIME/SMALLDATETIME is YYYYMMDD.
(2) change the data type in your table to DATETIME/SMALLDATETIME/DATE depending on accuracy required. Then `WHERE date_added >= '20110701' will work just fine (and will use an index on that column, if one exists).
(3) if you can't change the data type, then the following will work just fine as well (though no index will be used):
WHERE CONVERT(DATETIME, date_added, 101) >= '20110701'
Varchar can't be compared, try changing your column to date type.
Convert both date_added column values and your target date to DATETIME and compare; as in:
SELECT COUNT(1)
FROM Abatements_Doc
WHERE (CONVERT(datetime, date_added, 101)) >= CONVERT('07/01/2011', date_added, 101))