Cast string as date and use it in comparison - sql

I have a table as
NUM | TDATE
1 | 200712
2 | 200708
3 | 200704
4 | 20081210
where mytable is created as
mytable
(
num int,
tdate char(8) -- legacy
);
The format of tdate is YYYYMMDD.. sometimes the date part is optional.
So a date such as "200712" can be interpreted as 2007-12-01.
I want to write query such that i can treat tdate as a Date column and apply date comparison.
like
select num, tdate from mytable where tdate
between '2007-12-31 00:00:00' and '2007-05-01 00:00:00'
So far i tried this
select num, tdate,
CAST(LEFT(tdate,6)
+ COALESCE(NULLIF(SUBSTRING(CAST(tdate AS VARCHAR(8)),7,8),''),'01') AS Date)
from mytable
SQL Fiddle
How can I use the above converted date (3rd column ) for comparison? (needs a join?)
Also is there a better way to do this?
Edit: I have no control over the table scheme for now.. we have suggested the change to the DB team..for now have to stick with char(8) .

I think this a better way to get your fixed date:
SELECT CAST(LEFT(RTRIM(tdate) + '01',8) AS DATE)
You can create a subquery/cte with the date cast properly:
;WITH cte AS (select num, tdate,CAST(LEFT(RTRIM(tdate)+ '01',8) AS DATE)'FixedDate'
from mytable )
select num, FixedDate
from cte
where FixedDate
between '2007-12-31' and '2007-05-01'
Or you can just use your fixed date in the query directly:
select num, tdate
from mytable
where CAST(LEFT(RTRIM(tdate)+ '01',8) AS DATE) between '2007-12-31' and '2007-05-01'
Ideally you would add the fixed date field to your table so that queries can benefit from indexing the date.
Note: Be wary of BETWEEN with DATETIME as the time portion can result in undesired results if you really only care about the DATE portion.

'2007-12-31 00:00:00' > '2007-05-01 00:00:00', so your BETWEEN clause will never return any records.
This will work, with a subquery, and with the dates flipped:
select num, tdate, formattedDate
from
(
select num, tdate
,
CAST(LEFT(tdate,6) + COALESCE(NULLIF(SUBSTRING(CAST(tdate AS VARCHAR(8)),7,8),''),'01') AS Date) as formattedDate
from mytable
) a
where formattedDate between '2007-05-01 00:00:00' and '2007-12-31 00:00:00'
sqlFiddle here

I think you should avoid storing date in string type fields. If that is something you have to live with try following solution.
Since you are having yyyymmdd or yyyymm format you can first get them all in yyyymmdd format which is Culture independent ISO format and then use style 112 to convert into Date for comparison:
--Culture independent solution
;with cte as (
select num, tdate,
convert(date,left(rtrim(tdate) + '01',8),112) mydate --yyyymmdd format
from mytable
)
select num,tdate,mydate
from cte
where mydate between convert(date,'20071231',112) and --Values are in yyyymmdd format
convert(date,'20070501',112)

Yet another way to turn your string values into dates would be to use REPLACE:
SELECT num, tdate
FROM mytable
WHERE CAST(REPLACE(tdate, ' ', '01') AS date) BETWEEN #date1 AND #date2
;
If you really want to both return the converted date value and use it for filtering, you can employ CROSS APPLY to avoid repeating the logic:
SELECT t.num, t.tdate, x.date
FROM mytable AS t
CROSS APPLY (SELECT CAST(REPLACE(t.tdate, ' ', '01') AS date)) AS x (date)
WHERE x.date BETWEEN #date1 AND #date2
;
This method assumes that your char(8) strings are formatted as either YYYYMMDD or YYYYMM, although the method will work without any changes if you decide to start using values formatted as just YYYY in addition to the other two formats (to imply the beginning of a year, just like a YYYYMM implies the beginning of a month).

with date_cte(num,date)as
(select num,CAST(LEFT(tdate,6)
+ COALESCE(NULLIF(SUBSTRING(CAST(tdate AS VARCHAR(8)),7,8),''),'01') AS Date)
from mytable)
select t1.num, t1.tdate,t2.date
from mytable t1 join date_cte t2 on t1.num=t2.num
where t2.date
between '2007-12-31 00:00:00' and '2007-05-01 00:00:00'

I don't have the time to test right now, but something like this may work...
select num, tdate
from mytable
WHERE CAST(LEFT(tdate,6)
+ COALESCE(NULLIF(SUBSTRING(CAST(tdate AS VARCHAR(8)),7,8),''),'01') AS Date) BETWEEN CAST('2007-12-31 00:00:00' as smalldatetime) and CAST('2007-05-01 00:00:00' as smalldatetime)

My proposal would be to add a date field to your table. If your table is regularly updated, fill it from the legacy field through a stored proc on a regular schedule (either trigger or job).
You'll then be able to use the date as ... a date, without all these tricks, turnarounds, and other approximations which are all potential source for confusion, mistakes and questionable results.

Related

Extracting data from only the year

I have data in a table in SQL with dates, but how do I select only those that happen in 2021. (The dates look like 31-oct-2020) in the table. The dates are the actual date variable, not just text.
You should avoid storing your dates as text, but rather should use a proper date column. That being said, you may check the right 4 characters of the date string:
SELECT *
FROM yourTable
WHERE RIGHT(date_col, 4) = '2021';
If the column be an actual date type then use:
SELECT *
FROM yourTable
WHERE date_col >= '2021-01-01' AND date_col < '2022-01-01';
I suspect that your DB is Oracle after checking out your previous post. Then you can use
SELECT *
FROM yourTable
WHERE EXTRACT(year FROM dt) = 2021
or
SELECT *
FROM yourTable
WHERE TRUNC(dt,'YYYY') = date'2021-01-01'
or
SELECT *
FROM yourTable
WHERE dt BETWEEN date'2021-01-01' AND date'2021-12-31'
You can benefit the index if there's one on the date column(namely dt) by using the last SELECT statement
If using MSSQL, you can leverage the YEAR(...) to extract the year from a date.
Replace and , with the table name and date column name respectively.
select * from <tablename> where year(<datecolumn>) = 2021

Display a date that is the same as today in SQL Server [duplicate]

I have a table TEST with a DATETIME field, like this:
ID NAME DATE
1 TESTING 2014-03-19 20:05:20.000
What I need a query returning this row and every row with date 03/19/2014, no matter what the time is. I tried using
select * from test where date = '03/19/2014';
But it returns no rows. The only way to make it work that I found is to also provide the time portion of the date:
select * from test where date = '03/19/2014 20:03:02.000';
use range, or DateDiff function
select * from test
where date between '03/19/2014' and '03/19/2014 23:59:59'
or
select * from test
where datediff(day, date, '03/19/2014') = 0
Other options are:
If you have control over the database schema, and you don't need the
time data, take it out.
or, if you must keep it, add a computed column attribute that has the time portion of the date value stripped off...
Alter table Test
Add DateOnly As
DateAdd(day, datediff(day, 0, date), 0)
or, in more recent versions of SQL Server...
Alter table Test
Add DateOnly As
Cast(DateAdd(day, datediff(day, 0, date), 0) as Date)
then, you can write your query as simply:
select * from test
where DateOnly = '03/19/2014'
Simple answer;
select * from test where cast ([date] as date) = '03/19/2014';
I am using MySQL 5.6 and there is a DATE function to extract only the date part from date time. So the simple solution to the question is -
select * from test where DATE(date) = '2014-03-19';
http://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html
This works for me for MS SQL server:
select * from test
where
year(date) = 2015
and month(date) = 10
and day(date)= 28 ;
select * from test
where date between '03/19/2014' and '03/19/2014 23:59:59'
This is a realy bad answer. For two reasons.
1.
What happens with times like 23.59.59.700 etc.
There are times larger than 23:59:59 and the next day.
2.
The behaviour depends on the datatype.
The query behaves differently for datetime/date/datetime2 types.
Testing with 23:59:59.999 makes it even worse because depending on the datetype you get different roundings.
select convert (varchar(40),convert(date , '2014-03-19 23:59:59.999'))
select convert (varchar(40),convert(datetime , '2014-03-19 23:59:59.999'))
select convert (varchar(40),convert(datetime2 , '2014-03-19 23:59:59.999'))
-- For date the value is 'chopped'.
-- For datetime the value is rounded up to the next date. (Nearest value).
-- For datetime2 the value is precise.
use this
select * from TableName where DateTimeField > date() and DateTimeField < date() + 1
Try this
select * from test where Convert(varchar, date,111)= '03/19/2014'
you can try this
select * from test where DATEADD(dd, 0, DATEDIFF(dd, 0, date)) = '03/19/2014';
There is a problem with dates and languages and the way to avoid it is asking for dates with this format YYYYMMDD.
This way below should be the fastest according to the link below. I checked in SQL Server 2012 and I agree with the link.
select * from test where date >= '20141903' AND date < DATEADD(DAY, 1, '20141903');
Bad habits to kick : mis-handling date / range queries
You can use this approach which truncates the time part:
select * from test
where convert(datetime,'03/19/2014',102) = DATEADD(dd, DATEDIFF(dd, 0, date), 0)
-- Reverse the date format
-- this false:
select * from test where date = '28/10/2015'
-- this true:
select * from test where date = '2015/10/28'
Simply use this in your WHERE clause.
The "SubmitDate" portion below is the column name, so insert your own.
This will return only the "Year" portion of the results, omitting the mins etc.
Where datepart(year, SubmitDate) = '2017'
select *, cast ([col1] as date) <name of the column> from test where date = 'mm/dd/yyyy'
"col1" is name of the column with date and time
<name of the column> here you can change name as desired
select *
from invoice
where TRUNC(created_date) <=TRUNC(to_date('04-MAR-18 15:00:00','dd-mon-yy hh24:mi:ss'));
Test this query.
SELECT *,DATE(chat_reg_date) AS is_date,TIME(chat_reg_time) AS is_time FROM chat WHERE chat_inbox_key='$chat_key'
ORDER BY is_date DESC, is_time DESC
select * from invoice where TRANS_DATE_D>= to_date ('20170831115959','YYYYMMDDHH24MISS')
and TRANS_DATE_D<= to_date ('20171031115959','YYYYMMDDHH24MISS');
SELECT * FROM test where DATEPART(year,[TIMESTAMP]) = '2018' and DATEPART(day,[TIMESTAMP]) = '16' and DATEPART(month,[TIMESTAMP]) = '11'
use trunc(column).
select * from test t where trunc(t.date) = TO_DATE('2018/06/08', 'YYYY/MM/DD')

Query to select data between two dates

I have a start_date and end_date. I want to get the list of data in between these two dates.
Can anyone help me pointing the mistake in my query.
select * from [dbo].[User]
where Date between'2014-8-01' AND '2014-8-30'
There is no error but also the query doesn't return any records.
Proper casting is necessary
select * from [dbo].[User]
where CAST([Date] as DATE) between '2014-08-01' AND '2014-08-30'
Format for Date Passing as Parameter
'yyyy-MM-dd'
Better to use >= and < and use time in your where clause instead of using CAST in where. Better performance.
SELECT * FROM [dbo].[User]
WHERE [Date] >= '2014-8-01 00:00:00'
AND [Date] < '2014-8-31 00:00:00'

How do I get a maximium daily value of a numerical field over a year in SQL

How do I get a maximium daily value of a numerical field over a year in MS-SQL
This would query the daily maximum of value over 2008:
select
datepart(dayofyear,datecolumn)
, max(value)
from yourtable
where '2008-01-01' <= datecolumn and datecolumn < '2009-01-01'
group by datepart(dayofyear,datecolumn)
Or the daily maximum over each year:
select
datepart(year,datecolumn),
, datepart(dayofyear,datecolumn)
, max(value)
from yourtable
group by datepart(year,datecolumn), datepart(dayofyear,datecolumn)
Or the day(s) with the highest value in a year:
select
Year = datepart(year,datecolumn),
, DayOfYear = datepart(dayofyear,datecolumn)
, MaxValue = max(MaxValue)
from yourtable
inner join (
select
Year = datepart(year,datecolumn),
, MaxValue = max(value)
from yourtable
group by datepart(year,datecolumn)
) sub on
sub.Year = yourtable.datepart(year,datecolumn)
and sub.MaxValue = yourtable.value
group by
datepart(year,datecolumn),
datepart(dayofyear,datecolumn)
You didn't mention which RDBMS or SQL dialect you're using. The following will work with T-SQL (MS SQL Server). It may require some modifications for other dialects since date functions tend to change a lot between them.
SELECT
DATEPART(dy, my_date),
MAX(my_number)
FROM
My_Table
WHERE
my_date >= '2008-01-01' AND
my_date < '2009-01-01'
GROUP BY
DATEPART(dy, my_date)
The DAY function could be any function or combination of functions which gives you the days in the format that you're looking to get.
Also, if there are days with no rows at all then they will not be returned. If you need those days as well with a NULL or the highest value from the previous day then the query would need to be altered a bit.
Something like
SELECT dateadd(dd,0, datediff(dd,0,datetime)) as day, MAX(value)
FROM table GROUP BY dateadd(dd,0, datediff(dd,0,datetime)) WHERE
datetime < '2009-01-01' AND datetime > '2007-12-31'
Assuming datetime is your date column, dateadd(dd,0, datediff(dd,0,datetime)) will extract only the date part, and then you can group by that value to get a maximum daily value. There might be a prettier way to get only the date part though.
You can also use the between construct to avoid the less than and greater than.
Group on the date, use the max delegate to get the highest value for each date, sort on the value, and get the first record.
Example:
select top 1 theDate, max(theValue)
from TheTable
group by theDate
order by max(theValue) desc
(The date field needs to only contain a date for this grouping to work, i.e. the time component has to be zero.)
If you need to limit the query for a specific year, use a starting and ending date in a where claues:
select top 1 theDate, max(theValue)
from TheTable
where theDate between '2008-01-01' and '2008-12-13'
group by theDate
order by max(theValue) desc

sort distinct date column

I need distinct year and month from date column which would be sorted by same column.
I have date coulmn with values like (YYYY/MM/DD)
2007/11/7
2007/1/8
2007/11/4
2007/12/3
2008/10/4
2009/11/5
2008/5/16
after having query, it should be
2007/1/1
2007/11/1
2007/12/1
2008/5/1
2008/10/1
2009/11/1
This doesn't seems to be working
SELECT distinct (cast(year(datecol) as nvarchar(20) ) +
'/'+ cast(month(datecol) as nvarchar(20) ) + '/1') as dt1
FROM Table
ORDER BY dt1
Soemthing like this would work on MS SQL Server:
select
distinct
dateadd(day, -1 * DAY(datefield) + 1, datefield)
From
datetable
order by
dateadd(day, -1 * DAY(datefield) + 1, datefield)
The DATEADD function call basically subtracts (day-1) DAYS from the current date --> you always get the first of whatever month that date is in.
Sort by it and you're done! :-)
ADditionally, you could also add this functionality to your table as a "computed column" and then use that for easy acccess:
alter table yourTable
add FirstOfMonth As DATEADD(day, -1 * DAY(datefield) + 1, datefield) persisted
Then your query would be even simpler:
SELECT DISTINCT FirstOfMonth
FROM YourTable
ORDER BY FirstOfMonth
Marc
When dealing with dates in SqlServer avoid using cast like this - the resulting format will change depending on server config.
Instead use convert and choose a format (for instance 112) that adds leading zeros to the month.
Anil,
Do you also have time part in the dates ? What dataType are you using for the column ? Are you
using DateTime dataType or Char ?
This works for me
SELECT DISTINCT (DateField) AS Date FROM DateTable ORDER BY 1