CONVERT fails but TRY_CONVERT successfully convert same data - sql-server

I have a column with dates stored as strings (NVARCHAR(500)), in dd/MM/yyyy format.
This query gets all rows with a non null value in my date_column, 13955 rows returned, 18359 total rows on table.
SELECT ID, date_column
FROM [MyTable] WITH(NOLOCK)
WHERE
client = 178425
AND folder = 1422720
AND date_column IS NOT NULL
Filtering for rows that successfully converted the string to a date value using TRY_CONVERT. Returned all 13955 rows, indicating all of them are valid dates.
SELECT ID, date_column
FROM [MyTable] WITH(NOLOCK)
WHERE
client = 178425
AND folder = 1422720
AND date_column IS NOT NULL
AND TRY_CONVERT(DATE, date_column, 103) IS NOT NULL
Now converting the column to a date in the select statement, again no errors and all 13955 rows returned.
SELECT ID, CONVERT(DATE, date_column, 103)
FROM [MyTable] WITH(NOLOCK)
WHERE
client = 178425
AND folder = 1422720
AND date_column IS NOT NULL
AND TRY_CONVERT(DATE, date_column, 103) IS NOT NULL
So I assumed all NON-NULL values are valid dates, right?
But then if I remove the TRY_CONVERT from the where statement, I receive an error:
Conversion failed when converting date and/or time from character string.
SELECT ID, CONVERT(DATE, date_column, 103)
FROM [MyTable] WITH(NOLOCK)
WHERE
client = 178425
AND folder = 1422720
AND date_column IS NOT NULL
How can this be? ALL Non NULL rows are valid dates but only if I use TRY_CONVERT function there?
Any ideas on how to find the faulty values in my table?
PS. Not providing sample data because all tests I did out of this specific table worked as expected. I have no Idea what is causing this problem...

Related

In SQL Server, use a regex in SQL statement to extract a subset of a column

I have a table that has result date data in two different columns. The desired outcome is to output a single column with the date that is either extracted from the Result column or is the date from the ResultDate column.
Result
ResultDate
Test
5/1/2017
Test
9/12/2014
Test, completed 8/4/2020
Test, 5/5/2011
My first thought was to do something like this...
select coalesce(
regex('\d{1,2}\/\d{1,2}\/\d{2,4}',Result),
ResultDate
) as ResultDate
from Table
...but I can't find any info on using regex like this. All the posts talk about using regular expressions in the where clause, not in a column specification.
You could attempt to redefine the problem without a regular expression.
Assumption for sample data: dates are to be interpreted as dd/mm/yyyy.
Assumption for proposed solution: Result never contains two dates (would require extra logic).
Split the Result column on spaces.
cross apply string_split(d.Result, ' ') rs
Look for "words" where a conversion attempt to the desired format is successful.
try_convert(date, rs.value, 103) is not null
If SQL Server does unwanted date conversions, then you could add further restrictions along the line of your regular expression. If you really want you could add another string_split with slashes on the resulting value from the first split and evaluate the parts separately...
where len(rs.value) - len(replace(rs.value, '/', '')) = 2 --> two slashes
where try_convert(int, replace(rs.value, '/', '')) is not null --> only digits and slashes
where len(rs.value) >= 6 and len(rs.value) <= 10 --> length between 6 and 10
Sample data
create table data
(
Result nvarchar(100),
ResultDate date
);
insert into data (Result, ResultDate) values
('Test', '2017-01-05'),
('Test', '2014-12-09'),
('Test, completed 8/4/2020', null),
('Test, 5/5/2011', null);
Solution
Base solution:
select d.Result,
coalesce(d.ResultDate, convert(date, rs.value, 103)) as ResultDate
from data d
cross apply string_split(d.Result, ' ') rs -- result split
where coalesce(d.ResultDate, try_convert(date, rs.value, 103)) is not null;
Extended solution:
select d.Result,
coalesce(d.ResultDate, convert(date, rs.value, 103)) as ResultDate,
convert(nvarchar(10), coalesce(d.ResultDate, convert(date, rs.value, 103)), 103) as ResultDate103
from data d
cross apply string_split(d.Result, ' ') rs -- result split
where d.ResultDate is not null
or (try_convert(date, rs.value, 103) is not null
and len(rs.value) - len(replace(rs.value, '/', '')) = 2 -- date string must contain 2 slashes
and try_convert(int, replace(rs.value, '/', '')) is not null -- only digits and slashes
and len(rs.value) >= 6 and len(rs.value) <= 10); -- date string length filter (d/m/yy --> dd/mm/yyyy)
Result
ResultDate103 is only included for the "Extended solution".
Result ResultDate ResultDate103
------------------------ ---------- -------------
Test 2017-01-05 05/01/2017
Test 2014-12-09 09/12/2014
Test, completed 8/4/2020 2020-04-08 08/04/2020
Test, 5/5/2011 2011-05-05 05/05/2011
Fiddle to see things in action with intermediate steps.
Well, after more digging I found my answer...patindex.
select
replace(cast(coalesce(
iif(patindex('%[0-9][0-9]/[0-9][0-9]/[0-9][0-9]%', [Result])<>0
,substring([result],patindex('%[0-9][0-9]/[0-9][0-9]/[0-9][0-9]%', [Result]),10)
,null)
,[Satisfied Date]) as date),'-','') as resultDate
from Table
It is a little clunky, but it does exactly what I want it to.

Compare a date with specific date format

I have column of type varchar in a table and have different type of date format in it. I want to compare it with specific format of date. If it is true then those rows should be returned only. Format of the date is 'yyyymmdd'
e.g., 20200831 = 'yyyymmdd'
dates are in below format
20200831
31/Aug/2020
08-31-2020
2020-08-31
These are few date format which are present in the table.
If I understand the question correctly, a possible solution is the following statement:
SELECT DateText
FROM (VALUES
('20200831'),
('31/Aug/2020'),
('08-31-2020'),
('2020-08-31')
) v (DateText)
WHERE CONVERT(varchar(8), TRY_CONVERT(date, DateText), 112) = DateText
Result:
DateText
20200831
SELECT *
FROM tableName
WHERE ISDATE(dateCol) = 1 AND ISNUMERIC(dateCol) = 1;
ISDATE() function returns 1 if column value is valid SQL Server date
and ISNUMERIC() returns 1 if column value is valid number, so 20200831 satisfies both conditiion.
If you are using SQL Server 2012 or later, then the TRY_CONVERT function is one option here:
SELECT
dt,
TRY_CONVERT(datetime, dt) AS dt_out
FROM yourTable
WHERE
TRY_CONVERT(datetime, dt) = '2020-08-31';
Demo
Data:
WITH yourTable AS (
SELECT '20200831' AS dt UNION ALL
SELECT '31/Aug/2020' UNION ALL
SELECT '08-31-2020' UNION ALL
SELECT '2020-08-31'
)

Why it is returning the date which is not between the range?

I want to only display the data in between 15 to 23 May 2019. But why it is also returning the data which is not between the date range?
Below is my query
SELECT Appointment_ID,
WO_DespatchName, Despatch_Name AS WO_selectDespatchName,
FORMAT(Appointment_DateTime, 'dd/MMM/yyyy hh:mm tt') AS Appointment_DateTime,
FORMAT(Appointment_ProposalSent, 'dd/MMM/yyyy') AS Appointment_ProposalSent,
WO_MaidName, Maid_Name AS WO_selectMaidName,
Appointment_Location, Appointment_FaceToFace, Appointment_Service, Appointment_Remarks, WO_Duration,
Appointment_ContactID, Contact_Name AS Appointment_selectContactID
FROM Appointments A
LEFT JOIN Despatch ON Despatch_ID = WO_DespatchName
LEFT JOIN CustomerDetails ON Contact_ID = Appointment_ContactID
LEFT JOIN Maid ON Maid_ID = WO_MaidName
WHERE FORMAT(Appointment_DateTime, 'dd/MM/yyyy') BETWEEN '15/05/2019' AND '23/05/2019'
AND (A.IsDelete = 0 OR A.IsDelete IS NULL)
ORDER BY CONVERT(DateTime, Appointment_DateTime,101) ASC
The output that I get is this https://drive.google.com/file/d/1VnrSET1V1eoFvEUUtRwXtQmKGWamURgV/view?usp=sharing
You are comparing texts, not dates. So it will return any row where date converted to string starts between 15 and 23.
You need to use date datatype in your comparison:
WHERE Appointment_DateTime >= CAST('20190515' AS date) AND Appointment_DateTime < CAST('20190524' AS date)
Please notice I added one day to the end range, so 05/23/2019 11:59:59.9... PM still will be accepted. CAST as date is not necessary, but improves readability.
The problem is in your where clause. Since your Appointment_DateTime is in datetime format. I am expecting that this column has value type datetime.
Now come to your query, '23/05/2019' is simple string, so unable to compare this as datetime. First you need to convert your from and to date into datetime format.
Please try this (if only date matching is your concern):
SELECT Appointment_ID,
WO_DespatchName, Despatch_Name AS WO_selectDespatchName,
FORMAT(Appointment_DateTime, 'dd/MMM/yyyy hh:mm tt') AS Appointment_DateTime,
FORMAT(Appointment_ProposalSent, 'dd/MMM/yyyy') AS Appointment_ProposalSent,
WO_MaidName, Maid_Name AS WO_selectMaidName,
Appointment_Location, Appointment_FaceToFace, Appointment_Service, Appointment_Remarks, WO_Duration,
Appointment_ContactID, Contact_Name AS Appointment_selectContactID
FROM Appointments A
LEFT JOIN Despatch ON Despatch_ID = WO_DespatchName
LEFT JOIN CustomerDetails ON Contact_ID = Appointment_ContactID
LEFT JOIN Maid ON Maid_ID = WO_MaidName
WHERE FORMAT(Appointment_DateTime, 'dd/MM/yyyy') BETWEEN FORMAT('15/05/2019', 'dd/MM/yyyy') AND FORMAT('23/05/2019', 'dd/MM/yyyy')
AND (A.IsDelete = 0 OR A.IsDelete IS NULL)
ORDER BY CONVERT(DateTime, Appointment_DateTime,101) ASC
Or you may try convert to convert your string into datetime format.
Feel free to ask for any further confusion.

Convert INT to DATE in WHERE clause - TSQL

Working with sysjobs and sysjobhistory on SQL Server and I have been googling around but I can't seem to find a direct answer to this question. I think it is because I'm trying to do this in a WHERE clause.
So basically trying to select a job that has failed on the same day that it ran (basically today's date), since the current code is selecting all of the existing jobs that failed.
Below is my code:
SELECT DISTINCT
sj.name,
COUNT(sjh.instance_id) AS errors
FROM
msdb..sysjobhistory sjh
JOIN
msdb.dbo.sysjobs sj ON sj.job_id = sjh.job_id
WHERE
name IN ('SQLAgentJob1', 'SQLAgentJob2')
AND sjh.run_status = 1
AND (SELECT CONVERT(DATE, CONVERT(INT, sjh.run_date), 112)) = (SELECT CONVERT(DATE, GETDATE(), 112))
GROUP BY
sj.name
I keep getting an error stating:
Msg 529, Level 16, State 2, Line 13
Explicit conversion from data type int to date is not allowed.
Any ideas?
You don't have to do all those conversions, just convert your parameter (ie the current date) to the unseparated date format with 112:
SELECT DISTINCT sj.name, COUNT(sjh.instance_id) AS errors
FROM msdb..sysjobhistory sjh
JOIN msdb.dbo.sysjobs sj ON sj.job_id = sjh.job_id
WHERE name IN ('SQLAgentJob1','SQLAgentJob2')
AND sjh.run_status = 1
AND sjh.run_date= CONVERT(nvarchar(8), GETDATE(), 112)
GROUP BY sj.name
There is no integer to date conversion because there is no integer representation for dates.
The run_date column stores the YYYYMMDD format as an integer, perhaps to reduce space consumption (4 bytes instead of 8), perhaps for some other reason.
To compare against it, just create the equivalent string with CONVERT(nvarchar(8), GETDATE(), 112). The conversion from string to integer is implicit
This should work.
SELECT DISTINCT sj.name,
COUNT(sjh.instance_id) AS errors
FROM msdb..sysjobhistory sjh
JOIN msdb.dbo.sysjobs sj ON sj.job_id = sjh.job_id
WHERE name IN ('SQLAgentJob1',
'SQLAgentJob2')
AND sjh.run_status = 1
AND CAST(CONVERT(datetime, CAST(SJH.[run_date] AS CHAR(8))) as DATE) = CAST(GETDATE() as DATE)
GROUP BY sj.name
Explanation
First convert from int to datetime
Cast down from datetime to date (drop the time)
Compare against today's date (with the time dropped)

Return (Cast? Convert?) values as DateTIme from a Distinct year, month query in a store procedure

I have database with a Publications table that is many-to-may joined to iself through a SubPublications table
My stored procedure returns all of the distinct Year-Month combos from a ReleaseDate field of Publications of a specified type that are not related to a specific (by id) publication (hence the 2 params, see below).
QUESTION:
The proc works fine, but I want the return column type as DateTime2 with a dummy date of 1. As it is now, it returns 2 columns of integers. How do I do this?
I know I could do the conversion in my app code, but I'd rather have it delivered as a datetime from the DB.
My SQL ain't great. I don't even know if I should use a cast or a convert.
I can't find an example online of converting back to datetime within a query like that. Can anyone help? Here's the proc I wrote, as it stands:
ALTER PROCEDURE sp_DistinctPubMonthYears
#PubType char(1),
#PubId int = 0
AS
BEGIN
SELECT
DISTINCT TOP (100) PERCENT
DATEPART(month, ReleaseDate) AS month,
DATEPART(year, ReleaseDate) AS year
FROM(
SELECT
Publications.ReleaseDate AS ReleaseDate,
Publications.PublicationId As PubId,
Publications.PubType AS PubType,
SubPublications.PublicationId AS ParentId
FROM
Publications LEFT JOIN SubPublications
ON
Publications.PublicationId = SubPublications.PublicationId
WHERE
Publications.PubType = #PubType AND
Publications.PublicationId <> #PubId AND
(
SubPublications.PublicationId <> #PubId OR
/*either it's parent is NOT the one we're searching on or */
SubPublications.PublicationId IS NULL
/*or it's not joined to anything at all */
)
) AS sub
ORDER BY year ASC, month ASC
END
GO
You don't need TOP and you may as well ORDER BY the expression.
This DATEADD/DATEDIFF expression will give you start of current month
SELECT DISTINCT
CAST(
DATEADD(month, DATEDIFF(month, 0, ReleaseDate), 0) AS datetime2
) AS myCol
FROM(
...
ORDER BY
DATEADD(month, DATEDIFF(month, 0, ReleaseDate), 0)
Edit: As Faust mentioned, we can order on the alias if you prefer.
...
ORDER BY
myCol
In this case the result is the same.
If the CAST was to varchar then you would have different results. This is why I tend to use the expression not the alias but it's quite trivial. Surely I'd test my changes..., no?
DATEADD(MONTH, DATEDIFF(MONTH, '1600-01-01T00:00:00', ReleaseDate), '1600-01-01T00:00:00') should get you your yyyy-MM-dd 00:00:00 date. 1600-01-01T00:00:00 is just an arbitrary date that is best picked to be prior to any dates you may be storing in your ReleaseDate column.

Resources