【问题标题】:Display All Columns from all Table by using Union ALL with Different no of Columns in Each Table使用 Union ALL 显示所有表中的所有列,每个表中的列数不同
【发布时间】:2018-11-05 10:33:40
【问题描述】:

我有三个不同列数的表。例如 T1(C1)、T2(C1、C2、C3)、T3(C1、C4)。我想生成一个动态 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 分钟,这对我们来说是不可接受的。我们希望进一步提高性能。

我们将不胜感激立即提供帮助。

【问题讨论】:

  • 请花一分钟时间阅读How to Askedit您的问题。我们需要查看您现有的代码,而不仅仅是最终结果。
  • 您试图用类似的方法解决的实际潜在问题是什么?我无法想象为什么包含所有表的数据的视图可能会有帮助
  • 您是否在多个表中存储相同/相关的数据?
  • 你几乎是正确的。我们希望将所有表中的所有数据放入一个视图中,但它们的列数略有不同。所以当我合并所有表时,我看到了错误。为了解决这个问题,我创建了动态 SQL 来检查表中是否存在列。如果表中没有列,我将其添加为 NULL AS Col。
  • 我们可以假设如果两个表有一个共享名称的列,它们也有一个共享定义吗?或者table1 可以将字段Id 设置为biginttable2Id 设置为uniqueidentifier(例如)?

标签: sql-server tsql dynamic-sql


【解决方案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)

如 cmets 中所述,您无需每次需要执行此查询时都运行此动态 SQL,而是可以使用它来生成一个视图,然后您可以根据需要重用该视图。

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

【讨论】:

【解决方案2】:

你可以试试这个:

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

注意:我不使用这些 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-mode 将使用<root> 中的表名并将所有列添加为嵌套元素。

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-15
    • 2016-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多