SQL Fastest way to compare two dates (non standard varchar format and datetime) - sql

I have two "date" fields that I need to join on.
The first is a normal datetime in the format yyyy-mm-dd hh:mm:ss
The second is a varchar(8) in the red-headed step child format mmddyyyy
Now this gets painful because there is no easy way to convert to the corresponding type. There is a built-in format that is yyyymmdd but that doesn't match the varchar format.
There are two paths I can see:
declare #normal_date as datetime;
declare #hated_date as varchar(8);
set #normal_date='1974-11-01 00:00:00.000'
set #hated_date='11011974'
--cast to date time with string splits
select #normal_date
where CONVERT(datetime, RIGHT(#hated_date,4)+LEFT(#hated_date,2)+SUBSTRING(#hated_date,3,2))=#normal_date
--convert normal date to ackward format
select #normal_date
where REPLACE(CONVERT(varchar(10),#normal_date,101), '/','')=#hated_date
Which is better? Or is there a better way?
Edited to show costs
--Operator cost (39%)
CONVERT(datetime, RIGHT(#hated_date,4)+LEFT(#hated_date,2)+SUBSTRING(#hated_date,3,2))=#normal_date
--Operator cost (57%)
REPLACE(CONVERT(varchar(10),#normal_date,101), '/','')=#hated_date
--Operator cost (46%)
cast(stuff(stuff(#hated_date, 3,0, '/'),6,0,'/') as datetime)=#normal_date
--Operator cost (47%)
RIGHT(#hated_date, 4) + LEFT(#hated_date, 4)=#normal_date

This is yyyymmdd no?
RIGHT(#hated_date, 4) + LEFT(#hated_date, 4)
So, your script becomes
declare #normal_date as datetime;
declare #hated_date as varchar(8);
set #normal_date='1974-11-01 00:00:00.000'
set #hated_date='11011974'
--SELECT #hated_date = RIGHT(#hated_date, 4) + LEFT(#hated_date, 4))
select 'hurrah' WHERE #normal_date = RIGHT(#hated_date, 4) + LEFT(#hated_date, 4)

Another approach is this:
MONTH(#normal_date)*1000000 + DAY(#normal_date)*10000 + YEAR(#normal_date)
=
CAST(#hated_date AS INT)
one more thing: it is more precise to compare real execution costs than to rely on the optimizer's estimates.

Try this:
select cast(stuff(stuff('11011974', 3,0, '/'),6,0,'/') as datetime)
Update

Suggest you either fix the column to be datetime or add a datetime column to the table and convert the data so that you only have to do this conversion once when the data is entered (and once of course for existing data) This could probaly even be a calculated column. This is NOT something you want to be doing in select statements. If necessary create a dateconversion table with every opossible date in both formates and join to it if the table can't be changed.
You might also want to check to make sure there are no invalid dates in there which is always a possibility with storing dates in a data type other than a datetime one.

Related

How to split dash-separated values in SQL Server

I have a date saved in an nvarchar type and I want to split the day, month and year into separate nvarchar variables (that means three variables). The date looks as follows: exposure_date ='2018-12-04' and the format is yyyy-dd-mm
any help please?
My whole project is stuck on this.
The "correct" answer here is to fix your datatype. When storing data always choose an appropriate data type for the data you're storing. For a date (with no time part) then the correct datatype is date. if you're storing numerical data, then use a numerical datatype, such as int or decimal. (n)varchar is not a one size fits all datatype and using it to store data that has a data type designed for it is almost always a bad choice. I'm storing the data as an (n)varchar because I need it in a specific format is never an excuse; have your presentation layer handle to display format, not your RDBMS.
The first step, therefore would be to change your string representation yyyy-dd-MM of a date to the ISO format yyyyMMdd by doing:
UPDATE YourTable
SET exposure_date = LEFT(exposure_date,4) + RIGHT(exposure_date,2) + SUBSTRING(exposure_date,6,2);
Now you have a unambiguous representation, you can change the data type of your column without concerns of incorrect implicit casts or error:
ALTER YourTable ALTER COLUMN exposure_date date;
Then, finally, you can treat your data as what it is, a date, and use the DATEPART function:
SELECT DATEPART(YEAR,exposure_date) AS Exposure_Year,
DATEPART(MONTH,exposure_date) AS Exposure_Month,
DATEPART(DAY,exposure_date) AS Exposure_Day
FROM YourTable;
You can also try the following
Declare #myDate date
select #myDate= Cast(substring('2011-29-12', 1, 4)
+ '-' + substring('2011-29-12', 9, 2)
+ '-' + substring('2011-29-12', 6, 2)
as Date) --YYYY-MM-DD
Select #myDate as DateTime,
datename(day,#myDate) as Date,
month(#myDate) as Month,
datename(year,#myDate) as Year,
Datename(weekday,#myDate) as DayName
The output is as shown below
DateTime Date Month Year DayName
--------------------------------------------
2011-29-12 29 12 2011 Thursday
You can find the live demo here
You can try below -
select concat(cast(year(cast('2018-12-04' as date)) as varchar(4)),'-',
cast(month(cast('2018-12-04' as date)) as varchar(2)), '-',
cast(day(cast('2018-12-04' as date)) as varchar(2)))
from tablename
If you have fixed format, then you could use this simple query with substring method:
select substring(dt, 1, 4) + '-' +
substring(dt, 9, 2) + '-' +
substring(dt, 6, 2) [YYYY-MM-DD]
from (values ('2018-31-12')) tbl(dt)
Let's go directly to the main issue, which is you are using the wrong datatype to store dates, you should store them as DATE, the datatypes are there for a reason and you need to choose a proper one for your column.
So, you need to ALTER your table and change the column datatype to DATE instead of NVARCHAR datatype.
ALTER <Table Name Here>
ALTER COLUMN <Column Name Here> DATE;
Then all things will easy, you just run the following query to get the desired output
SELECT YEAR(<Column Name Here>) TheYear,
MONTH(<Column Name Here>) TheMonth,
DAY(<Column Name Here>) TheDay
FROM <Table Name Here>
Which is the right and the best solution.
You can also (if you are not going to alter your table) do as
CREATE TABLE Dates(
StrDate NVARCHAR(10)
);
INSERT INTO Dates VALUES
(N'2018-12-04'),
(N'Invalid');
SELECT LEFT(StrDate, 4) StrYear,
SUBSTRING(StrDate, 6, 2) StrMonth,
RIGHT(StrDate, 2) StrDay
FROM Dates;
OR
SELECT YEAR(StrDate) StrYear,
MONTH(StrDate) StrMonth,
DAY(StrDate) StrDay
FROM (
SELECT TRY_CAST(StrDate AS DATE) StrDate
FROM Dates
)T

BigInt won't convert to proper date format

So I have a query I'm trying to write where there are two columns that will have variable results. One is date and one is time. My query will look like
Select Schedule ID , Job_Name , next_run_date , next_run_time
The values will vary depending on what database I'm running against. For example, [next_run_date] might = 20181014 and [next_run_time] might read 1000 which would be 1am. But if I run it on a different server, it could have a completely different set of values, but just the same format.
I've unsuccessfully tried to convert the columns to date/time format by using
CONVERT(varchar(10),CONVERT(date,[next_run_date],110),110) AS 'Next Run'
And just get 'Explicit conversion from data type int to date is not allowed'
What I'd like it to display is [next_run_date] might = 10-14-2018 and [next_run_time] = 01:00. Just unsure how to convert this correctly. I do not have to write privs to the database. If I read correctly, at least for the date column, I would have to convert from Bigin to Varchar to ToDate, but unclear how to fully write that.
For the time field you can stuff a : in it.
And a FORMAT for the times below 10 AM.
And the date part can be done with 2 casts and a CONVERT or a FORMAT.
The date from an INT to a VARCHAR in the 'mm-dd-yyyy' format:
CONVERT(VARCHAR(10), CAST(CAST([next_run_date] AS VARCHAR(8)) AS DATE), 110)
The time from an INT to a VARCHAR in the 'hh:mi' format:
STUFF(CAST(FORMAT([next_run_time],'000000') AS VARCHAR(4)),3,0,':')
Example snippet:
DECLARE #Table TABLE (next_run_date INT, next_run_time INT);
INSERT INTO #Table (next_run_date, next_run_time) VALUES
(20180901, 13500)
,(20181015, 134200)
;
SELECT
CONVERT(VARCHAR(10), CAST(CAST([next_run_date] AS VARCHAR(8)) AS DATE), 110) AS [Next Run Date],
STUFF(CAST(FORMAT([next_run_time],'000000') AS VARCHAR(4)),3,0,':') AS [Next Run Time]
FROM #Table
Returns:
Next Run Date Next Run Time
------------- -------------
09-01-2018 01:35
10-15-2018 13:42
You need to convert the bigint to a varchar first, then to a date:
CONVERT(date,CONVERT(varchar(10),[next_run_date]),110) AS 'Next Run'
You could also break up the number into parts and craft a date and time.
DECLARE #Date INT=20181014
DECLARE #Time INT=123456
SELECT CONVERT(DATE,
SUBSTRING(CONVERT(VARCHAR(20),#Date),1,4)+'/'+
SUBSTRING(CONVERT(VARCHAR(20),#Date),5,2)+'/'+
SUBSTRING(CONVERT(VARCHAR(20),#Date),7,2)
) AS [Date]
SELECT CONVERT(TIME,
SUBSTRING(CONVERT(VARCHAR(20),#Time),1,LEN(CONVERT(VARCHAR(20),#Time))-4)+':'+
SUBSTRING(CONVERT(VARCHAR(20),#Time),LEN(CONVERT(VARCHAR(20),#Time))-3,2)+':'+
SUBSTRING(CONVERT(VARCHAR(20),#Time),LEN(CONVERT(VARCHAR(20),#Time))-1,2)
) AS [Time]
select convert(datetime, cast(20181014 as varchar), 102)
Note :
CAST is part of the ANSI-SQL specification; whereas, CONVERT is not.
In fact, CONVERT is SQL implementation specific. CONVERT differences
lie in that it accepts an optional style parameter which is used for
formatting.

date time stored as varchar in sql how to filter on varchar

I am working on a project in which dates and times ar stored as a varchar e.g. "30-11-2017,7:30" first date in dd-mm-yyy format and then time separated with a comma. I am trying to filter on it but it is not working correctly kindly guide me how to filter data on date.
select *
from timetrack
where startDateAndTime >= '30-11-2017,7:30'
In attached image records have been shown. When I apply above query it shows no records
You can easily convert your date to SQL datatype datetime uisng parse function, for example select parse('30-11-2017,7:30' as datetime using 'it-IT').
So, in your case, you can apply this function in where clause, so you can easily apply comparison between dates:
select *
from timetrack
where parse(startDateAndTime as datetime using 'it-IT') >= '2017-11-30 07:30:00.000'
Your format is apparently italian :) But you have to specify your own date in the format convertable to datetime, as I have done in above example.
NOTE: parse is available starting with SQL Management Studio 2012.
Unless you are using ISO date format (yyyy-MM-dd HH:mm:ss or close) applying ordering (which inequalities like greater than or equal use) will not work: the date order is disconnected from the string ordering.
You'll need to parse the date and times into a real date time type and then compare to that (details of this depend on which RDBMS you are using).
If, you want to just filter out the date then you could use convert() function for SQL Server
select *
from timetrack
where startDateAndTime >= convert(date, left(#date, 10), 103)
Else convert it to datetime as follow
select *
from timetrack
where startDateAndTime >= convert(datetime, left(#date, 10)+' ' +
reverse(left(reverse(#date), charindex(',', reverse(#date))-1)), 103)
You need the date in a datetime column, Otherwise you can't filter with your current varchar format of your date.
Without changing the existing columns, this can be achieved by making a computed column and making it persisted to optimize performance.
ALTER TABLE test add CstartDateTime
as convert(datetime, substring(startDateAndTime, 7,4)+ substring(startDateAndTime, 4,2)
+ left(startDateAndTime, 2) +' '+ right(startDateAndTime, 5), 112) persisted
Note: this require all rows in the column contains a valid date with the current format
Firstly, you need to check what is the data that is entered in the 'startDateAndTime' column,then you can convert that varchar into date format
If the data in 'startDateAndTime' column has data like '30-11-2017,07:30', you would then have to convert it into date:
SELECT to_date('30-11-2017,07:30','dd-mm-yyyy,hh:mm') from dual; --check this
--Your query:
SELECT to_date(startDateAndTime ,'dd-mm-yyyy,hh:mm') from timetrack;

Char to DateTime Conversion

I have one column capturedatetime(Char(30)):
2006-04-25T15:50:59.997000 PM
And I want to convert it and load it at other table column which have is in DateTime. either by T-sql or SSIS which ever way.
I have tried with:
select CONVERT(datetime, '2006-04-25T15:50:59.997000 PM', 126)
But it creates an error:
Conversion failed when converting date and/or time from character string
Late update:
In this column I also have other data that is in a completely different format:
29-JAN-10 08.57.41.000000 PM
(1) STOP storing datetime data in string columns! This is nothing, nothing, nothing but trouble.
(2) Why on earth does your column get data in two different string formats that aren't even valid? Why does the string use 24 hour time and have AM/PM suffix? Why use a regional string format and Y2K disaster like 29-JAN-10?
Here is one way, but it's awfully ugly. I highly recommend you fix the SSIS process to give you valid datetime values in the first place, if not as datetimes, at least as valid ISO strings (yyyy-mm-ddThh:mm:ss.nnn):
DECLARE #x TABLE (d CHAR(30));
INSERT #x SELECT '2006-04-25T15:50:59.997000 PM'
UNION ALL SELECT '29-JAN-10 08.57.41.000000 PM';
SET LANGUAGE ENGLISH; -- this is important, else style 6 may not work
SELECT
CASE WHEN d LIKE '__[0-9]%' THEN
CONVERT(DATETIME, LEFT(d, 23))
WHEN d LIKE '[0-9][0-9]-%' THEN
CONVERT(DATETIME, CONVERT(CHAR(8),
CONVERT(DATETIME,REPLACE(LEFT(d,9),' ','-'),6),112)
+ ' ' + REPLACE(SUBSTRING(d,11,8),'.',':')
+ ' ' + RIGHT(RTRIM(d),2))
END
FROM #x;
The conversion for 126 requires no spaces ... I've got it to work like this:
declare #T varchar(50)
declare #dt datetime
set #T = '2006-04-25T15:50:59.997'
set #dt = convert(datetime,#t,126)
select #T, #dt
select convert(datetime,left('2006-04-25T15:50:59.997000 PM',23))
or
select convert(datetime,left(capturedatetime,23))
If you use cast, you do not even need to supply a format. Code snippet below tested on SQL 2012 Developer version.
declare #var_string varchar(50) = '2006-04-25T15:50:59.997';
declare #var_datetime datetime = cast(#var_string as datetime);
select #var_string as my_string, #var_datetime as my_variable;

T-SQL 2008 Convert Date and time string to datetime

I have two columns in my table, one to capture time and one to capture date. Unfortunately, both are varchar(). I need to take the two fields, concatenate them together, and then convert them to datetime.
I am trying to accomplish that with this:
select CONVERT(datetime,(select txt_returned_date+' '+CONVERT(varchar(20),CONVERT(TIME,txt_time_returned))),126)
from table_name
I am getting this error message:
Conversion failed when converting date and/or time from character string.
The date is being captured as "20130308" as a string. Time is being captures as "4:27 PM" as a string
What I am doing here is converting the string of the time to TIME, then back to varchar. Then I am concatenating them together. This works by itself, but once I introduce the CONVERT(datetime) to the whole query, it is giving me the error.
Any help to try to accomplish this is helpful. Thanks!
You can concatenate the DATE and TIME values together once they have been converted to a DATETIME. Here's a sample to play with that shows concatenating a DATE column and a TIME column that have been stored as VARCHAR:
-- Set up some variables to test with
DECLARE #myTime TIME = GETDATE()
, #myDate DATE = GETDATE()
, #myTimeTxt VARCHAR(16)
, #myDateTxt VARCHAR(10);
-- Initialize your variables
SELECT #myTimeTxt = #myTime
, #myDateTxt = #myDate;
-- Display your separated values
SELECT #myDateTxt, #myTimeTxt;
-- Display your concatenated value
SELECT CAST(#myDateTxt AS DATETIME) + CAST(CAST(#myTimeTxt AS TIME) AS DATETIME);
You can use this option
DECLARE #date date = '20010101',
#time time = '01:01:01'
SELECT CAST(#date AS datetime) + #time
Result:2001-01-01 01:01:01.000
Demo on SQLFiddle
Are you using SQL 2012? If so you may be able to use the datetimedromparts function to achieve this. If not for this specific example, it's always good to know for the future :)
http://technet.microsoft.com/en-gb/library/hh213233.aspx