【问题标题】:Loop through table of tables to dynamically CREATE table structures循环遍历表以动态创建表结构
【发布时间】:2019-06-12 04:41:04
【问题描述】:

我有一个元数据表,它有四列 Table_N、Column_N、DType 和 DLength,我试图循环遍历元数据表,并为每个不同的表动态地吐出 CREATE TABLE 语句及其所有列、数据类型和数据长度。基本上是典型的表结构或定义。

到目前为止,我已经添加了一个游标来循环它们,并使用动态 SQL 语法遇到障碍,特别是处理 DLength 字段的某些列值的 NULL 值。例如,如果列的数据类型是 DATE,那么该列的 Dlength 值将为 NULL。如果我将 DType 具有 VARCHAR 和 DLength 具有 100 的列连接起来,那么 '@DT' + '(' + @DL+ ')' 将导致 VARCHAR(100)但是对于 DATE、INT 或任何类似的数据类型,我需要一种不同的方法。另外,不是光标的忠实粉丝,所以如果您对此有完全不同的方法,请随时提出建议。任何建议都非常感谢。谢谢!

表名:DDC_Loop(下面的示例包含 2 个表元数据)

Table_N         Column_N    DType       DLength
--------------------------------------------------------
AUT_C_TABLOG    ORIGINAL    VARCHAR             1
AUT_C_TABLOG    PROTOCOL    VARCHAR             1
AUT_C_TABLOG    TABNAME         VARCHAR            30
ANLA             GEGST          VARCHAR             8
ANLA             GPLAB            DATE             NULL
ANLA             GRBLT          VARCHAR             5
ANLA             GRBND          VARCHAR             5
ANLA             KTOGR          VARCHAR             8
ANLA             LAND1          VARCHAR             3
ANLA             MENGE          NUMERIC           16,3

【问题讨论】:

  • 你在复制SHOW CREATE TABLE的功能吗?如果有,为什么?
  • 你好 Bill,这里的整个想法是使用 DDC_Loop 中的元数据来实现 100 个表。从上面的示例中,我们将创建 2 个表,即 AUT_C_TABLOGANLA

标签: sql-server tsql dynamic-sql


【解决方案1】:

使用公共表表达式、滞后、超前、几个案例表达式和 xml 路径,您可以将表的全部内容的创建表语句作为一个长字符串返回。

首先,创建并填充示例表(在您以后的问题中保存我们这一步)

CREATE TABLE Tables
(
    Table_N sysname,         
    Column_N sysname,    
    DType sysname,
    DLength varchar(5)
);

INSERT INTO Tables (Table_N, Column_N, DType, DLength) VALUES
('AUT_C_TABLOG', 'ORIGINAL', 'VARCHAR', '1'),
('AUT_C_TABLOG', 'PROTOCOL', 'VARCHAR', '1'),
('AUT_C_TABLOG', 'TABNAME',  'VARCHAR', '30'),
('ANLA', 'GEGST', 'VARCHAR', '8'),
('ANLA', 'GPLAB', 'DATE', NULL),
('ANLA', 'GRBLT', 'VARCHAR', '5'),
('ANLA', 'GRBND', 'VARCHAR', '5'),
('ANLA', 'KTOGR', 'VARCHAR', '8'),
('ANLA', 'LAND1', 'VARCHAR', '3'),
('ANLA', 'MENGE', 'NUMERIC', '16,3');

然后,使用 cte 获取滞后和领先表名称(这样您就知道何时添加 add create table 以及何时不添加):

WITH CTE AS 
(
    SELECT  Table_N, 
            Column_N, 
            DType, 
            DLength, 
            LAG(Table_N) OVER(ORDER BY Table_N) AS PrevTableName,
            LEAD(Table_N) OVER(ORDER BY Table_N) AS NextTableName
    FROM Tables
)

现在,在查询中使用几个 case 表达式:

SELECT  CASE WHEN PrevTableName IS NULL OR Table_N <> PrevTableName 
            THEN 'CREATE TABLE '+ Table_N +' (' 
            ELSE '' 
        END + Column_N +' '+ DType +
        CASE WHEN DLength IS NULL 
            THEN '' 
            ELSE '('+ DLength +')'
        END +
        CASE WHEN NextTableName IS NULL OR Table_N <> NextTableName 
            THEN '); '
            ELSE ', ' 
        END
FROM CTE
ORDER BY Table_N, Column_N -- Don't forget this order by, it's important!
FOR XML PATH('')

结果:

CREATE TABLE ANLA (GEGST VARCHAR(8), GPLAB DATE, GRBLT VARCHAR(5), GRBND VARCHAR(5), KTOGR VARCHAR(8), LAND1 VARCHAR(3), MENGE NUMERIC(16,3)); 
CREATE TABLE AUT_C_TABLOG (ORIGINAL VARCHAR(1), PROTOCOL VARCHAR(1), TABNAME VARCHAR(30)); 

【讨论】:

  • 非常感谢,佐哈尔!这也是一个很酷的解决方案!我将把它读入单独的 CREATE 语句中。这两种解决方案都很简洁。
  • 当然,我将添加用于创建和插入嵌套时间的脚本!呈现到 XML PATH 的输出从第一个表的第 11 列开始,没有 CREATE 语句,但随后的所有 279 个表都是完美的。仅供参考.. 输出第一列:ADDRNUMBER VARCHAR(10), CLIENT VARCHAR(3), CONSNUMBER VARCHAR(3), CREATE TABLE AXT (DATE_FROM DATE, DFT_RECEIV VARCHAR(1).... .
  • 正确答案!
【解决方案2】:

您可以尝试生成动态语句并执行该语句。这里需要为每个不同的表名给行编号,然后,当行号为 1 时,将CREATE TABLE 部分包含在 SQL 语句中。

-- Table
CREATE TABLE #DDC_Loop (
    Table_N nvarchar(100),         
    Column_N nvarchar(100),    
    DType nvarchar(100),       
    DLength nvarchar(100)
)
INSERT INTO #DDC_Loop
    (Table_N, Column_N, DType, DLength)
VALUES
    ('AUT_C_TABLOG', 'ORIGINAL', 'VARCHAR', '1'),
    ('AUT_C_TABLOG', 'PROTOCOL', 'VARCHAR', '1'),
    ('AUT_C_TABLOG', 'TABNAME',  'VARCHAR', '30'),
    ('ANLA',         'GEGST',    'VARCHAR', '8'),
    ('ANLA',         'GPLAB',    'DATE',    NULL),
    ('ANLA',         'GRBLT',    'VARCHAR', '5'),
    ('ANLA',         'GRBND',    'VARCHAR', '5'),
    ('ANLA',         'KTOGR',    'VARCHAR', '8'),
    ('ANLA',         'LAND1',    'VARCHAR', '3'),
    ('ANLA',         'MENGE',    'NUMERIC', '16,3')

-- Dynamci statement
DECLARE @stm nvarchar(max)
SET @stm = N''
;WITH cte AS (
    SELECT 
        Table_N, 
        Column_N, 
        DType, 
        DLength,
        ROW_NUMBER() OVER (PARTITION BY Table_N ORDER BY Table_N) RN,
        CASE 
            WHEN DLength IS NULL THEN N', ' + Column_N + N' ' + DType
            ELSE N', ' + Column_N + N' ' + DType + N'(' + DLength + N')'
        END ColumnDefinition
    FROM #DDC_Loop
)
SELECT 
    @stm = @stm + 
    CASE 
        WHEN RN = 1 THEN N'); CREATE TABLE ' + Table_N + N'(' + STUFF(ColumnDefinition, 1, 2, N'')
        ELSE ColumnDefinition
    END
FROM cte
ORDER BY Table_N, RN
SET @stm = STUFF(@stm, 1, 3, N'') + SUBSTRING(@stm, 1, 3)

-- Print and execute statement
PRINT @stm
EXEC (@stm)

生成的语句:

CREATE TABLE ANLA(GEGST VARCHAR(8), GPLAB DATE, GRBLT VARCHAR(5), GRBND VARCHAR(5), KTOGR VARCHAR(8), LAND1 VARCHAR(3), MENGE NUMERIC(16,3)); 
CREATE TABLE AUT_C_TABLOG(ORIGINAL VARCHAR(1), PROTOCOL VARCHAR(1), TABNAME VARCHAR(30)); 

【讨论】:

  • 感谢佐罗夫!真的很喜欢这个解决方案,只需对其进行测试,它就可以完美运行。在 280 张桌子破裂之前吐出 15 张,我相信它与第 16 张桌子中的一列有关。正在努力。跟踪错误并将及时通知您。
  • Msg 156, Level 15, State 1, Line 1 关键字“BACKUP”附近的语法不正确。消息 102,级别 15,状态 1,第 1 行 ')' 附近的语法不正确。消息 156,级别 15,状态 1,第 1 行关键字“退出”附近的语法不正确。
  • 正确答案!
  • 当然,佐罗夫。我试图将它们都标记为那个绿色的勾号作为正确答案,但看起来只有一个变成绿色。这就是为什么不得不对两者发表评论的原因。我是新手,所以如果有其他地方可以,请告诉我。
  • @Maximus 我明白了。您只能接受一个答案(这是解决您问题的最佳方法),但您可以对所有答案进行投票。祝你好运,我们随时为您提供帮助。
猜你喜欢
  • 2010-12-07
  • 2011-12-03
  • 1970-01-01
  • 2017-04-30
  • 2021-03-31
  • 2021-12-24
  • 2020-03-30
  • 2019-01-28
  • 1970-01-01
相关资源
最近更新 更多