Select data in date format - sql

I have a query in which I want to select data from a column where the data is a date. The problem is that the data is a mix of text and dates.
This bit of SQL only returns the longest text field:
SELECT MAX(field_value)
Where the date does occur, it is always in the format xx/xx/xxxx
I'm trying to select the most recent date.
I'm using MS SQL.
Can anyone help?

Try this using ISDATE and CONVERT:
SELECT MAX(CONVERT(DateTime, MaybeDate))
FROM (
SELECT MaybeDate
FROM MyTable
WHERE ISDATE(MaybeDate) = 1) T
You could also use MAX(CAST(MaybeDate AS DateTime)). I got in the (maybe bad?) habit of using CONVERT years ago and have stuck with it.

To do this without a conversion error:
select max(case when isdate(col) = 1 then cast(col as date) end) -- or use convert()
from . . .
The SQL statement does not specify the order of operations. So, even including a where clause in a subquery will not guarantee that only dates get converted. In fact, the SQL Server optimizer is "smart" enough to do the conversion when the data is brought in and then do the filtering afterwards.
The only operation that guarantees sequencing of operations is the case statement, and there are even exceptions to that.

Another solution would be using PATINDEX in WHERE clause.
SELECT PATINDEX('[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]', field_value)
Problem with this approach is you really are not sure if something is date (e.g. 99/99/9999 is not date).
And problem with IS_DATE is it depends on configuration (e.g. DATEFORMAT).
So, use an appropriate option.

Related

Is there any way to use CASE in WHERE clause in Oracle?

I have the following data in DB which supposed to be a date but in number format:
date_col:
20200130.95656
20200910.85213
0
20220101.55412
0
0
20220730.85626
Now I need to format the dates to mm/dd/yyyy so I used the following command in oracle:
to_char(to_date(trunc(date_col),'yyyymmdd'),'mm/dd/yyyy')
to output it like this 07/30/22. But the problem I still need to get the 0 values in the date_col and at the same time to only get the dates between 09/10/2020 to 07/30/2022 so I was wondering if there is a way to do this in Oracle?
I can filter the dates by using the following code:
SELECT char(to_date(trunc(date_col),'yyyymmdd'),'mm/dd/yyyy')
FROM table1 WHERE
date_col != 0
AND char(to_date(trunc(date_col),'yyyymmdd'),'mm/dd/yyyy') BETWEEN '09/10/2020' AND '07/30/2022';
but I need to get the 0 at the same time. I'm thinking on using CASE on the WHERE clause but don't know how. I Googled but doesn't seems to answer my question. I'm only used to using CASE on the SELECT and not on WHERE so I'm wondering if there is a trick on using it on WHERE.
Update
Output should be:
data_col
________________
- 09/10/2020
- 0
- 01/01/2022
- 0
- 0
- 07/30/2022
Date is on number because of vendor who created the formatting of the date..
Before we get into this, there are a few things I need to point out here. I know you may not have much influence on the schema design, but you need to understand you are working with a BROKEN SCHEMA.
Building on this, it bears repeating internationalization/cultural issues mean converting these numbers to strings and then to dates is about the slowest and most error-prone way possible to accomplish the conversion to date values.
Finally, the BETWEEN '09/10/2020' AND '07/30/2022' comparison is horribly broken. The left-hand side casts back to char and so you are doing string comparisons instead of date comparisons. There is no value that will ever satisfy this condition, because when evaluating the boundary strings the 0s are equal, the 9 is already greater than the 7, and that's as far as you'll ever get.
That out of the way, you can use CASE in a WHERE clause. However, remember that CASE is not like an if() statement. You don't use it to select code branches. It is an expression... it produces a value. Therefore, to use CASE with a WHERE clause, you want something to compare with that value result. For example: WHERE 0 = CASE WHEN SomeColumn=1 THEN 0 ELSE 1 END
But we don't need CASE here. This will do the job:
SELECT char(to_date(trunc(date_col),'yyyymmdd'),'mm/dd/yyyy')
FROM table1
WHERE date_col = 0 OR date_col BETWEEN 20200910.0 AND 20220730.99999
This will perform much better, not only because it avoids the extra conversions, but in leaving the column values intact for the comparison it also preserves the ability to use indexes with the column, which cuts to the core of database performance.
You could use this. If your date_col have no decimal part you could use BETWEEN instead
SELECT TO_CHAR(TO_DATE(TRUNC(date_col),'yyyymmdd'),'mm/dd/yyyy')
FROM table1
WHERE date_col = 0
OR (date_col >= 20200910 AND date_col < 20200730);

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?

Cannot use calculated offset in BigQuery's DATE_ADD function

I'm trying to create a custom query in Tableau to use on Google's BigQuery. The goal is to have an offset parameter in Tableau that changes the offsets used in a date based WHERE clause.
In Tableau it would look like this:
SELECT
DATE_ADD(UTC_USEC_TO_MONTH(CURRENT_DATE()),<Parameters.Offset>-1,"MONTH") as month_index,
COUNT(DISTINCT user_id, 1000000) as distinct_count
FROM
[Orders]
WHERE
order_date >= DATE_ADD(UTC_USEC_TO_MONTH(CURRENT_DATE()),<Parameters.Offset>-12,"MONTH")
AND
order_date < DATE_ADD(UTC_USEC_TO_MONTH(CURRENT_DATE()),<Parameters.Offset>-1,"MONTH")
However, BigQuery always returns an error:
Error: DATE_ADD 2nd argument must have INT32 type.
When I try the same query in the BigQuery editor using simple arithmetic it fails with the same error.
SELECT
DATE_ADD(UTC_USEC_TO_MONTH(CURRENT_DATE()),5-3,"MONTH") as month_index,
FROM [Orders]
Any workaround for this? My only option so far is to make multiple offsets in Tableau, it seems.
Thanks for the help!
I acknowledge that this is a hole in functionality of DATE_ADD. It can be fixed, but it will take some time until fix is rolled into production.
Here is a possible workaround. It seems to work if the first argument to DATE_ADD is a string. Then you can truncate the result to a month boundary and convert it from a timestamp to a string.
SELECT
FORMAT_UTC_USEC(UTC_USEC_TO_MONTH(DATE_ADD(CURRENT_DATE(),5-3,"MONTH"))) as month_index;

Using DAY(), WEEK(), and YEAR() at one query

i using MySQL Query for my task.
And I interested using Date and time function.
can i use DAY(), WEEK(), and YEAR() at one query?
SELECT Object
FROM table
WHERE DAY(date) BETWEEN 1 AND 7
GROUP BY WEEK(date, 1), YEAR(date)
i want do this bcoz i'm worry if sometimes my program have an error because of the date setting and not recognize some date.please give me an input.
Yes, you can use them all in a single query.
The only disadvantage I can think of is that using any of the DAY, WEEK or YEAR functions won't be able to use the index on the column the function is applied to, assuming one is present.
If you're having issues relating to date formatting, you should get familiar with:
DATE_FORMAT
STR_TO_DATE

split string in sql query

I have a value in field called "postingdate" as string in 2009-11-25, 12:42AM IST format, in a table named "Post".
I need the query to fetch the details based on date range. I tried the following query, but it throws an error. Please guide me to fix this issue. Thanks in advance.
select postingdate
from post
where TO_DATE(postingDate,'YYYY-MM-DD')>61689
and TO_DATE(postingDate,'YYYY-MM-DD')<61691
As you've now seen, trying to perform any sort of query against a string column which represents a date is a problem. You've got a few options:
Convert the postingdate column to some sort of DATE or TIMESTAMP datatype. I think this is your best choice as it will make querying the table using this field faster, more flexible, and less error prone.
Leave postingdate as a string and use functions to convert it back to a date when doing comparisons. This will be a performance problem as most queries will turn into full table scans unless your database supports function-based indexes.
Leave postingdate as a string and compare it against other strings. Not a good choice as it's tough to come up with a way to do ranged queries this way, as I think you've found.
If it was me I'd convert the data. Good luck.
In SQL Server you can say
Select postingdate from post
where postingdate between '6/16/1969' and '6/16/1991'
If it's really a string, you're lucky that it's in YYYY-MM-DD format. You can sort and compare that format as a string, because the most significant numbers are on the left side. For example:
select *
from Posts
where StringDateCol between '2010-01-01' and '2010-01-02'
There's no need to convert the string to a date, comparing in this way is not affected by the , 12:42AM IST appendage. Unless, of course, your table contains dates from a different time zone :)
You will need to convert your string into a date before you run date range queries on it. You may get away with just using the string if your not interested in the time portion.
The actual functions will depend on your RDBMS
for strings only
select * from posts
where LEFT(postingDate,10) > '2010-01-21'
or
for datetime ( Sybase example)
select * from posts
where convert(DateTime,postingDate) between '2010-01-21' and '2010-01-31'