是否有可以检查 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
使用大型 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