想象一下,我在一个INSERT
语句中有200个列,我偶尔会得到一个"无法转换"其中一列的错误。事情是,我不知道哪一列导致此错误。
在T-SQL或mybatis中是否有任何方法来检查WHICH列的格式是否正确? (我有日期,字符,数字)。我可以为每列使用ISNUMERIC
,ISDATE
,但这不是那么优雅。
我在Java中使用mybatis,所以我不能使用任何PreparedStatement
左右。
答案 0 :(得分:1)
您可以构建一个尝试转换每个可疑列的查询 并将查询限制为其中一个转换尝试失败的位置。
在尝试将其转换或转换为日期时间或数字类型时,大多数错误数据都会出现在CHAR或VARCHAR中。
所以你可以限制你的研究。
此外,从错误中您应该看到哪个值无法转换为哪种类型。这也有助于限制您研究的领域。
使用表变量的简化示例:
declare @T1 table (id int identity(1,1) primary key, field1 varchar(30), field2 varchar(30), field3 varchar(30));
declare @T2 table (id int identity(1,1) primary key, field1_int int, field2_date date, field3_dec decimal(10,2));
insert into @T1 (field1, field2, field3) values
('1','2018-01-01','1.23'),
('not an int','2018-01-01','1.23'),
('1','not a date','1.23'),
('1','2018-01-01','not a decimal'),
(null,'2018-01-01','1.23'),
('1',null,'1.23'),
('1','2018-01-01',null)
;
select top 1000
id,
case when try_convert(int, field1) is null then field1 end as field1,
case when try_convert(date, field2) is null then field2 end as field2,
case when try_convert(decimal(10,4), field3) is null then field3 end as field3
from @T1
where
try_convert(int, coalesce(field1, '0')) is null
or try_convert(date, coalesce(field2, '1900-01-01')) is null
or try_convert(decimal(10,4), coalesce(field3, '0.0')) is null;
返回:
id field1 field2 field3
-- ---------- ----------- -------------
2 not an int NULL NULL
3 NULL not a date NULL
4 NULL NULL not a decimal
如果原始数据没有太多不良数据,您可以先尝试修复原始数据。
或者将try_convert用于包含错误数据的有问题的列。
例如:
insert into @T2 (field1_int, field2_date, field3_dec)
select
try_convert(int, field1),
try_convert(date, field2),
try_convert(decimal(10,4), field3)
from @T1;
答案 1 :(得分:1)
随着进口量的增加 - 特别是当您预期会出现问题时 - 强烈建议采用两步进制。
NVARCHAR(MAX)
)这是一种可以适应您需求的通用方法。它将针对类型映射表检查所有表值并输出所有值,这些值在TRY_CAST
中失败(需要SQL-Server 2012 +)
模型的表格登台表(部分借鉴了LukStorms的答案 - thx!)
CREATE TABLE #T1 (id INT IDENTITY(1,1) PRIMARY KEY
,fldInt VARCHAR(30)
,fldDate VARCHAR(30)
,fldDecimal VARCHAR(30));
GO
INSERT INTO #T1 (fldInt, fldDate, fldDecimal) values
('1','2018-01-01','1.23'),
('blah','2018-01-01','1.23'),
('1','blah','1.23'),
('1','2018-01-01','blah'),
(null,'2018-01-01','1.23'),
('1',null,'1.23'),
('1','2018-01-01',null);
- 类型映射(可能会自动从现有目标表的INFORMATION_SCHEMA
获取)
DECLARE @type_map TABLE(ColumnName VARCHAR(100),ColumnType VARCHAR(100));
INSERT INTO @type_map VALUES('fldInt','int')
,('fldDate','date')
,('fldDecimal','decimal(10,2)');
- 登台表的名称
DECLARE @TableName NVARCHAR(100)='#T1';
- 为每列动态创建的语句
DECLARE @columnSelect NVARCHAR(MAX)=
(SELECT
' UNION ALL SELECT id ,''' + tm.ColumnName + ''',''' + tm.ColumnType + ''',' + QUOTENAME(tm.ColumnName)
+ ',CASE WHEN TRY_CAST(' + QUOTENAME(tm.ColumnName) + ' AS ' + tm.ColumnType + ') IS NULL THEN 0 ELSE 1 END ' +
'FROM ' + QUOTENAME(@TableName)
FROM @type_map AS tm
FOR XML PATH('')
);
- 最终动态创建的语句
DECLARE @cmd NVARCHAR(MAX)=
'SELECT tbl.*
FROM
(
SELECT 0 AS id,'''' AS ColumnName,'''' AS ColumnType,'''' AS ColumnValue,0 AS IsValid WHERE 1=0 '
+ @columnSelect +
') AS tbl
WHERE tbl.IsValid = 0;'
- 使用EXEC()
执行
EXEC(@cmd);
结果:
+----+------------+---------------+-------------+---------+
| id | ColumnName | ColumnType | ColumnValue | IsValid |
+----+------------+---------------+-------------+---------+
| 2 | fldInt | int | blah | 0 |
+----+------------+---------------+-------------+---------+
| 5 | fldInt | int | NULL | 0 |
+----+------------+---------------+-------------+---------+
| 3 | fldDate | date | blah | 0 |
+----+------------+---------------+-------------+---------+
| 6 | fldDate | date | NULL | 0 |
+----+------------+---------------+-------------+---------+
| 4 | fldDecimal | decimal(10,2) | blah | 0 |
+----+------------+---------------+-------------+---------+
| 7 | fldDecimal | decimal(10,2) | NULL | 0 |
+----+------------+---------------+-------------+---------+
创建的语句就像这里:
SELECT tbl.*
FROM
(
SELECT 0 AS id,'' AS ColumnName,'' AS ColumnType,'' AS ColumnValue,0 AS IsValid WHERE 1=0
UNION ALL SELECT id
,'fldInt'
,'int'
,[fldInt]
,CASE WHEN TRY_CAST([fldInt] AS int) IS NULL THEN 0 ELSE 1 END
FROM [#T1]
UNION ALL SELECT id
,'fldDate'
,'date',[fldDate]
,CASE WHEN TRY_CAST([fldDate] AS date) IS NULL THEN 0 ELSE 1 END
FROM [#T1]
UNION ALL SELECT id
,'fldDecimal'
,'decimal(10,2)'
,[fldDecimal]
,CASE WHEN TRY_CAST([fldDecimal] AS decimal(10,2)) IS NULL THEN 0 ELSE 1 END
FROM [#T1]
) AS tbl
WHERE tbl.IsValid = 0;