【问题标题】:SELECT INTO ##temptable from dynamic @sqlSELECT INTO ##temptable 从动态@sql
【发布时间】:2019-11-08 05:48:16
【问题描述】:

SQL Server 2012。

有一个带有 sql 语句的表,由开发人员维护。

CREATE TABLE t
(
    id INT PRIMARY KEY CLUSTERED NOT NULL IDENTITY(1, 1)
  , sql_statement NVARCHAR(MAX) NOT NULL
  , recipient NVARCHAR(MAX) NOT NULL
);

INSERT INTO t
SELECT 'select 1 as one, 2 as two, 3 as three'
     , 'some-email-address@host.com'

时不时地,一个自动化流程会启动并执行其中一个语句,检查它是否返回了任何行,如果返回,则通过电子邮件发送给它们。重要的是,该过程会根据各种条件向电子邮件添加一些额外内容,因此它不是简单的“生成并发送 csv 数据”任务(我们不能简单地使用将查询结果附加到电子邮件的内置功能) .

这个过程应该是尽可能动态的。特别是,它应该支持任何返回行的有效 SQL 语句。

从 sql_statement 列检索和处理数据的当前实现类似于:

DECLARE @sql NVARCHAR(MAX);

SELECT @sql = sql_statement
  FROM dbo.t
 WHERE id = 1;

DECLARE @actual_sql NVARCHAR(MAX) = N'SELECT * INTO ##t from (' + @sql + N') t;';
EXECUTE(@actual_sql)
DECLARE @msg NVARCHAR(MAX);
SELECT @msg = 'plenty of heavy-lifting here, building the email message body based on the contents of ##t and various conditions'

DROP TABLE ##t

EXECUTE msdb.dbo.sp_send_dbmail
  @recipients = @recipients
  , ...                                    ...
  , @body = @msg

上述解决方案的问题在于它阻止了开发人员在sql_statement 列中使用WITH 语句,因为它们会导致EXECUTE(@actual_sql) 行出现语法错误(原因很明显:你不能select from (with...) )。他们可以在FROM 块中使用子查询,但我希望他们能够使用任何返回行的 SQL 代码。

有什么解决办法吗?

【问题讨论】:

  • 您是否真的将 sql 语句存储在表中以供执行?这似乎是一个非常糟糕的主意。特别是如果您不确保完全“消毒”。
  • 这是一个有效的问题。这些陈述目前由一小群(
  • 如果您将查询的格式强制为N'...FROM (' + @sql + N')',则不能。 CTE 必须在语句的开头声明,而不是中间或结尾,但该 CTE 确实SELECT 需要在结尾。您必须强制您的用户使用子查询或更改您的解决方案(根据我之前的评论,我建议后者)。
  • 我的一个想法是在 @sql 变量中的最后一个“FROM”之前注入“ INTO ##t ”子句,但对于许多不起作用的场景(在使用 FROM 关键字结束查询)。另一个是教育开发人员在他们的查询中始终包含“INTO ##t”,但随着解决方案的增长,我需要能够并行运行这些东西并动态生成 ##t 表名以避免重叠。
  • 专注于您的问题会对您造成伤害,因为这是xy problem

标签: sql-server sql-server-2012 dynamic-sql temp-tables


【解决方案1】:

如果您可以设法维护一个为每个 sql_statement 创建结果集架构的脚本,那么这将起作用。

DROP TABLE IF EXISTS t1
DECLARE @pre_sql NVARCHAR(MAX) = '

        CREATE TABLE [dbo].[t1] (
            [one]   [NVARCHAR](MAX) NULL
           ,[two]   [NVARCHAR](MAX) NULL
           ,[three] [NVARCHAR](MAX) NULL
        )'
EXEC sp_executesql @pre_sql

DECLARE @sql NVARCHAR(MAX) = 'with CTE AS(select 1 as one, 2 as two, 3 as three) SELECT * from CTE'

INSERT
    INTO t1 EXEC sp_executesql @sql

SELECT * FROM t1

【讨论】:

  • 那行得通,如果没有更好的选择,我可能会这样做。但是,它会使要维护的脚本数量增加一倍(选择语句 + 每次检查的创建表语句)。我最初的想法是将sql查询编写为 SELECT ... INTO ##t ... ,这将立即解决单个项目的问题。但后来我意识到人们会并行运行检查,事情会开始严重崩溃。所以我放弃了全局临时表的想法,现在正试图让它在没有它们的情况下工作。
猜你喜欢
  • 2016-01-22
  • 2018-05-22
  • 2012-01-23
  • 2014-09-15
  • 2023-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-16
相关资源
最近更新 更多