【问题标题】:Scan and find data type扫描并查找数据类型
【发布时间】:2016-09-14 17:45:49
【问题描述】:

我们可以扫描一个表并找到它的数据类型吗,因为当我们使用导入导出向导将 csv 文件加载到一个表中时,它甚至将一个数字列默认为 varchar 我们可以在导入后扫描表并找到可能是理想的数据类型?

例如考虑一个 csv 文件 A.csv 它包含

ColumnA ColumnB ColumnC ColumnD ColumnE
1234      xyz     123.4   1      abc123
4258      acv     785.6   0      abs58
785       fgf     879.6   1      ftrd15
448       wsd   87878.6   0      wewe
78528     ews    6968.2   1      awaq
525554    qwe       2.3   0      afgd87

所以当我使用导入导出向导加载它时,所有列都是 varchar,但我需要扫描表然后查找数据类型。我最初并不关心使用正确的数据类型加载数据,我只关心在将数据加载到表中然后扫描表以查找数据类型之后。

【问题讨论】:

  • SSMS 数据导入向导可以建议数据类型(建议类型按钮),但要小心,因为它有一些限制并且有时会做出奇怪的假设。
  • 这在加载后究竟如何工作?一旦你知道了类型,你会怎么做——再做一张桌子?
  • 是的,用正确的数据类型创建一个新表

标签: sql sql-server sql-server-2008


【解决方案1】:

看起来您要处理 6 种主要数据类型。

  1. DATETIME
  2. INTEGER
  3. DECIMAL
  4. CHARACTER
  5. VARCHAR
  6. BIT

因此,一种无需显式尝试转换每一个并捕获错误的方法是使用ISDATE, ISNUMERICCHARINDEX,因为您不能在2008 年使用TRY_CONVERT。对于每一列,您可以执行类似的操作。当然,您可以在每列的游标中执行此操作,或者只是复制 case 语句几次,或者使用交叉连接。

SELECT DISTINCT
'ColumnA' as ColumnName,
CASE
   WHEN ISNUMERIC(ColumnA) = 1 AND LEN(ColumnA) = 1 AND ColumnA NOT LIKE '%[2-9]%' THEN 'Bit',
   WHEN ISNUMERIC(ColumnA) = 1 AND CHARINDEX('.',ColumnA) > 0 THEN 'Decimal'
   WHEN ISNUMERIC(ColumnA) = 1 AND CHARINDEX('.',ColumnA) = 0 THEN 'Integer'
   WHEN ISDATE(ColumnA) = 1 THEN 'Date'
   WHEN LEN(ColumnA) = 1 AND ColumnA LIKE '%[a-z]%' THEN 'Character'
   ELSE 'VARCHAR'
END AS DataTypeCheck
FROM YourTable

它并不完美,因为我们没有检查所有数据类型,但它至少可以帮助您入门。您可以添加更多LEN() 函数来确定您想要将DECIMAL 长度和精度设置为什么,以及VARCHAR() 长度。但是,无法知道后续插入是否会导致二进制数据被截断......因为这些值是未知的。因此,您只需将这些字段长度设置为足够大以接受任何后续输入。此外,这将为您提供该列的所有可能数据类型。因此,如果您有 12 和 12.34,它将返回 INTDECIMAL,您应该在其中选择 DECIMAL。如果需要,这可以在后续查询中处理。

【讨论】:

  • 非常感谢您的回复,这对我的事业有很大帮助最后一个问题如何确定布尔列?
  • @Zack 你使用BIT 可以是0,1,or NULL。我将此添加到案例语句的第一行。
【解决方案2】:

这个问题很有问题。数据的目的地/预期用途决定了数据类型,而不是数据的不完整扫描。 应该确定数据类型是什么,而不是容易出错的代码(并且任何代码容易出错)。例如,20122010 的数据类型应该是什么:

  1. INT / BIGINT
  2. 浮动
  3. VARCHAR(1 - 8000)
  4. VARBINARY(1 - 8000)
  5. 日期/日期时间? (是YYYYDDMM 还是DDMMYYYY?)

12ab呢:

  1. VARCHAR(1 - 8000)
  2. VARBINARY(1 - 8000)

true呢:

  1. VARCHAR(1 - 8000)
  2. 比特

并且依赖ISNUMERIC 是不可靠的,因为它会为不会转换的值返回1。例如,在某些文化中,使用逗号而不是句点来表示小数,因此以下是有效的货币金额,但在这种情况下它不会转换为人们期望的方式:

SELECT ISNUMERIC('212012,00'); -- 1
SELECT CONVERT(MONEY, '212012,95') AS [Money]; -- 21201295.00

或者,如果在接受的答案中使用代码,则以下内容将被视为有效的“整数”:

SELECT CONVERT(INT, '212012,00') AS [Int]; -- error
-- Msg 245, Level 16, State 1, Line 3
-- Conversion failed when converting the varchar value '212012,00' to data type int.

这个呢:

SELECT ISNUMERIC('212,012.00,0,1'); -- 1

【讨论】:

  • 逗号的编码很简单。不知道你为什么这么强调它。每个人都说OP不应该这样做。我什至说它并不完美。感谢您通过示例重申。他们是很好的例子
  • @scsimon 源是 CSV 文件,因此“数字”可能没有嵌入逗号,这消除了一些复杂性。尽管如此,我认为ISNUMERIC 的其他“奇怪”行为使其不可靠,除了逗号。但我并没有强调这一点。我举了几个例子,其中类型 不能 根据值来确定。任何解决方案都存在太多歧义。我知道你说这是一个好的开始,但并不完美。问题不是你的解决方案,而是你的要求。在不知道它是什么的情况下,它使没有有意义的 ETL 数据进入系统。
【解决方案3】:

根据您想要支持多少不同的数据类型,您可以使用convert() 函数与datalength() 的组合来发现可以成功转换列的内容,并通过百分比找出正确的数据类型成功转换的记录与总数。

但我仍然建议在加载之前发现类型以避免浪费时间和存储资源,正如我之前评论的那样。

【讨论】:

  • 我认为如果是一次(问题不清楚)你可以做一个SELECT cast(columna as integer) FROM table看看它是否报告错误。
  • 是的,如果是一次性的,小数据集...我们没有来自 OP 的完整要求。还有一整罐被称为“脏数据”的蠕虫。
  • 今年的轻描淡写。 :D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-04
相关资源
最近更新 更多