基于一个表创建SQL Server用户定义的表类型

6
我有一个SQL Server中的表。我想使用用户定义的表类型(例子在这里)将行批量上传到此表。为此,我需要创建一个与我的目标表格式相同的用户定义的表类型。

有没有简单的方法来做到这一点,还是说我必须手动复制所有列信息?

换句话说,我正在寻找与这个问题相反的操作(基于用户定义的类型创建SQL Server表)。

我想将这个:

CREATE TABLE [dbo].[MyTable]
(
    AAA [int] NOT NULL,
    BBB [varchar](50) NOT NULL,
    CCC [datetime] NOT NULL
)

将其转化为:

CREATE TYPE [dbo].[udtMyTable] 
AS TABLE
   (
      AAA [int] NOT NULL,
      BBB [varchar](50) NOT NULL,
      CCC [datetime] NOT NULL
   )

只进行文本替换是安全的吗?


当然,这会是什么问题呢? - Sean Lange
用我链接的问题中提到的方法反过来做似乎非常容易。我想知道是否有一种相反的方法可以实现。 - user2023861
1
@user2023861,SELECT...INTO可用于创建表,但无法创建表类型,因此在这种情况下没有逆转。除非您需要在代码中执行此操作,否则文本替换方法是最简单的方法。 - Dan Guzman
1个回答

0

准备好了吗,一个充满CONCAT的可怕CTE即将到来...

  • 此代码也可在我的 GitHub Gist 上找到,以备不时之需。

  • 下面的表值函数将为 INFORMATION_SCHEMA.TABLES 中的每个表生成一个 CREATE TYPE ... AS TABLE (...) 语句。

  • 更进一步,还有一个标量函数,它返回单个指定表和模式的字符串 CREATE TYPE 语句。

  • 示例输出如下所示。

  • 功能:

    • 是的!,可以复制粘贴!
    • 整齐地左对齐列名和类型名。
    • 在注释中包括列序数,因此您可以轻松地将它们与 SqlDataReaderSqlDataRecord 列序数匹配。
    • 基于源表的 PK 生成 PRIMARY KEY 约束,包括支持复合 PK。
    • 处理 max-length 列。
    • 在方括号中转义所有列名。不幸的是,T-SQL 没有“仅在必要时转义”的函数,因为我发现过多的方括号很丑陋(看着你,SSDT)。
    • 由于这些都是 FUNCTION 对象,而不是 PROCEDURE 对象,因此不需要任何特殊权限即可运行,并且不可能直接对您的数据库进行任何更改(您需要将生成的 CREATE TYPE 语句复制+粘贴到新的 SSMS 编辑器中并自己运行它)。所以,nyaaaa!你不能起诉我炸了你的生产 DB。
  • 不支持的功能:

    • 不会生成表类型上的索引。
    • 我没有考虑到的任何内容也不会包含在功能集中。
  • 重要说明:

    • 需要支持 STRING_AGG,这意味着您需要 SQL Server 2017 并且您的 DB 的兼容性级别为 140 或更高。
    • 我有点忽略了使用 nvarchar vs. varchar 字符串与 CONCAT - 所以这是一个可能的改进。
    • 您可能希望将模式从 dbo 更改为您用于不应与生产 dbo-模式对象混合的 DBA 工具的模式。

第一部分:CREATE FUNCTION dbo.ShowCreateTypeFromTables()

CREATE FUNCTION dbo.ShowCreateTypeFromTables() RETURNS TABLE
AS

RETURN
WITH
    pkCols AS (
        SELECT
            kcu.TABLE_SCHEMA,
            kcu.TABLE_NAME,
            CONCAT( '[', kcu.COLUMN_NAME, ']' ) AS COLUMN_NAME,
            kcu.ORDINAL_POSITION AS PK_Ordinal
        FROM
            INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS tc
            INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu ON
                tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
                AND
                tc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA
        WHERE
            tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
    ),
    pkDefExprs AS (
        SELECT
            k.TABLE_SCHEMA,
            k.TABLE_NAME,
            CONCAT(
                CHAR(13),CHAR(10), /* CRLF \r\n */
                CHAR(13),CHAR(10), /* CRLF \r\n */
                CHAR(9), /* TAB \t */
                'PRIMARY KEY ( ',
                STRING_AGG( k.COLUMN_NAME, /*separator:*/ ', ' ) WITHIN GROUP ( ORDER BY k.PK_Ordinal ),
                ' )'
            ) AS PKDefinition 
        FROM
            pkCols AS k
        GROUP BY
            k.TABLE_SCHEMA,
            k.TABLE_NAME
    ),

    maxColNameLengths AS (
    
        SELECT
            c.TABLE_SCHEMA,
            c.TABLE_NAME,
            MAX( LEN( c.COLUMN_NAME ) + 2 ) AS MaxColumnNameLength /* +2 for the [] added in `allCols` below */
        FROM
            INFORMATION_SCHEMA.COLUMNS AS c
        GROUP BY
            c.TABLE_SCHEMA,
            c.TABLE_NAME
    ),

    allCols AS (
        SELECT
            c.TABLE_SCHEMA,
            c.TABLE_NAME,
            c.COLUMN_NAME,
            LEFT( CONCAT( '[', c.COLUMN_NAME, ']', SPACE( ml.MaxColumnNameLength ) ), ml.MaxColumnNameLength ) AS ColumnNamePad,
            c.DATA_TYPE,
            c.CHARACTER_MAXIMUM_LENGTH,
            (
                CASE
                    WHEN c.CHARACTER_MAXIMUM_LENGTH IS NULL THEN c.DATA_TYPE
                    WHEN c.CHARACTER_MAXIMUM_LENGTH = -1 THEN CONCAT( c.DATA_TYPE, '(max)' )
                    ELSE CONCAT( c.DATA_TYPE, '(', c.CHARACTER_MAXIMUM_LENGTH, ')' )
                END
            ) AS ColumnTypeExpr,
            ( CASE c.IS_NULLABLE WHEN 'YES' THEN '    NULL' ELSE 'NOT NULL' END ) AS NullableExpr,
            c.ORDINAL_POSITION
        FROM
            INFORMATION_SCHEMA.COLUMNS AS c
            INNER JOIN INFORMATION_SCHEMA.TABLES AS t ON
                c.TABLE_SCHEMA = t.TABLE_SCHEMA
                AND
                c.TABLE_NAME = t.TABLE_NAME
        
            INNER JOIN maxColNameLengths AS ml ON
                c.TABLE_SCHEMA = ml.TABLE_SCHEMA
                AND
                c.TABLE_NAME = ml.TABLE_NAME
    ),
    createTableColumnExprs AS (
        SELECT
            CONCAT(
                CHAR(9), /* \t TAB */
                allCols.ColumnNamePad, ' ',
                LEFT( CONCAT( allCols.ColumnTypeExpr, SPACE(17) ), 17 ), ' ',
                allCols.NullableExpr, ' ',
                '/* ', ( allCols.ORDINAL_POSITION - 1 ), ' */'
            ) AS ColumnExpr,
            allCols.*
        FROM
            allCols
    ),
    createTableExprs AS (
        SELECT
            e.TABLE_SCHEMA,
            e.TABLE_NAME,
            STRING_AGG( e.ColumnExpr, /*separator:*/ CONCAT(',', CHAR(13), CHAR(10) ) ) WITHIN GROUP ( ORDER BY e.ORDINAL_POSITION ) AS ColumnExprs 
        FROM
            createTableColumnExprs AS e
        GROUP BY
            e.TABLE_SCHEMA,
            e.TABLE_NAME
    )

    SELECT
        ct.TABLE_SCHEMA,
        ct.TABLE_NAME,
        
        CONCAT(
            'CREATE TYPE ',
            ct.TABLE_SCHEMA,
            '.TypeOf_',
            ct.TABLE_NAME,
            ' AS TABLE (',
            CONCAT(CHAR(13),CHAR(10)),
            ct.ColumnExprs,
            ISNULL( pke.PKDefinition, '' ),
            CONCAT(CHAR(13),CHAR(10)),
            ');',
            CONCAT(CHAR(13),CHAR(10))
        ) AS CreateTypeExpr

    FROM
        createTableExprs AS ct
        LEFT OUTER JOIN pkDefExprs AS pke ON
            ct.TABLE_SCHEMA = pke.TABLE_SCHEMA
            AND
            ct.TABLE_NAME = pke.TABLE_NAME

第二部分:CREATE FUNCTION dbo.ShowCreateTypeFromTable( @schema sysname, @tableName sysname )

CREATE FUNCTION dbo.ShowCreateTypeFromTable( @schema sysname, @tableName sysname ) RETURNS nvarchar(max)
AS

BEGIN
    
    DECLARE @stmt nvarchar(max) = NULL;
    SELECT
        @stmt = e.CreateTypeExpr
    FROM
        dbo.ShowCreateTypeFromTables() AS e
    WHERE
        e.TABLE_SCHEMA = @schema
        AND
        e.TABLE_NAME = @tableName;

    RETURN @stmt;
    
END

第三部分:示例用法和输出:

USE AdventureWorks2017;

SELECT [dbo].[ShowCreateTypeFromTable] ( 'Production', 'Product'  )
GO

-- Generates this:

CREATE TYPE Production.TypeOf_Product AS TABLE (

    [ProductID]             int               NOT NULL /* 1 */,
    [Name]                  nvarchar(50)      NOT NULL /* 2 */,
    [ProductNumber]         nvarchar(25)      NOT NULL /* 3 */,
    [MakeFlag]              bit               NOT NULL /* 4 */,
    [FinishedGoodsFlag]     bit               NOT NULL /* 5 */,
    [Color]                 nvarchar(15)          NULL /* 6 */,
    [SafetyStockLevel]      smallint          NOT NULL /* 7 */,
    [ReorderPoint]          smallint          NOT NULL /* 8 */,
    [StandardCost]          money             NOT NULL /* 9 */,
    [ListPrice]             money             NOT NULL /* 10 */,
    [Size]                  nvarchar(5)           NULL /* 11 */,
    [SizeUnitMeasureCode]   nchar(3)              NULL /* 12 */,
    [WeightUnitMeasureCode] nchar(3)              NULL /* 13 */,
    [Weight]                decimal               NULL /* 14 */,
    [DaysToManufacture]     int               NOT NULL /* 15 */,
    [ProductLine]           nchar(2)              NULL /* 16 */,
    [Class]                 nchar(2)              NULL /* 17 */,
    [Style]                 nchar(2)              NULL /* 18 */,
    [ProductSubcategoryID]  int                   NULL /* 19 */,
    [ProductModelID]        int                   NULL /* 20 */,
    [SellStartDate]         datetime          NOT NULL /* 21 */,
    [SellEndDate]           datetime              NULL /* 22 */,
    [DiscontinuedDate]      datetime              NULL /* 23 */,
    [rowguid]               uniqueidentifier  NOT NULL /* 24 */,
    [ModifiedDate]          datetime          NOT NULL /* 25 */
    
    PRIMARY KEY ( [ProductID] )
);

截图证明:

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接