【问题标题】:How to identify all columns which are causing data type error in T-SQL Union?如何识别导致 T-SQL Union 中数据类型错误的所有列?
【发布时间】:2021-06-30 09:44:55
【问题描述】:

我正在尝试对两个大表(超过 600 列)执行联合。两个表中的所有变量都应该具有相同的格式,但它们是由两个不同的团队分别准备的。

SELECT * into #FINAL_TABLE
from 
(select * from Table1
UNION
select * FROM Table2 ) as tmp

我收到一个错误,其中包含有关数据类型错误信息的一列:

消息 8114,第 16 层,状态 5,600 号线
将数据类型 nvarchar 转换为数值时出错。

基于那个我不能说的错误:

  1. 哪一列导致错误?
  2. 是否还有其他列也会导致不匹配错误?

是否有更有效的方法来识别导致日期类型不匹配错误的所有列,然后逐列检查?

【问题讨论】:

  • 你好。有几点需要说明:(1) 表中有 600 列的数据库未规范化,(2) 除非在非常特殊的情况下,否则永远不要编写 SELECT * 查询,这不是一个,(3 ) 确保将数据放入具有适当数据类型的列中(INT 列中的整数,DATE 列中的日期等)。
  • 如果合并表,列的顺序也必须相同(建议不要使用 * 的另一个原因)。您可以使用INFORMATION_SCHEMA.COLUMNS 系统视图来比较列数据类型和顺序。
  • @TT。谢谢你的评论。 (1) 我无法更改,下一步是将该表导出为 CSV 并将其导入特定软件。 (2)。有道理。 (3)。好的,但这仍然是逐列解决方案。
  • 旁白:Union 将删除重复的行,但会降低性能。如果不需要,即您知道没有重复项,则使用union all

标签: sql sql-server tsql


【解决方案1】:

此查询应识别两个表之间的数据类型不匹配。

SELECT 
  IST1.COLUMN_NAME as Table1_column
  ,IST1.DATA_TYPE as Table1_datatype
  ,IST2.COLUMN_NAME as Table2_column
  ,IST2.DATA_TYPE as Table2_datatype
FROM information_schema.columns IST1
INNER JOIN information_schema.columns IST2 
  ON IST1.ORDINAL_POSITION=IST2.ORDINAL_POSITION
WHERE IST1.table_name='Table1' AND IST1.TABLE_SCHEMA='dbo'
AND IST2.table_name='Table2' AND IST2.TABLE_SCHEMA='dbo'
AND IST1.DATA_TYPE <> IST2.DATA_TYPE

【讨论】:

  • 这个查询不正确,因为它会返回 false "false"... 就像 IST1 的列是日期时间,而 IST2 的列是 DATETIME2 或 NCHAR/NVARCHAR/CHAR/VARCHAR。您不能使用 IST1.DATA_TYPE IST2.DATA_TYPE 而是使用带有数据类型“家族”而非数据类型的 CASE!
  • 你是对的,这些是可能的转换,因此数据类型不必完全匹配。但是提问的人说Table1和Table2应该基本相同。我只是为他提供了一个快速查询,以检查该声明的有效性。
【解决方案2】:

您可以这样做,这将返回数据类型不一致的表名和列名。但是 cmets 中的点确实站在这里:

  • 您应该明确SELECT 中定义您的列,而不是使用SELECT *
  • 在一个表中有 600 列肯定是一个设计缺陷,应该加以解决。尤其是当您想UNION 该数据时

另外,如果你正在合并数据,我怀疑你想要UNION ALL,而不是UNION。我怀疑你的任何行都会在这里重复:

  1. 您有 600 列,这意味着所有 600 需要在 2 个单独的行上相同
  2. 您的 2 个表有不同的定义,这意味着 2 个表不能有重复项。

无论如何,一种方法是使用dm_exec_describe_first_result_set 获取返回结果集的详细信息,然后比较两个结果集中相同序号位置的列的数据类型。

SELECT T1.[name] AS Table1Column,
       T1.system_type_name AS Table1Type,
       T2.[name] AS Table2Column,
       T2.system_type_name AS Table2Type
FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM Table1;',NULL,NULL) T1
     JOIN sys.dm_exec_describe_first_result_set(N'SELECT * FROM dbo.Table2;',NULL,NULL) T2 ON T1.column_ordinal = T2.column_ordinal
WHERE T1.system_type_id <> T2.system_type_id;

【讨论】:

    【解决方案3】:

    对于 SQL 2017 及更高版本:

    DECLARE @SomeString varchar(100) = 'ABC_DEF_GHI_jkl';
    WITH T0 AS
    (
    SELECT 1 AS INDICE, 
           SUBSTRING(@SomeString, 1, 1) AS RAW_LETTER, 
           SUBSTRING(UPPER(@SomeString), 1, 1) AS UP_LETTER
    UNION ALL
    SELECT INDICE + 1, 
           SUBSTRING(@SomeString, INDICE + 1, 1) AS RAW_LETTER, 
           SUBSTRING(UPPER(@SomeString), INDICE + 1, 1)
    FROM   T0
    WHERE  INDICE < LEN(@SomeString)
    )
    SELECT STRING_AGG(RAW_LETTER, '') WITHIN GROUP (ORDER BY INDICE)
    FROM   T0
    WHERE  RAW_LETTER COLLATE Latin1_General_BIN = UP_LETTER;
    

    对于 2017 年之前的 SQL Server:

    WITH T0 AS
    (
    SELECT 1 AS INDICE, 
           SUBSTRING(@SomeString, 1, 1) AS RAW_LETTER, 
           SUBSTRING(UPPER(@SomeString), 1, 1) AS UP_LETTER
    UNION ALL
    SELECT INDICE + 1, 
           SUBSTRING(@SomeString, INDICE + 1, 1) AS RAW_LETTER, 
           SUBSTRING(UPPER(@SomeString), INDICE + 1, 1)
    FROM   T0
    WHERE  INDICE < LEN(@SomeString)
    )
    SELECT STUFF((SELECT '' + RAW_LETTER
                  FROM   T0
                  WHERE  RAW_LETTER COLLATE Latin1_General_BIN = UP_LETTER
                  FOR XML PATH('')), 1, 0, '');
    

    【讨论】:

      猜你喜欢
      • 2012-07-23
      • 2022-01-18
      • 2013-03-01
      • 2013-02-18
      • 2013-11-29
      • 2019-04-17
      • 2020-09-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多