【问题标题】:Finding the data types of a SQL temporary table查找 SQL 临时表的数据类型
【发布时间】:2011-11-21 03:56:47
【问题描述】:

我需要从使用#temp 表切换到@table 变量,以便可以在函数中使用它。

我的查询使用插入到#temp(来自多个表),如下所示:

SELECT 
  a.col1, 
  a.col2, 
  b.col1... 
INTO #temp
FROM ...

有没有一种简单的方法可以找出#temp 表中列的数据类型,以便我可以创建具有与#temp 相同的列和数据类型的@table 变量?

【问题讨论】:

  • @AaronBertrand 是对的,但来自@gotqn 的答案对于没有temp 表来说是极好的。

标签: sql sql-server


【解决方案1】:

查找 SQL 临时表的数据类型

方法 1 - 使用 SP_HELP

EXEC TempDB..SP_HELP #TempTable;

注意-

在表结构中,表名称显示类似于“#TempTable________________________________________________________________________________________________________0000000004CB”的内容。实际上,每个临时表名称的总长度将是 128 。为了在多个会话中以不同的方式处理相同的临时表名称,SQL Server 将自动在中间添加一些下划线,并在末尾添加字母数字。

方法 2 - 使用 SP_COLUMNS

EXEC TempDB..SP_COLUMNS '#TempTable';

方法 3 – 使用 INFORMATION_SCHEMA.COLUMNS、SYS.COLUMNS、SYS.TABLES 等系统表

SELECT * FROM TempDB.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME IN (
SELECT NAME FROM TempDB.SYS.TABLES WHERE OBJECT_ID=OBJECT_ID('TempDB.dbo.#TempTable')
);
GO

SELECT * FROM TempDB.SYS.COLUMNS WHERE OBJECT_ID=OBJECT_ID('TempDB.dbo.#TempTable');
GO

SELECT * FROM TempDB.SYS.TABLES WHERE OBJECT_ID=OBJECT_ID('TempDB.dbo.#TempTable');
GO

【讨论】:

    【解决方案2】:

    您需要确保 sp_help 在表所在的同一数据库 (tempdb) 中运行。您可以通过直接为调用添加前缀来做到这一点:

    EXEC tempdb.dbo.sp_help @objname = N'#temp';
    

    或者通过在 tempdb.sys.columns 前添加一个连接前缀:

    SELECT [column] = c.name, 
           [type] = t.name, c.max_length, c.precision, c.scale, c.is_nullable 
        FROM tempdb.sys.columns AS c
        INNER JOIN tempdb.sys.types AS t
        ON c.system_type_id = t.system_type_id
        AND t.system_type_id = t.user_type_id
        WHERE [object_id] = OBJECT_ID(N'tempdb.dbo.#temp');
    

    这不会为您处理好事情,例如调整 varchar 的 max_length 与 nvarchar 不同,但这是一个好的开始。

    在 SQL Server 2012 或更高版本中,您可以使用新的 DMF 来描述结果集,从而消除该问题(并为您组装 max_length/precision/scale)。但它不支持#temp 表,所以只需注入查询不带INTO

    SELECT name, system_type_name, is_nullable
      FROM sys.dm_exec_describe_first_result_set(N'SELECT 
            a.col1, 
            a.col2, 
            b.col1... 
          --INTO #temp
          FROM ...;',NULL,1);
    

    【讨论】:

    • 我喜欢第二个建议,但它没有提供文本类型,所以在这里它与数据类型结合在一起。 'SELECT cols.column_id,cols.name,ty.name,cols.max_length,cols.precision,cols.scale FROM tempdb.sys.columns cols join sys.types ty on cols.system_type_id = ty.system_type_id WHERE [object_id] = OBJECT_ID(N'tempdb..#myTable');'
    • 我想在redshift中做这个,可以吗?
    【解决方案3】:

    其他答案将为您提供所需的信息,但仍需要您在定义表变量时全部输入。

    以下 TSQL 将允许您快速生成任何给定表的表变量定义。

    这可以为您节省大量时间,而不是手动输入表定义,例如:

    table(Field1Name nvarchar(4), Field2Name nvarchar(20), Field3Name int
    , Field4Name numeric(28,12))
    

    TSQL:

    select top 10 * 
    into #temp
    from db.dbo.myTable
    
    
    
    declare @tableName nvarchar(max)
    set @tableName = '#temp'
    
    use tempdb
    declare @tmp table(val nvarchar(max))
    insert into @tmp 
    select case data_type   
        when 'binary' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + cast(CHARACTER_MAXIMUM_LENGTH AS nvarchar(max)) + ')'
        when 'char' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + cast(CHARACTER_MAXIMUM_LENGTH AS nvarchar(max)) + ')'
        when 'datetime2' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + CAST(DATETIME_PRECISION as nvarchar(max)) + ')'
        when 'datetimeoffset' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + CAST(DATETIME_PRECISION as nvarchar(max)) + ')'
        when 'decimal' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + cast(NUMERIC_PRECISION as nvarchar(max)) + ',' + cast(NUMERIC_SCALE as nvarchar(max)) + ')'
        when 'nchar' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + cast(CHARACTER_MAXIMUM_LENGTH AS nvarchar(max)) + ')'
        when 'numeric' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + cast(NUMERIC_PRECISION as nvarchar(max)) + ',' + cast(NUMERIC_SCALE as nvarchar(max)) + ')'
        when 'nvarchar' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + cast(CHARACTER_MAXIMUM_LENGTH AS nvarchar(max)) + ')'
        when 'time' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + CAST(DATETIME_PRECISION as nvarchar(max)) + ')'
        when 'varbinary' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + cast(CHARACTER_MAXIMUM_LENGTH AS nvarchar(max)) + ')'
        when 'varchar' then COLUMN_NAME + ' ' + DATA_TYPE + '(' + cast(CHARACTER_MAXIMUM_LENGTH AS nvarchar(max)) + ')'
        -- Most standard data types follow the pattern in the other section.  
        -- Non-standard datatypes include: binary, char, datetime2, datetimeoffset, decimal, nvchar, numeric, nvarchar, time, varbinary, and varchar
        else COLUMN_NAME + ' ' + DATA_TYPE
    
        end +  case when IS_NULLABLE <> 'YES' then ' NOT NULL' else '' end 'dataType'
         from INFORMATION_SCHEMA.COLUMNS
    where TABLE_NAME like @tableName + '%'
    
    declare @result nvarchar(max)
    set @result = ''
    select @result = @result + [val] + N','
    from @tmp
    where val is not null
    
    set @result = substring(@result, 1, (LEN(@result)-1))
    
    -- The following will replce '-1' with 'max' in order to properly handle nvarchar(max) columns
    set @result = REPLACE(@result, '-1', 'max')
    select @result
    

    输出:

    Field1Name nvarchar(4), Field2Name nvarchar(20), Field3Name int
    , Field4Name numeric(28,12)
    

    【讨论】:

    • 正是我今天要找的东西
    【解决方案4】:

    要获取具有数据类型的列名称,请使用此

    EXEC tempdb.dbo.sp_help N'#temp';
    

    或 仅获取列名称以使用此

    SELECT * 
    FROM tempdb.sys.columns 
    WHERE [object_id] = OBJECT_ID(N'tempdb..#temp');
    

    【讨论】:

      【解决方案5】:

      接受的答案没有给出数据类型。将 tempdb.sys.columns 与 sys.types 连接会给出答案注释中提到的数据类型。但是加入 system_type_id 会产生一个额外的数据类型为“sysname”的行。相反,“user_type_id”给出了下面给出的确切解决方案。

      SELECT cols.NAME
       ,ty.NAME
      FROM tempdb.sys.columns cols
      JOIN sys.types ty ON cols.user_type_id = ty.user_type_id
      WHERE object_id = OBJECT_ID('tempdb..#temp')
      

      【讨论】:

      • 为了进一步扩展这个答案,您还可以从 tempdb.sys.columns 中选择 max_length、precision、scale 和 is_nullable 来获取最大长度、精度、比例以及该列是否可以为 null 或不是。
      【解决方案6】:

      您要做的是获取有关您正在查询的列的系统类型的信息。

      对于SQL Server 2012 and later,您可以使用sys.dm_exec_describe_first_result_set 函数。它返回有关列的非常详细的信息,system_type_column 包含完整的系统类型定义(准备在您的表定义中使用):

      例如:

      SELECT * 
      FROM [sys].[dm_exec_describe_first_result_set] (N'SELECT object_id, name, type_desc FROM sys.indexes', null, 0);
      

      【讨论】:

      • 我收到错误“无法确定元数据,因为语句 'SELECT * FROM tempdb.dbo.#T' 使用临时表。”对于临时表...
      • 查看以下关于限制的文章 - msdn.microsoft.com/en-us/library/ff878258.aspx - 请参阅 Remarks 部分。
      【解决方案7】:

      您需要限定 sp_help 进程从 tempdb 数据库运行以获取有关哈希表的详细信息,因为这是实际存储哈希表的位置。如果您尝试从不同的数据库运行 sp_help,您将收到该表不存在于该数据库中的错误。

      如果您的查询是在 tempdb 之外执行的,我认为是这样,您可以运行以下命令:

      exec tempdb..sp_help #temp
      

      此过程的一个好处是它为您包含了列数据类型的文本描述。这使得复制和粘贴到另一个查询变得非常容易,例如如果您尝试使用临时表的定义来创建表变量。

      您可以在 Syscolumns 表中找到相同的信息,但它会为您提供必须自己映射的类型的数字标识符。使用 sp_help 将为您节省一步。

      【讨论】:

      • 谢谢你,这比接受的答案要好得多,因为它实际上解释了正在做什么以及为什么。
      【解决方案8】:

      我会走懒惰的路线并使用

      use tempdb
      GO
      EXECUTE sp_help #temp
      

      【讨论】:

        【解决方案9】:

        是的,临时表的数据类型将是您选择并插入其中的列的数据类型。因此,只需查看 select 语句并根据您选择的列确定每种数据类型。

        【讨论】:

        • 谢谢 - 我知道你可以这样做,但是检查多个列和表会变得很痛苦,特别是如果它是一个巨大的临时表。
        • @woggles 我明白了。在这种情况下,billinkc 的 lazy 方法更适合您。
        • 现在我只需要找到一个不错的懒惰方法来编写 declare @ table 语句:)
        • @woggles 我不知道是否有一种懒惰的方法,但这会告诉你列名、数据类型和每一个的长度,在你可以迭代并创建脚本的结果集中自己动态构建 SQL 语句。 SELECT sc.name,st.name as type_name ,sc.max_length FROM tempdb.sys.columns sc inner join sys.types st on st.system_type_id=sc.system_type_id WHERE [object_id] = OBJECT_ID('tempdb..#temp');
        • @woggles 在当前版本的 SQL Server 中很痛苦(不要忘记精度和规模,将 nvarchar 的 max_length 减半的条件,如果 max_length 为 -1,则将 max_length 更改为 MAX,处理别名类型、表类型、CLR 类型)...在 Denali 中会有更好的方式使用元数据发现功能:sqlblog.com/blogs/aaron_bertrand/archive/2010/12/20/…
        猜你喜欢
        • 1970-01-01
        • 2017-04-20
        • 2016-02-05
        • 2012-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-17
        相关资源
        最近更新 更多