使用动态复杂查询创建表

Posted

技术标签:

【中文标题】使用动态复杂查询创建表【英文标题】:Create a table using a dynamic complex query 【发布时间】:2020-01-30 22:12:35 【问题描述】:

有没有办法从复杂的查询结果集中创建表? 一个参数将包含一个查询,我需要接受这个查询并将结果放入一个临时表中。

我让它用于一个简单的查询

Declare @Src nvarchar(4000)='Select Distinct a,b,c from mytable'
Declare @newId VARCHAR(50)

SELECT @newId = REPLACE(CONVERT(VARCHAR(50),NEWID()),'-','')

-- Make sure the table doesn't exist... if does we need to delete it. 
IF OBJECT_ID('TMP_' + @newId) IS NOT NULL 
BEGIN 
    SET @SQLStr = 'DROP TABLE TMP_' + @newId
    EXEC (@SQLStr)
END 
-- I might need the structucture or the result set or the results in the tmp table.
SET @SQLStr = 'SELECT * INTO TMP_' + @newId + ' FROM (' + @SRC + ') S WHERE 1=0'
EXEC(@SQLStr)

但是如果@src 带有“order by”或带有子查询会怎样

Select a,b,c from mytable order by a,b

我需要找到“from”并在其前面添加“to 'TMP_' + @newId',但这并不安全,因为您可以在 select 的字段部分中包含子查询。

我尝试使用sp_describe_first_result_set @Tsql=@Src,但据我所知,我需要在游标中解析结果以构建创建表语句并执行它,然后我可以使用以下方法插入复杂查询:

EXEC('Inset into tmp_'+ @newId + ' 
exec('+@src+')');

还有其他方法可以简化吗? 我正在尝试找到适用于所有版本 SQL 服务器(快速或标准)的解决方案。

【问题讨论】:

除非您想显着简化假设,否则简短的回答是否定的。如果您允许任何选择查询作为起点,那么您有一个非常非常复杂的目标,即解析查询并在字符串中找到正确的位置以注入您的 into 子句。涉及简单 CTE 的查询将是一个挑战。而 TSQL 不太适合这种情况。 并且注意“version”和“edition”是不一样的;两者都会影响可能限制任何解决方案的功能。 【参考方案1】:

我会尝试使用“sys.sp_describe_first_result_set”,例如this。 您不需要光标。

示例:

Drop  table if exists #ResultStructure
Drop  table if exists #test

create table #test (
    column1 int,
    column2 int
);

create table #ResultStructure (is_hidden bit NOT NULL
, column_ordinal int NOT NULL
, name sysname NULL
, is_nullable bit NOT NULL
, system_type_id int NOT NULL
, system_type_name nvarchar(256) NULL
, max_length smallint NOT NULL
, precision tinyint NOT NULL
, scale tinyint NOT NULL
, collation_name sysname NULL
, user_type_id int NULL
, user_type_database sysname NULL
, user_type_schema sysname NULL
, user_type_name sysname NULL
, assembly_qualified_type_name nvarchar(4000)
, xml_collection_id int NULL
, xml_collection_database sysname NULL
, xml_collection_schema sysname NULL
, xml_collection_name sysname NULL
, is_xml_document bit NOT NULL
, is_case_sensitive bit NOT NULL
, is_fixed_length_clr_type bit NOT NULL
, source_server sysname NULL
, source_database sysname NULL
, source_schema sysname NULL
, source_table sysname NULL
, source_column sysname NULL
, is_identity_column bit NULL
, is_part_of_unique_key bit NULL
, is_updateable bit NULL
, is_computed_column bit NULL
, is_sparse_column_set bit NULL
, ordinal_in_order_by_list smallint NULL
, order_by_list_length smallint NULL
, order_by_is_descending smallint NULL
, tds_type_id int NOT NULL
, tds_length int NOT NULL
, tds_collation_id int NULL
, tds_collation_sort_id tinyint NULL
);
DECLARE @SQLStr nvarchar(max)
Declare @Src nvarchar(4000)='select * from #test order by column1'
Declare @newId VARCHAR(50)

SELECT @newId = REPLACE(CONVERT(VARCHAR(50),NEWID()),'-','')
Insert #ResultStructure
exec sys.sp_describe_first_result_set @Src;

select @SQLStr = STRING_AGG(#row,'') FROM ( 
    select 
      case when column_ordinal = 1 then 'create table TMP_' + @newId + '(' else ', ' end
        + QUOTENAME (name) + ' ' + system_type_name
        + case when column_ordinal = max(column_ordinal) over () then ');' else '' 
      end as #row
     from #ResultStructure
 ) T
print @SQLStr
--EXEC(@SQLStr)


【讨论】:

这太棒了!唯一的问题是 STRING_AGG...它仅在 SQL Server 2017 中引入...我必须在以前的版本中找到等效的版本才能与 SQL 2008 及更高版本兼容 我已使您的脚本与旧版本兼容,但现在,我需要确保记录按@src 的 order by 子句(如果存在)插入...这样做,我尝试以与您执行 STRING_AGG 相同的方式来执行此操作? 我不认为这是可能的,一张桌子没有顺序。这里有更多关于这个主题的内容:link。还是我误会了什么? 谢谢!为了保留插入创建表的顺序,我使用了一个标识列,这样如果复杂的 Select 语句中有 order by,它将保留插入数据的顺序。【参考方案2】:

一种方法是用'SELECT TOP 100 PERCENT ' 替换所有'SELECT ' 字符串。如果 SELECT 有 TOP * 子句,则ORDER BY 子句在子查询中有效。

SET @SQLStr = 'SELECT * INTO TMP_' + @newId + ' FROM (' + REPLACE(@SRC, 'SELECT ', 'SELECT TOP 100 PERCENT ') + ') S WHERE 1=0'
EXEC(@SQLStr)

【讨论】:

但是源查询可能已经包含一个***子句。那么这将不起作用。

以上是关于使用动态复杂查询创建表的主要内容,如果未能解决你的问题,请参考以下文章

使用 Google Datalab,如何在 Google BigQuery 中使用 UDF 从复杂查询创建表?

具有动态表名的 Oracle Select 查询

以可使用 Impala 查询的方式在包含复杂类型的配置单元表上创建日期限制视图?

MySQL-复杂查询及条件-起别名-多表查询-04

如何在java里实现复杂的动态查询功能?

Linq2Sql - 存储复杂的Linq查询以便将来动态执行 - 原始文本 - 可能吗?