如何检查INSERT中的哪些列没有正确的数据类型?

时间:2018-04-15 11:47:28

标签: sql-server tsql mybatis

想象一下,我在一个INSERT语句中有200个列,我偶尔会得到一个"无法转换"其中一列的错误。事情是,我不知道哪一列导致此错误。

在T-SQL或mybatis中是否有任何方法来检查WHICH列的格式是否正确? (我有日期,字符,数字)。我可以为每列使用ISNUMERICISDATE,但这不是那么优雅。

我在Java中使用mybatis,所以我不能使用任何PreparedStatement左右。

2 个答案:

答案 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)

随着进口量的增加 - 特别是当您预期会出现问题时 - 强烈建议采用两步进制。

  1. 将数据导入非常宽容的临时表(所有NVARCHAR(MAX)
  2. 检查,评估,操纵,纠正所需的任何内容并从此处进行实际插入
  3. 这是一种可以适应您需求的通用方法。它将针对类型映射表检查所有表值并输出所有值,这些值在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;