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
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'
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.