带有 union all 的存储过程循环

Posted

技术标签:

【中文标题】带有 union all 的存储过程循环【英文标题】:Stored procedure loop with union all 【发布时间】:2021-07-07 22:35:11 【问题描述】:

我需要帮助来优化此处显示的存储过程。它可以工作,但是当它运行时,它会为每个数据库生成一个选择脚本。我需要存储过程来联合所有的选择语句。

有人知道怎么做吗?

SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

IF OBJECT_ID('tempdb.dbo.#database') IS NOT NULL
    DROP TABLE #database

CREATE TABLE #database
(
    id INT IDENTITY PRIMARY KEY, 
    name sysname
)

SET NOCOUNT ON

BEGIN

INSERT INTO #database(name) 
    SELECT name
    FROM master.sys.databases 
    WHERE name IN (SELECT [DATABASE] COLLATE Latin1_General_CI_AS  
                   FROM SETTINGS.DBO.SETTINGS
                   WHERE [KEY] = 'DB_NAME' AND SETTING = '1' )
    ORDER BY name
END

DECLARE @id INT, @cnt INT, @sql NVARCHAR(max), @currentDb sysname;

SELECT @id = 1, @cnt = MAX(id)
FROM #database

WHILE @id <= @cnt
BEGIN
    SELECT @currentDb = name
    FROM #database
    WHERE id = @id

    SET @sql =  'select * FROM ' + @currentDb + '.dbo.People'
    PRINT @sql

    EXEC (@sql);  
    
    PRINT '--------------------------------------------------------------------------'
    SET @id = @id + 1; 
END

DROP TABLE #database
END

【问题讨论】:

您可以尝试创建一个大型查询字符串,在每次迭代中将UNIONs 或INSERT 放入一个临时表中,最后将SELECT 放入一个临时表中。 (不过,保存数据库名称的临时表并不是必需的。您可以直接在 sys.databases 中的 SELECT 上使用光标。) 旁注:您应该使用quotename() 以安全的方式格式化@currentDb,然后再将其添加到查询中。 @Mags 你过得怎么样?下面的回答解决了你的问题吗?你熟悉“接受”和回答吗? 【参考方案1】:

在循环中构建动态字符串,并且仅在循环完成后执行。

WHILE @id <= @cnt BEGIN
    SELECT @currentDb = [name]
    FROM #database
    WHERE id = @id;

    SET @sql = @sql + CASE WHEN LEN(@sql) > 0 THEN ' union all ' ELSE '' END
        + 'SELECT * FROM ' + QUOTENAME(@currentDb) + '.dbo.People';

    SET @id = @id + 1; 
END;
IF len(@sql) > 0 BEGIN
    print @sql
    exec (@sql); 
END;

注意使用QUOTENAME 来防止 SQL 注入问题 - 即使只是偶然。

【讨论】:

感谢您的报告,抱歉回复晚了。我不能让它工作。我在上面使用过的完全相同,但它不起作用我没有错误,结果值= 0。 你好,我改了代码,复制下来试了下。 要调试此代码,您将检查 @Sql 并查看查询有什么问题。【参考方案2】:

我更改了代码并复制并尝试了下面的链接,并且效果很好。感谢您的帮助。

How do you use a cursor to combine data in a Union All?

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql = @sql +
'UNION ALL SELECT column1 FROM ' + QUOTENAME(name) + '..tableA' + CHAR(10)
FROM sys.databases 
WHERE [name] NOT IN ('master', 'tempdb', 'model', 'msdb')

SELECT @sql = STUFF(@sql, 1, 10, '')

PRINT @sql
EXEC (@sql)

【讨论】:

以上是关于带有 union all 的存储过程循环的主要内容,如果未能解决你的问题,请参考以下文章

带有循环的存储过程

带有 FAST_FORWARD 游标循环的存储过程开始快,结束慢

Oracle 存储过程中的 UNION

UNION ALL 有 4 张桌子

如何php调用oracle存储过程返回的是一个结果集,该怎么从php页面中吧数据循环输出呀

SQL查询效率——UNION ALL的影响