通过在每个 Table 中使用不同列数的 Union ALL 显示所有 Table 中的所有列

Display All Columns from all Table by using Union ALL with Different no of Columns in Each Table

我有三个列数不同的表。例如 T1(C1)、T2(C1、C2、C3)、T3(C1、C4)。我想生成一个 Dynamic SQL 来创建一个像

这样的视图
    CREATE VIEW [dbo].[vwData]
AS 
SELECT C1,NULL AS C2,NULL AS C3,NULL AS C4
 FROM DBO.T1
UNION ALL 
SELECT C1,C2,C3,NULL AS C4
 FROM DBO.T2
UNION ALL 
SELECT C1,NULL AS C2,NULL AS C3,C4
 FROM DBO.T3

我通过使用两个嵌套循环来实现这个目标,方法是检查每一列是否存在于 table 中。
但在生产环境中,我们有大约 30 table 个,每个 table 个大约有 60 个列。 创建 Dynamic SQL 大约需要 7 分钟,这对我们来说不是 Acceptable。我们希望进一步提高性能。

将不胜感激。

这里有一些动态 SQL 可以创建和执行您描述的内容。这与您当前 SQL 的表现相比如何?

Fiddle: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=800747a3d832e6e29a15484665f5cc8b

declare @tablesOfInterest table(tableName sysname, sql nvarchar(max))
declare @allColumns table(columnName sysname)
declare @sql nvarchar(max)

insert @tablesOfInterest(tableName) values ('table1'), ('table2')

insert @allColumns (columnName)
select distinct c.name
from sys.columns c 
where c.object_id in
(
    select object_id(tableName)
    from @tablesOfInterest
)

update t
set sql = 'select ' + columnSql + ' from ' + quotename(tableName)
from @tablesOfInterest t
cross apply
(
    select string_agg(coalesce(quotename(c.Name), 'null') + ' ' + quotename(ac.columnName), ', ') within group (order by ac.columnName)
    from @allColumns ac
    left outer join sys.columns c
    on c.object_id = object_id(t.tableName)
    and c.Name = ac.columnName
) x(columnSql)


select @sql = string_agg(sql, ' union all ')
from @tablesOfInterest

print @sql

exec (@sql)

正如评论中所提到的,而不是 运行 这个动态 SQL 每次你需要执行这个查询时,你可以使用它来生成一个视图,然后你可以根据需要重用它。

适当地向基础表添加索引和过滤器可以进一步提高性能;但在不了解更多上下文的情况下,我们无法就具体细节提供太多建议。

你可以试试这个:

我使用一些我知道的通用 tables,他们共享一些专栏来展示原则。只需将 tables 替换为您自己的 tables:

注意:我不使用这些INFORMATION_SCHEMAtable阅读他们的内容。它们作为重叠列的示例...

DECLARE @statement NVARCHAR(MAX);

WITH cte(x) AS
(
    SELECT
     (SELECT TOP 1 * FROM INFORMATION_SCHEMA.TABLES FOR XML AUTO, ELEMENTS XSINIL,TYPE) AS [*]
    ,(SELECT TOP 1 * FROM INFORMATION_SCHEMA.COLUMNS FOR XML AUTO, ELEMENTS XSINIL,TYPE) AS [*]
    ,(SELECT TOP 1 * FROM INFORMATION_SCHEMA.ROUTINES FOR XML AUTO, ELEMENTS XSINIL,TYPE) AS [*]
    --add all your tables here...
    FOR XML PATH(''),TYPE
)
,AllColumns AS
(
    SELECT DISTINCT a.value('local-name(.)','nvarchar(max)') AS ColumnName
    FROM cte
    CROSS APPLY x.nodes('/*/*') A(a)
)
,AllTables As
(
    SELECT a.value('local-name(.)','nvarchar(max)') AS TableName
          ,a.query('*') ConnectedColumns
    FROM cte
    CROSS APPLY x.nodes('/*') A(a)
)
SELECT @statement=
STUFF((
(
 SELECT 'UNION ALL SELECT ' +
        '''' + TableName + ''' AS SourceTableName ' +
       (
        SELECT ',' + CASE WHEN ConnectedColumns.exist('/*[local-name()=sql:column("ColumnName")]')=1 THEN QUOTENAME(ColumnName) ELSE 'NULL' END + ' AS ' + QUOTENAME(ColumnName)   
        FROM AllColumns ac
        FOR XML PATH('root'),TYPE
       ).value('.','nvarchar(max)') + 
       ' FROM ' + REPLACE(QUOTENAME(TableName),'.','].[')
 FROM AllTables
 FOR XML PATH(''),TYPE).value('.','nvarchar(max)')
),1,10,'');

EXEC( @statement); 

简短说明:

每个 table 的第一行将转换为 XML。使用 AUTO-mode 将在 <root> 中使用 table 的名称并将所有列添加为嵌套元素。

第二个 CTE 将创建存在于任何 table 中的所有列的不同列表。

第三个 CTE 将提取所有表及其连接的列。

最后的 SELECT 将使用嵌套的字符串连接来创建所有列的 UNION ALL SELECT。给定名称的存在将决定该列是使用其名称还是作为 NULL.

调用

只需使用 PRINT 打印出 @statement 以查看生成的动态创建的 SQL 命令。