【问题标题】:Use subquery inside a CTE so that I can use recursion在 CTE 中使用子查询,以便我可以使用递归
【发布时间】:2016-09-01 13:56:47
【问题描述】:

我需要将多行文本连接成 1 个字符串。我不想使用 FOR XML PATH,因为我需要纯文本,而不是 XML。 我认为使用递归 CTE 可以解决问题,但我需要在 CTE 中使用子查询来创建基本案例,并且 CTE 中的递归案例无法识别子查询表。

这是我的 SQL:

DECLARE @EndCreateTableScript varchar(20) = ') ON [PRIMARY] ';
DECLARE @NewLine varchar(2) = CHAR(13) + CHAR(10);
DECLARE @createTableScript varchar(max)
SET @createTableScript = 'CREATE TABLE [' 


;WITH CTE
AS (
    SELECT ScriptTbl2.RowNumber, ScriptTbl2.CreateTableStart, ScriptTbl2.ColumnScript, ScriptTbl2.EndTableScript
    FROM
        (
            SELECT ROW_NUMBER() OVER (PARTITION BY TableName ORDER BY TableName) AS RowNumber, TableName, 
                    CreateTableStart, ColumnTextStart + DataSizeText + ColumnNullText + @NewLine AS ColumnScript, @EndCreateTableScript + TextImageScript AS EndTableScript
            FROM
            (
                SELECT DISTINCT SchemaName, TableName, @createTableScript + SchemaName + '].[' + TableName + '] ( ' + @NewLine AS CreateTableStart,
                    '[' +ColName + '] [' + DataType + '] ' AS ColumnTextStart,  
                    CASE WHEN DataType in ('bit', 'int', 'money', 'datetime') THEN ' '
                         WHEN DataType in ('numeric', 'decimal') THEN '(' + DataTypePrecision + ', ' + DataTypeScale + ') '
                         WHEN CAST(DataTypeMaxLength AS INT) = -1 THEN '(max) '
                         WHEN DataType in ('varchar', 'varbinary') THEN '('+ DataTypeMaxLength +') '
                         WHEN DataType = 'nvarchar' THEN  '('+ CAST(CAST(DataTypeMaxLength AS INT)/2 AS varchar(5)) +') '
                    END AS DataSizeText,    
                    CASE IsColumnNullable WHEN '0' THEN 'NOT NULL,' ELSE 'NULL,' END AS ColumnNullText,
                    CASE WHEN TextImageFileGroup IS NOT NULL THEN 'TEXTIMAGE_ON [' + TextImageFileGroup + ']' ELSE '' END AS TextImageScript                                                                                                 
                FROM #DBObjectsToAdd
            ) AS ScriptTbl
        ) AS ScriptTbl2
    WHERE RowNumber = 1
    UNION ALL
    SELECT CTE.RowNumber, CTE.CreateTableStart, CTE.ColumnScript + ' ' + ScriptTbl2.ColumnScript, CTE.EndTableScript 
    FROM CTE JOIN #DBObjectsToAdd ScriptTbl ON CTE.RowNumber = ScriptTbl2.RowNumber + 1
    )
    SELECT *
    FROM CTE

问题是子查询表 ScriptTbl2 在递归情况下无法识别。我该如何解决这个问题?

目标是将 ColumnScript 文本连接成每个表格的 1 行文本。

更新 数据并不重要。我只想将多行文本连接成 1 行。 1 表 1 文本 1 2 表 1 文本 2 3 表 1 文本 3 4 表 2 文本 4 5 表2文字5

使用 CTE 递归或其他一些类型的查询更改为: 1 表 1 文本 1 文本 2 文本 3 2 表2 文本4 文本5

更新 我创建了一个临时表,需要 Row_Number 进行递归和 3 个文本字段。

CREATE TABLE #TableScripts(RowNumber int, TableStart varchar(100), ColumnScript varchar(max), TableEnd varchar(100))

这是添加数据的SELECT 查询。

SELECT RowNumber, TableName, CreateTableStart, ColumnScript, TextImageScript
FROM
(
    SELECT DISTINCT ROW_NUMBER() OVER (PARTITION BY TableName ORDER BY TableName) AS RowNumber,
                SchemaName, TableName, @createTableScript + SchemaName + '].[' + TableName + '] ( ' + @NewLine AS CreateTableStart,
        ('[' +ColName + '] [' + DataType + '] ') +  
        (CASE WHEN DataType in ('bit', 'int', 'money', 'datetime') THEN ' '
                WHEN DataType in ('numeric', 'decimal') THEN '(' + DataTypePrecision + ', ' + DataTypeScale + ') '
                WHEN CAST(DataTypeMaxLength AS INT) = -1 THEN '(max) '
                WHEN DataType in ('varchar', 'varbinary') THEN '('+ DataTypeMaxLength +') '
                WHEN DataType = 'nvarchar' THEN  '('+ CAST(CAST(DataTypeMaxLength AS INT)/2 AS varchar(5)) +') '
        END) +  
        (CASE IsColumnNullable WHEN '0' THEN 'NOT NULL,' ELSE 'NULL,' END) + @NewLine AS ColumnScript,
            @EndCreateTableScript + (CASE WHEN TextImageFileGroup IS NOT NULL THEN 'TEXTIMAGE_ON [' + TextImageFileGroup + ']' ELSE '' END)  AS TextImageScript                                                                                                  
    FROM #DBObjectsToAdd
) AS ScriptTbl      
ORDER BY TableName

出于某种原因,将ROW_NUMBER 函数添加到查询中会将其中1 个表的行数从2(它只有2 列)增加到32。

【问题讨论】:

  • 为什么不想在这里使用 FOR XML?这就是做这种事情的方法。
  • XML 创建的文本在有新行或 '' 时添加那些特殊字符
  • 能否请您发布表格数据和预期输出?
  • 文本将是一个创建表的 sql 脚本。
  • 我在上面添加了当前表格数据的样本。

标签: sql-server recursion


【解决方案1】:

我认为这只是递归部分中对 JOIN 的错误引用,但这并不是问题所在。

您正在引用 CTE,可以将其视为递归的上一次迭代。但是对于您的“当前迭代”,您尝试引用 ScriptTbl2,但 ScriptTbl2 仅存在于“锚”查询中 - 您必须使用 ROW_NUMBER() 等重做该查询,以在递归部分构建您自己的 ScriptTbl2 .

可能是这样的:

DECLARE @EndCreateTableScript varchar(20) = ') ON [PRIMARY] ';
DECLARE @NewLine varchar(2) = CHAR(13) + CHAR(10);
DECLARE @createTableScript varchar(max)
SET @createTableScript = 'CREATE TABLE [' 


;WITH CTE
AS (
    SELECT ScriptTbl2.RowNumber, ScriptTbl2.CreateTableStart, ScriptTbl2.ColumnScript, ScriptTbl2.EndTableScript
    FROM
        (
            SELECT ROW_NUMBER() OVER (PARTITION BY TableName ORDER BY TableName) AS RowNumber, TableName, 
                    CreateTableStart, ColumnTextStart + DataSizeText + ColumnNullText + @NewLine AS ColumnScript, @EndCreateTableScript + TextImageScript AS EndTableScript
            FROM
            (
                SELECT DISTINCT SchemaName, TableName, @createTableScript + SchemaName + '].[' + TableName + '] ( ' + @NewLine AS CreateTableStart,
                    '[' +ColName + '] [' + DataType + '] ' AS ColumnTextStart,  
                    CASE WHEN DataType in ('bit', 'int', 'money', 'datetime') THEN ' '
                         WHEN DataType in ('numeric', 'decimal') THEN '(' + DataTypePrecision + ', ' + DataTypeScale + ') '
                         WHEN CAST(DataTypeMaxLength AS INT) = -1 THEN '(max) '
                         WHEN DataType in ('varchar', 'varbinary') THEN '('+ DataTypeMaxLength +') '
                         WHEN DataType = 'nvarchar' THEN  '('+ CAST(CAST(DataTypeMaxLength AS INT)/2 AS varchar(5)) +') '
                    END AS DataSizeText,    
                    CASE IsColumnNullable WHEN '0' THEN 'NOT NULL,' ELSE 'NULL,' END AS ColumnNullText,
                    CASE WHEN TextImageFileGroup IS NOT NULL THEN 'TEXTIMAGE_ON [' + TextImageFileGroup + ']' ELSE '' END AS TextImageScript                                                                                                 
                FROM #DBObjectsToAdd
            ) AS ScriptTbl
        ) AS ScriptTbl2
    WHERE RowNumber = 1
    UNION ALL
    SELECT CTE.RowNumber, CTE.CreateTableStart, CTE.ColumnScript + ' ' + ScriptTbl2.ColumnScript, CTE.EndTableScript 
    FROM CTE JOIN (
        SELECT ScriptTbl2.RowNumber, ScriptTbl2.CreateTableStart, ScriptTbl2.ColumnScript, ScriptTbl2.EndTableScript
        FROM (
            SELECT ROW_NUMBER() OVER (PARTITION BY TableName ORDER BY TableName) AS RowNumber, TableName, 
                    CreateTableStart, ColumnTextStart + DataSizeText + ColumnNullText + @NewLine AS ColumnScript, @EndCreateTableScript + TextImageScript AS EndTableScript
            FROM
            (
                SELECT DISTINCT SchemaName, TableName, @createTableScript + SchemaName + '].[' + TableName + '] ( ' + @NewLine AS CreateTableStart,
                    '[' +ColName + '] [' + DataType + '] ' AS ColumnTextStart,  
                    CASE WHEN DataType in ('bit', 'int', 'money', 'datetime') THEN ' '
                         WHEN DataType in ('numeric', 'decimal') THEN '(' + DataTypePrecision + ', ' + DataTypeScale + ') '
                         WHEN CAST(DataTypeMaxLength AS INT) = -1 THEN '(max) '
                         WHEN DataType in ('varchar', 'varbinary') THEN '('+ DataTypeMaxLength +') '
                         WHEN DataType = 'nvarchar' THEN  '('+ CAST(CAST(DataTypeMaxLength AS INT)/2 AS varchar(5)) +') '
                    END AS DataSizeText,    
                    CASE IsColumnNullable WHEN '0' THEN 'NOT NULL,' ELSE 'NULL,' END AS ColumnNullText,
                    CASE WHEN TextImageFileGroup IS NOT NULL THEN 'TEXTIMAGE_ON [' + TextImageFileGroup + ']' ELSE '' END AS TextImageScript                                                                                                 
                FROM #DBObjectsToAdd
            ) AS ScriptTbl
        ) AS ScriptTbl2
    ) AS ScriptTbl3
    ON ScriptTbl3.RowNumber = CTE.RowNumber + 1 --you want each iteration to increase, right?
)
SELECT *
FROM CTE

【讨论】:

  • 当我进行更改时,RowNumber 无法解析,因为它不存在并且ScriptTbl2SELECT 中不存在
  • @GloriaSantin 答案已完全更新,看看是否有帮助
  • 当然你可以将通用查询重构为视图等,但我认为尝试重用锚查询是要立即解决的问题
  • 我收到一个错误,我无法在 CTE 中使用 DISTINCT。这没有意义。
  • 我创建了一个锚点数据的临时表。因此,临时表包含 3 个文本字段。 StartScriptText、ColumnText、EndScriptText。我需要将 RowNumber 添加到临时表中,但是当我出于某种原因这样做时,其中一个表返回 32 行冗余数据。它应该只有 2 行数据。
猜你喜欢
  • 2021-03-15
  • 2014-08-11
  • 1970-01-01
  • 2012-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
  • 1970-01-01
相关资源
最近更新 更多