Validate Date Values in NVarchar field - sql-server

Hi I have a reporting application written against some 3rd party software. Unfortunately it stores all values as nvarchar and does not validate data entry on the client side as a result I am getting the following error when
"Conversion failed when converting date and/or time from character string"
System.Data.SqlClient.SqlException was unhandled by user code
or if I try to execute the code in SSMS:
Msg 241, Level 16, State 1, Procedure settlement_list, Line 10
Conversion failed when converting date and/or time from character string.
I assume this is the result of someone entering a text value in the data field so I've tried this ISDATE code to find the bad value:
SELECT mat3_02_01, CONVERT(datetime, mat3_04_02), mat3_04_02 FROM lntmu11.matter3
WHERE ISDATE(mat3_04_02) <> 1
AND Coalesce(mat3_04_02, '') <> ''
order by mat3_04_02 desc
and I get zero row returned ... I also manually sifted through the data (its sveral 100 thousand rows so its kind of hard and see no bad values ???
Does anyone have any suggestions ?
EDIT ---
Here is the stored proc (I know where clause is ugly)
SELECT mat_no, 'index'=matter.mat1_01_06,
'insurance'=Replace(Replace(matter.mat1_03_01, 'INSURANCE COMPANY', ' '), 'COMPANY', ''),
matter.[status], 'casestage'=mat1_04_01, 'injured'=matter.MAT1_01_07, matter.client,
'terms'=mat3_04_06, 'ClmAmt'=matter.mat1_07_01,
'ClmBal'=matter.mat1_07_03, 'SetTot'=matter3.MAT3_04_09, 'By'=mat3_03_02,
'DtSttld'=mat3_04_02, 'SettlStg'=(MAT3_06_08 + ' / ' + MAT3_06_05)
FROM [lntmu11].matter3 inner join
[lntmu11].matter ON [lntmu11].matter.sysid = [lntmu11].matter3.sysid
WHERE
(DateDiff(month, convert(datetime, MAT3_04_02, 101), GETDATE()) = #range
and mat3_03_02 like #by)
or
(mat3_04_06 like #by2
and DateDiff(month, convert(datetime, MAT3_04_02, 101), GETDATE()) = #range)
ORDER BY MAT3_03_02

You can't force the order the query engine will try to process the statement without first dumping the ISDATE() = 1 rows into a #temp table. You can't guarantee the processing order or short circuiting, even though some will suggest using a CTE or subquery to filter out the bad rows first. So some might suggest:
;WITH x AS
(
SELECT mat3_02_01, mat3_04_02
FROM Intmu11.matter3
WHERE ISDATE(mat3_04_02) = 1
AND mat3_04_02 IS NOT NULL -- edited!
)
SELECT mat3_02_01, CONVERT(DATETIME, mat3_04_02), mat3_04_02
FROM x
ORDER BY mat3_04_02 DESC;
And this may even appear to work, today. But in the long term, really the only way to guarantee this processing order - in current versions of SQL Server - is:
SELECT mat3_02_01, mat3_04_02
INTO #x
FROM Intmu11.matter3
WHERE ISDATE(mat3_04_02) = 1
AND mat3_04_02 IS NOT NULL; -- edited!
SELECT mat3_02_01, CONVERT(DATETIME, mat3_04_02), mat3_04_02
FROM #x
ORDER BY mat3_04_02 DESC;
Have you thought about validating the values on input? For example, you can change where this error appears in the application by slapping them on the wrist when they enter an invalid date, instead of punishing the person who selects their bad data. If you are controlling the update/insert via a stored procedure, you can say:
IF ISDATE(#mat3_04_02) = 0
BEGIN
RAISERROR('Please enter a valid date.', 11, 1);
RETURN;
END
If you aren't controlling data manipulation via stored procedure(s), then you can add a check constraint to the table (after you've cleaned up the existing bad data).
UPDATE Intmu11.matter3 SET mat3_04_02 = NULL
WHERE ISDATE(mat3_04_02) = 0;
ALTER TABLE Intmu11 WITH NOCHECK
ADD CONSTRAINT mat3_04_02_valid_date CHECK (ISDATE(mat3_04_02)=1);
This way when the error message gets bubbled up to the user they will see the constraint name and hopefully will be able to map that to the data entry point on the front end that failed:
Msg 547, Level 16, State 0, Line 1 The INSERT statement conflicted
with the CHECK constraint "mat3_04_02_valid_date". The conflict
occurred in database "your_db", table "Intmu11.matter3", column
'mat3_04_02'. The statement has been terminated.
Or better yet, use the right data type in the first place! Again, after updating the existing bad data to be NULL, you can say:
ALTER TABLE Intmu11.matter3 ALTER COLUMN mat3_04_02 DATETIME;
Now when someone tries to enter a non-date, they'll get the same error that the users are currently getting when they try to select the bad data:
Msg 241, Level 16, State 1, Line 1 Conversion failed when
converting date and/or time from character string.
In SQL Server 2012, you'll be able to get around this with TRY_CONVERT() but you should still be trying to get the data type right from the beginning.

Examine the query where
ISDATE(mat3_04_02) = 1
AND
Coalesce(mat3_04_02, '') = ''
To be a date it must have a value.
But is only matches the second condition if it has not value.
The intersection (and) of those two conditions is always false.
If you are looking for null then "mat3_04_02 is null" but it still will return 0 rows.
Try
SELECT mat3_02_01, CONVERT(datetime, mat3_04_02), mat3_04_02
FROM lntmu11.matter3
WHERE ISDATE(mat3_04_02) = 1
order by CONVERT(datetime, mat3_04_02) desc
I think you would want date sorted and not string sorted
The question started as finding valid dates and it morphed into finding invalid dates
SELECT mat3_02_01, mat3_04_02
FROM lntmu11.matter3
WHERE ISDATE(mat3_04_02) = 0
AND mat3_04_02 is not null
order by mat3_04_02) desc

Related

Error in if not exists query in SQL Server

What is wrong with this following query? I can't find the error. Can anyone help
me with this issue?
IF (NOT EXISTS(SELECT *
FROM chennai_metro_data
WHERE TIME1 ='09:00' AND DATE1 ='1-23-2017'))
BEGIN
INSERT INTO chennai_metro_data
VALUES (2021700002,'1-23-2017','09:00',1,0,555555)
END
ELSE
BEGIN
UPDATE chennai_metro_data
SET CUMFLOW = 555555
WHERE TIME1 = '09:00' AND DATE1 = '1-23-2017'
END
I'm getting this error:
Msg 206, Level 16, State 2, Line 1
Operand type clash: int is incompatible with date
As a best practice, you should always define the list of columns you're inserting into when using INSERT - that helps avoid a lot of problems !
And also: for the dates, to be independent of any language & regional settings, try to use the ISO-8601 format - YYYYMDDD for just dates (no time), or YYYY-MM-DDTHH:MM:SS for date & time.
So try this code:
INSERT INTO chennai_metro_data(col1, col2, ...., colN)
VALUES (2021700002, '20170123', '09:00', 1, 0, 555555)
and replace col1 thorugh colN with your actual column names from that table that you want to insert data into.

System.Data.SqlClient.SqlException: String or binary data would be truncated

I am creating a web app in which I am executing a select command on my stored procedure, but I want to insert the same fetched data into another table.
So I tried to do something like the following
CREATE PROCEDURE profinalinstexpensesonid
(#from varchar(5000),
#to varchar(5000),
#trainer varchar(5000),
#sonvinid varchar(5000)
)
AS
BEGIN
INSERT INTO invoice(sonvinid, tid, date, brandname, zone, location, area, venuename, venue, instructore, amount)
SELECT
instructoreexpense.sonvinid,
sonvininsert.trainer,
CONVERT(VARCHAR, sonvininsert.date, 105) AS date,
sonvininsert.brandname,
SUBSTRING(sonvininsert.zone, 1, 1) AS zone,
sonvininsert.location,
sonvininsert.area,
companysonvinunitvenue.venuename,
sonvininsert.venue,
sonvininsert.instructore,
instructoreexpense.amount
FROM
instructoreexpense
LEFT OUTER JOIN
sonvininsert ON sonvininsert.sonvinid = instructoreexpense.sonvinid
AND sonvininsert.status = '0'
LEFT OUTER JOIN
finalinstructoreexpense ON finalinstructoreexpense.sonvinid = instructoreexpense.sonvinid
LEFT OUTER JOIN
companysonvinunitvenue ON companysonvinunitvenue.id = sonvininsert.comsonvinid
WHERE
sonvininsert.date BETWEEN CONVERT(DATETIME, #from, 105)
AND CONVERT(DATETIME, #to, 105)
AND sonvininsert.trainer = (SELECT empname
FROM trainerdetails
WHERE trid = #trainer)
AND instructoreexpense.sonvinid NOT IN (SELECT CAST(Item AS INTEGER)
FROM SplitString(#sonvinid, ','))
ORDER BY
instructoreexpense.sonvinid
END
and when I execute the stored procedure like
exec profinalinstexpensesonid '01-01-2013','01-01-2017','andrews'
I am getting the following error
Msg 8152, Level 16, State 13, Procedure profinalinstexpensesonid, Line 10
String or binary data would be truncated.
On my line 10 I have the following code
insert into invoice(sonvinid, tid, date, brandname, zone, location, area, venuename, venue, instructore, amount)
I don't know what is wrong here?
The error message states the size of a column in invoice table is less compared to the size of the data being inserted into it.
For example if column brandname has data type varchar(50) and you are trying to insert more than 50 characters then it will cause error.
To resolve this compare the size of columns in invoice with the size of the columns being inserted.
You need to check column size of invoice table as well as columns in select list from which you are populating data.
Let's say you are inserting column "B" having data type as varchar(70) from table2 in column "A" having data type varchar(50) in table1; this won't work as you are trying to insert 70 characters in 50 varchar sized column.
Check source & destination column data type & it's length; and change it and try again.

how to retrieve the records based on the GETDATE

I am trying to automate the task using SQL server agent to generate the report of the people who gets inserted to the table on regular basis. I have created the below stored procedure, and was trying to use same query in SQL server agent job but is not working can someone please help.
SELECT s.LAST_NAME AS sn,
RTRIM(s.FIRST_NAME)+ ' ' + LTRIM(s.LAST_NAME) AS Name,
s.FIRST_NAME AS F_Name
LEFT(middle_name,1) AS Initial,
sy.USERNAME AS USER,
s.HOME_ZIP AS ZIP,
RTRIM(UPPER(sy.USERNAME)) + LTRIM('#xyz.com') AS userP,
stm.DESCRIPTION_Maj AS company,
rg.RECORD_INPUT_DATE
FROM STCIO s
JOIN SYSME sy
ON s.ID_NUMBER =sy.ID_NUMBER
JOIN EHMGR rg
ON s.ID_NUMBER =rg.ID_NUMBER
JOIN STMEER stm
ON rg.MAJOR =stm.MAJOR
AND s.MAT_CODE IN ('','G','Q')
AND rg.CURRENT_FLAG = 'X'
AND CONVERT(datetime,CONVERT(CHAR(8),rg.RECORD_INPUT_DATE)) = GETDATE()
NOTE:datatype for Record_input_date is numeric(8,0)
Error message received is
"Arithmetic overflow error converting expression to data type datetime."
I don't have an authority to make any changes. All I'm looking for is to have this query running converting the record_input_date (numeric) to datetime and populate the record based on the getdate()
Now this would happen if you still have the date stored as numeric in a wrong format (non ANSI format)
Like instead of 20160307 for today's date it stores it as 20160703 in which case it will give error for values like 20162002 or when the date is stored as ddmmyyyy or any other variant format. To solve look at some sample data and tweak your query from
CONVERT(datetime,convert(char(8),rg.RECORD_INPUT_DATE)) = GETDATE()
to
CONVERT(datetime,convert(char(8),rg.RECORD_INPUT_DATE),<formatstring>) = GETDATE()
See list of format strings here
Another way is to use date from parts function in higher version of sql server like
SELECT DATEFROMPARTS(RECORD_INPUT_DATE / 10000,
RECORD_INPUT_DATE % 100,
(RECORD_INPUT_DATE/ 100) % 100) AS YR_MNTH_DT
If you cannot use either of above, you'll have to isolate days,months and year from the number.
Example if your number is wrong format like ddmmyyyy (03062016)
DECLARE #dd INT, #mm INT, #yyyy INT, #newdate INT
SET #dd= RECORD_INPUT_DATE/1000000 --3
SET #mm= (RECORD_INPUT_DATE/10000) %100--6
SET #yyyy= (RECORD_INPUT_DATE) % 10000--2016
SET #newdate= #yyyy*10000+#mm*100+#dd
and use this #newdate for comparison
CONVERT(datetime,convert(char(8),#newdate)) = GETDATE()
Step 1 is turning this wall of text query into something you can read.
SELECT s.LAST_NAME AS sn
, RTRIM(s.FIRST_NAME) + ' ' + LTRIM(s.LAST_NAME) AS Name
, s.FIRST_NAME AS F_Name
, LEFT(middle_name, 1)AS Initial
, sy.USERNAME AS [USER]
, s.HOME_ZIP AS ZIP
, RTRIM(UPPER(sy.USERNAME)) + '#xyz.com' AS userP
, stm.DESCRIPTION_Maj AS company
, rg.RECORD_INPUT_DATE
FROM STCIO s
JOIN SYSME sy ON s.ID_NUMBER = sy.ID_NUMBER
JOIN EHMGR rg ON s.ID_NUMBER = rg.ID_NUMBER
JOIN STMEER stm ON rg.MAJOR = stm.MAJOR
AND s.MAT_CODE in ('', 'G', 'Q')
AND rg.CURRENT_FLAG = 'X'
AND CONVERT(DATETIME, CONVERT(CHAR(8), rg.RECORD_INPUT_DATE)) = GETDATE()
The problem here is that you have an integer that is not able to be converted to a datetime value. This is an inherent problem of using improper datatypes. You are likely going to be forced to drop the date condition from this query and replace it with an ISDATE. Insert those results to a temp table. Then another query to pull from the temp table with your date predicates.

Getting conversion failed error even when CONVERT function is not being called SQL

I use this command to select all the specific dates if the given variable is date, if it is not it should return all of the fields.
The commands works when #query is in the form of a date, but it returns an error:
"Conversion failed when converting date and/or time from character string."
when it is any other arbitrary string.
Code:
select * from table where
format(dateofbirth,'dd/MMM/yyyy') = Case
when ISDATE(#query)=1 then
format(CONVERT(datetime,#query),'dd/MMM/yyyy')
else
format(dateofbirth,'dd/MMM/yyyy')
Edit:
#query can be any string for eg. "1/1/2013" , "random" , "3".
The command should return all fields if #query is not in form of a date.
You can work around this problem by re-formulating your query condition like this:
declare #query as varchar(20)='blah'
SELECT *
FROM testtable
WHERE ISDATE(#query) = 0
OR CONVERT(date, dateofbirth) = CASE ISDATE(#query)
WHEN 1 THEN CONVERT(date, #query) ELSE NULL
END
Demo on sqlfiddle.
The problem is that logic operators are not short-circuited in SQL, so the optimizer treats CONVERT(date, #query) as something that it can pre-compute to speed up the query. By expanding the condition to a CASE that depends entirely on #query you can eliminate the execution of the CONVERT branch when ISDATE(#query) returns "false".

TSQL Date Conversion Failure - My Inability to understand result sets

I've got a staging area that I'm trying to validate data in, going through multiple iterations of validation. Currently, I'm fighting some issues with a nvarchar(50) column that I'm attempting to convert to a date.
I'm aware of the common pitfall of poorly formed strings failing date conversion, so here's what I'm doing.
SELECT *
FROM ( SELECT * FROM STAGE_TABLE WHERE ISDATE(DATE_COL) = 1)
WHERE CAST(DATE_COL AS DATE) < GETDATE()
... this results in the standard "Conversion failed when converting date and/or time from character string."
But here's where things get weird for me. If I change the above statement to the following:
SELECT CAST(DATE_COL AS DATE)
FROM ( SELECT * FROM STAGE_TABLE WHERE ISDATE(DATE_COL) = 1)
... all is well, and all I've done is moved the cast from the where clause to the select clause. I think I'm missing something at a fundamental level.
FWIW, if I were to pull all records from STAGE_TABLE without the WHERE ISDATE clause, I would have poorly formed date strings.
Any insights greatly appreciated!
You should find that the first query merges the two WHERE clauses into one, and works out the CAST before the ISDATE (fail).
The 2nd query clearly has to process the WHERE first, so the CAST never sees bad data
I have just tested and can verify:
create table STAGE_TABLE ( date_col nvarchar(50) )
insert into STAGE_TABLE select 'a'
insert into STAGE_TABLE select '20100101'
First query
SELECT *
FROM ( SELECT * FROM STAGE_TABLE WHERE ISDATE(DATE_COL) = 1) X
WHERE CAST(DATE_COL AS DATE) < GETDATE()
First plan
|--Filter(WHERE:(isdate([tempdb].[dbo].[STAGE_TABLE].[date_col])=(1)))
|--Table Scan(OBJECT:([tempdb].[dbo].[STAGE_TABLE]), WHERE:(CONVERT(date,[tempdb].[dbo].[STAGE_TABLE].[date_col],0)<getdate()))
Second query
SELECT CAST(DATE_COL AS DATE)
FROM ( SELECT * FROM STAGE_TABLE WHERE ISDATE(DATE_COL) = 1) X
Second plan
|--Compute Scalar(DEFINE:([Expr1004]=CONVERT(date,[tempdb].[dbo].[STAGE_TABLE].[date_col],0)))
|--Filter(WHERE:(isdate([tempdb].[dbo].[STAGE_TABLE].[date_col])=(1)))
|--Table Scan(OBJECT:([tempdb].[dbo].[STAGE_TABLE]))
There does not seem to be a hint/option to fix the first query (since it gets rolled into one WHERE clause), but you can use this which processes both conditions in one scan pass.
SELECT *
FROM (SELECT * FROM STAGE_TABLE) X
WHERE CAST(CASE WHEN ISDATE(DATE_COL) = 1 THEN DATE_COL ELSE NULL END AS DATE) < GETDATE()

Resources