通过使用Union ALL和每个表中不同的列数来显示所有表中的所有列

时间:2018-11-05 10:33:40

标签: sql-server tsql dynamic-sql

我有三个表,它们的列数不同。例如T1(C1),T2(C1,C2,C3),T3(C1,C4)。我想生成一个动态SQL,该动态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

我通过检查表中是否存在每列来使用两个嵌套循环来实现此目标。
但是在生产中,我们大约有30个表,每个表中都有大约60列。 创建动态SQL大约需要7分钟,这对我们来说是不可接受的。我们希望进一步提高性能。

非常感谢您的及时帮助。

2 个答案:

答案 0 :(得分:1)

这里有一些动态SQL,它将创建并执行您描述的内容。与您当前的SQL性能相比如何?

提琴: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,不如使用它来生成一个视图,然后可以根据需要重新使用该视图。

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

答案 1 :(得分:1)

您可以尝试以下方法:

我使用一些我知道的通用表,它们共享一些列以显示原理。只需将表替换为您自己的表即可:

注意:我不使用这些 INFORMATION_SCHEMA 表来读取其内容。它们用作重叠列的示例...

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); 

简短说明:

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

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

第三CTE将提取所有具有关联列的表格。

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

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