您好我有针对某些第三方软件编写的报告应用程序。不幸的是,它将所有值存储为nvarchar,并且不验证客户端上的数据条目,因此我在
时收到以下错误"Conversion failed when converting date and/or time from character string"
System.Data.SqlClient.SqlException was unhandled by user code
或者如果我尝试在SSMS中执行代码:
Msg 241, Level 16, State 1, Procedure settlement_list, Line 10
Conversion failed when converting date and/or time from character string.
我认为这是有人在数据字段中输入文本值的结果,所以我尝试使用此ISDATE代码来查找错误值:
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
我得到零行返回...我也手动筛选数据(它的几十万行所以它的那种硬,看不出坏的值???
有人有任何建议吗?
编辑---
这是存储过程(我知道where子句很难看)
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
答案 0 :(得分:2)
如果没有先将ISDATE() = 1
行转储到#temp表中,您就无法强制查询引擎尝试处理语句的顺序。您无法保证处理顺序或短路,即使有人会建议使用CTE或子查询首先过滤掉坏行。所以有些人可能会建议:
;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;
今天,这似乎有效。但从长远来看,确保此处理顺序的唯一方法 - 在当前版本的SQL Server中 - 是:
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;
您是否考虑过验证输入值?例如,您可以在应用程序输入无效日期时将其发送到手腕上,而不是惩罚选择其错误数据的人,从而更改此错误在应用程序中出现的位置。如果您通过存储过程控制更新/插入,则可以说:
IF ISDATE(@mat3_04_02) = 0
BEGIN
RAISERROR('Please enter a valid date.', 11, 1);
RETURN;
END
如果您不通过存储过程控制数据操作,则可以向表中添加检查约束(在清除现有错误数据之后)。
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);
这样当错误消息冒泡到用户时,他们将看到约束名称,并希望能够将其映射到前端失败的数据入口点:
Msg 547,Level 16,State 0,Line 1
INSERT语句冲突 使用CHECK约束“mat3_04_02_valid_date”。冲突 发生在数据库“your_db”,表“Intmu11.matter3”,列中 'mat3_04_02'。
声明已经终止。
或者更好的是,首先使用正确的数据类型!同样,在将现有错误数据更新为NULL
之后,您可以说:
ALTER TABLE Intmu11.matter3 ALTER COLUMN mat3_04_02 DATETIME;
现在当有人试图输入非日期时,他们会在用户尝试选择错误数据时遇到相同的错误:
Msg 241,Level 16,State 1,Line 1
当转换失败 从字符串转换日期和/或时间。
在SQL Server 2012中,您可以使用TRY_CONVERT()
解决此问题,但您仍应尝试从头开始获取数据类型。
答案 1 :(得分:2)
检查查询位置
ISDATE(mat3_04_02) = 1
AND
Coalesce(mat3_04_02, '') = ''
要成为一个日期,它必须有一个值。 但如果它没有值,则只匹配第二个条件。 这两个条件的交集(和)始终为false。
如果您正在寻找null,那么“mat3_04_02为空”但它仍将返回0行。
尝试
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
我认为您希望日期排序而不是字符串排序
问题始于找到有效日期,并变为查找无效日期
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