是否有可以检查 varchar 数据并建议合适数据类型的工具?

Is there a tool that can inspect varchar data and suggest suitable datatypes?

使用大型 varchar 或 nvarchar 字段将外部数据导入暂存 table 是一种很常见的做法,这样可以更好地控制下游的数据验证和转换。

因此,在创建新的 ETL 时,必须创建正确数据类型的目标 table。

我确定一定有一个工具可以分析 (n)varchar 列中的数据并建议 suitable 目标列数据类型,其方式与导入数据向导对平面数据所做的非常相似文件数据源。

我一直是手动完成的,但是在为 100 多列 table 创建 ETL 时变得非常乏味。必须有一个工具可以让我自动完成 90% 的过程。

让我们假设 2012+ 并且你有一个看起来像这样的 table。

Col1        Col2            Col3
2018-05-23  Another Value   25.26
2018-06-30  John Smith      3635.556365
2018-07-01  Jane Doe        NULL

然后我们可以 XML 找点乐子。交叉应用 B 会将行转换为 XML,而交叉应用 C 将 "dynamically" 逆透视您的数据。然后就是一个简单的聚合。

;with cte as (
Select C.*
 From  YourTable A
 Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
 Cross Apply (
                Select ColNr  = Row_Number() over (Order by (select null))
                      ,Item   = a.value('local-name(.)','varchar(100)')
                      ,Value  = a.value('.','varchar(max)')
                 From  B.XMLData.nodes('/row')  as C1(n)
                 Cross Apply C1.n.nodes('./@*') as C2(a)
             ) C
)
Select ColNr       = max(ColNr)
      ,Item 
      ,CntValues   = sum(1) 
      ,MinStrLen   = min(len(Value))
      ,MaxStrLen   = max(len(Value))
      ,MinValue    = min(Value)
      ,MaxValue    = max(Value)
      ,PctNumeric  = sum(IIF(try_convert(money,Value) is null,0,1)) / sum(convert(money,1))
      ,MinNumeric  = min(try_convert(money,Value))
      ,MaxNumeric  = max(try_convert(money,Value))
      ,MaxDecimal  = max(case when try_convert(money,Value) is not null and charindex('.',Value)>0 then len(parsename(Value,1)) else 0 end)
      ,PctDate     = sum(IIF(try_convert(date,Value) is null,0,1)) / sum(convert(money,1))
      ,MinDateTime = min(try_convert(datetime,Value))
      ,MaxDateTime = max(try_convert(datetime,Value))
 From  cte
 Group By Item
 Order By 1

Returns

请注意:这不是一个完整的答案,int 也没有提出建议,但它会增加对泛型的一些了解 table。

我知道问题已解决,@John 很棒。

我想在这么有趣的问题上分享我的想法。

在这样的问题中,脚本必须涵盖许多测试用例并且已经过全面测试。

我的脚本针对小示例测试了一两次。

如果符合要求,就可以开发成完整的健壮脚本。

--create table ETL(Col1 varchar(50), Col2 varchar(50), Col3 varchar(50))
--insert into ETL values
-- ('2018-05-23','Another Value','25.26')
--,('2018-06-30','John Smith','3635.50')
--,('2018-07-01','Jane Doe', '')
--select * from ETL
DECLARE @TableName VARCHAR(50) = 'ETL'
    ,@colName VARCHAR(300)
    ,@Sql NVARCHAR(max)
DECLARE @DataType VARCHAR(50) = ''

DECLARE @getobject CURSOR CREATE TABLE #temp (
    ColName VARCHAR(50)
    ,colType VARCHAR(50)
    ) SET @getobject = CURSOR
FOR
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @TableName
ORDER BY ORDINAL_POSITION

OPEN @getobject

FETCH NEXT
FROM @getobject
INTO @colName

--Even if one data is decimal then data type has to be decimal
WHILE @@FETCH_STATUS = 0
BEGIN
    TRUNCATE TABLE #temp

    SET @DataType = ''
    -- it is necessary to make column null if it is blank(you can do this in ETL too)
    SET @Sql = 'update ' + @TableName + '  set ' + @colName + '=null where ' + @colName + '='''''

    EXEC (@Sql)

    SET @Sql = ''
    SET @Sql = '  if not exists(select 1 from ' + @TableName + ' where ISNUMERIC(' + @colName + ')=0 and (' + @colName + ' is not null ))
begin

if exists(select  ' + @colName + ' from dbo.ETL where TRY_CONVERT(DECIMAL(18,5),' + @colName + '  ) is not null)
insert into #temp values(''' + @colName + ''',''DECIMAL(18,5)'')

ELSE
insert into #temp values(''' + @colName + ''',''INT'')

end
else if not exists(select 1 from dbo.ETL where ISDATE(' + @colName + ')=0 and (' + @colName + ' is not null ))
insert into #temp values(''' + @colName + ''',''DateTime'')'

    -- print @Sql
    EXEC (@Sql)

    SELECT @DataType = colType
    FROM #temp

    PRINT @DataType

    IF (
            @DataType IS NOT NULL
            AND @DataType <> ''
            )
    BEGIN
        SET @Sql = ''
        SET @Sql = 'ALTER table ' + @TableName + '
              Alter column ' + @colName + '  ' + @DataType + ''

        PRINT @Sql

        EXEC (@Sql)
    END

    FETCH NEXT
    FROM @getobject
    INTO @colName
END

CLOSE @getobject

DEALLOCATE @getobject

--drop table ETL
DROP TABLE #temp