这段 TSQL 是如何工作的?

Faj*_*iya 7 sql-server t-sql errors

为什么下面的批次不能按预期工作?

DROP TABLE IF EXISTS #test1;

IF 1 = 1
    SELECT *
    INTO #test1
    FROM sys.databases
    WHERE database_id <= 5;

ELSE
    SELECT *
    INTO #test1
    FROM sys.databases;


SELECT *
FROM #test1;
Run Code Online (Sandbox Code Playgroud)

当我运行它时,我得到:

消息 2714,级别 16,状态 1,第 12 行 数据库中已有一个名为“#test1”的对象。

我必须像这样更改代码:

DROP TABLE IF EXISTS ##test1;

IF 1 = 1
    EXEC ('
    SELECT *
    INTO ##test1
    FROM sys.databases
    WHERE database_id <= 5;
    '    );

ELSE
    EXEC ('
    SELECT *
    INTO ##test1
    FROM sys.databases;');


SELECT *
FROM ##test1;
Run Code Online (Sandbox Code Playgroud)

这按预期工作。上述错误似乎是一个解析错误,我不确定。谁能帮忙解释一下这里发生了什么?

And*_*y M 4

该错误是在解析时生成的,因此在批处理执行之前。仅当INTO目标是临时表(本地或全局)时才会发生这种情况。当目标是常规表时,不会发生这种情况。

解决此问题的一种方法是使用动态 SQL,如您所示。正如您还发现的那样,该方法不适用于本地临时表,这可能并不总是很好。

您还可以按照 Aleksey Vitsko 的建议,预先创建目标表并改为使用INSERT...SELECT来填充它。我认为这可能不方便的一种情况是,当查询返回多个计算列时,因为您首先必须正确确定数据类型,以便适应输出数据,而不会导致溢出、精度损失、转换错误或其他此类情况。SELECT...INTO

要解决这两个问题(继续使用本地临时表,避免显式定义目标列),您可以尝试像这样重写脚本:

DROP TABLE IF EXISTS #test1;

/* the query from the "if true" branch */
SELECT
  *
INTO
  #test1
FROM
  sys.databases
WHERE
  database_id <= 5
  AND ...  /* your IF condition should go here */
;

IF NOT (...)  /* the negated version of your IF condition goes here;
                 make sure to account for possible "unknowns" */
  /* the "else" branch query */
  INSERT INTO
    #test1
  SELECT
    *
  FROM
    sys.databases
;

SELECT * FROM #test1;
Run Code Online (Sandbox Code Playgroud)

第一个查询将运行并创建目标表。如果IF条件为真,表也将被填充,否则表将为空。如果条件不成立,下一条语句将填充创建的表。