在If和Else块中使用SQL插入到临时表中

19

我想根据 SQL 2005 中的条件来填充一个临时表。这个临时表无论如何都会拥有相同的结构,但是根据条件不同将使用不同的查询来填充。下面是一个简化的脚本示例,在 ELSE 块的 INSERT INTO 语句中检查语法时出现以下错误:

数据库中已经存在名为 '#MyTestTable' 的对象。

DECLARE @Id int
SET @Id = 1

IF OBJECT_ID('tempdb..#MyTestTable') IS NOT NULL DROP TABLE #MyTestTable

IF (@Id = 2) BEGIN 
    SELECT 'ABC' AS Letters
    INTO #MyTestTable;
END ELSE BEGIN
    SELECT 'XYZ' AS Letters
    INTO #MyTestTable;
END

我可以在IF/ELSE语句之前创建临时表,然后只需在条件块中执行INSERT SELECT语句,但该表将具有许多列,我试图使其更有效率。那是唯一的选择吗?还是有其他方法可以实现这个目的?

谢谢, Matt

9个回答

25
回答晚了8年,但我很惊讶没有人想到:
select * into #MyTempTable from...
where 1=2

IF -- CONDITION HERE
insert into #MyTempTable select...
ELSE
insert into #MyTempTable select...

简单快捷。不需要动态SQL。
编辑:正如Sushant在评论中所说,T-SQL中也可以使用Select top 0,并且在大多数情况下看起来更好。话虽如此,它的性能与where 1=2完全相同(通过查询计划验证),所以请根据自己的喜好选择。

2
太棒了!多年后,对于我们这些寻求帮助的人仍然非常有用。 - MsTapp
2
确实是一个不错的解决方案,但我想补充一点,使用“Select Top 0 *”比强制添加条件更好。此外,如果原始表包含标识列,则必须在插入之前将其禁用。 - Sushant Sardeshpande
"Where 1=2" 的执行效果与 top 0 完全相同(在 mysql 中也适用),因此它并不比强制条件更好。不过,你关于 ID 列的观点是正确的。 - Gaspa79

16

你遇到的问题不是在填充临时表,而是在尝试创建表。SQL解析你的脚本并发现你正在尝试在两个不同的地方创建它,因此会引发错误。它没有足够聪明的能力意识到“执行路径”不可能触发两个创建语句。使用动态SQL也行不通。我已经尝试过。

DECLARE @Command  varchar(500)

DECLARE @Id int 
SET @Id = 2

IF OBJECT_ID('tempdb..#MyTestTable') IS NOT NULL DROP TABLE #MyTestTable 

IF (@Id = 2) BEGIN  
    SET @Command = 'SELECT ''ABC'' AS Letters INTO #MyTestTable'
END ELSE BEGIN 
    SET @Command = 'SELECT ''XYZ'' AS Letters INTO #MyTestTable'
END 

EXECUTE (@Command)

select * from #MyTestTable

但是临时表只能在动态会话期间存在。因此,不幸的是,看起来你必须先声明这个表,然后再填充它。也许编写和支持代码很尴尬,但它的性能足够高效。


1
我的回答既不建议使用动态SQL,也不推荐使用它--事实上,它展示了动态SQL无法用于解决这个问题,并得出结论:在数据插入之前明确声明临时表可能是解决问题的方法。 - Philip Kelley
您可以使用动态SQL并插入到全局临时表(##MyTesttable),然后删除全局表。 - Rym

6
在您提供的情况下,您可以这样做。
DECLARE @Id int
SET @Id = 1

IF OBJECT_ID('tempdb..#MyTestTable') IS NOT NULL DROP TABLE #MyTestTable

SELECT 
  CASE WHEN (@Id = 2) 
    THEN 'ABC' 
    ELSE 'XYZ' 
  END AS Letters
INTO #MyTestTable;

但是,除此之外,您需要在if语句之前创建表格,如下所示:

Create Table #MyTestTable (
  MyValue varchar(3)
)
IF (@Id = 2) BEGIN 
  Insert Into (MyValue)
  SELECT 'ABC' AS Letters;
END ELSE BEGIN
  Insert Into (MyValue)
  SELECT 'XYZ' AS Letters;
END

是的,对于这种情况,这个方法可行。但实际的查询要复杂得多。 - CuppM

2

如果无法预先创建临时表并且不想将核心逻辑放在动态SQL中,我使用以下解决方案。

IF 1 = 1 -- Replace with actual condition
BEGIN
    SELECT * INTO #tmp1 FROM dbo.Table1
END
ELSE
BEGIN
    SELECT * INTO #tmp2 FROM dbo.Table2
END

-- Inserting data into global temp table so sql server can't complain on not recognizing in a context
DECLARE @Command VARCHAR(MAX)
IF OBJECT_ID('tempdb..#tmp1') IS NOT NULL
BEGIN
    SET @Command = 'SELECT * INTO ##tmp FROM #tmp1'
END
ELSE
BEGIN
    SET @Command = 'SELECT * INTO ##tmp FROM #tmp2'
END

EXECUTE(@Command)
SELECT * INTO #tmpFinal FROM ##tmp -- Again passing data back to local temp table from global temp table to avoid seeing red mark

IF OBJECT_ID('tempdb..##tmp') IS NOT NULL DROP TABLE ##tmp
IF OBJECT_ID('tempdb..#tmp1') IS NOT NULL DROP TABLE #tmp1
IF OBJECT_ID('tempdb..#tmp2') IS NOT NULL DROP TABLE #tmp2

SELECT * FROM #tmpFinal

IF OBJECT_ID('tempdb..#tmpFinal') IS NOT NULL DROP TABLE #tmpFinal

1
这是一个旧问题,但对于其他人来说:
用户Philip Kelley给出的动态SQL答案不适用于本地临时表(#Mytemp)。您可以创建动态SQL将其插入到全局临时表中(##MyTemp),稍后可以将其删除。
DECLARE @Command  varchar(500)

DECLARE @Id int 
SET @Id = 2

IF OBJECT_ID('tempdb..#MyTestTable') IS NOT NULL DROP TABLE ##MyTestTable 

IF (@Id = 2) BEGIN  
    SET @Command = 'SELECT ''ABC'' AS Letters INTO ##MyTestTable'
END ELSE BEGIN 
    SET @Command = 'SELECT ''XYZ'' AS Letters INTO ##MyTestTable'
END 

EXECUTE (@Command)

select * from ##MyTestTable

DROP ##MyTestTable

1
我尝试了这个:

SELECT S1.* INTO #MytestTable
FROM 
(   SELECT 'ABC' AS Letters WHERE 1 = CASE @Id=2 THEN 1 ELSE 2 END
    UNION
    SELECT 'XYZ' AS Letters WHERE 1 = CASE @Id=1 THEN 1 ELSE 2 END
) AS S1

如果以后需要向 #MyTestTable 添加列,则此解决方案更好,否则您必须在重新运行脚本之前物理删除它,在测试条件下这很麻烦。


0

你可以尝试这段代码。

IF (CONDITION HERE)
       begin
           select * into #MyTempTable from tablename ....
       end
       ELSE
           truncate table #MyTempTable 
           insert into #MyTempTable 
           select * from tablename ....
       end

谢谢!!!


那根本不是解决方案。 - Schatak

0

这段代码可能对你有帮助

--creating temptable using columns of two existing tables
--you can create your temp table Using other methods

select top 0 VI.*,VU.FullName
into #mytemptable
from dbo.Items VI inner join
     dbo.Users as VU 
     on VU.Id=VI.Id

--insert your data base on your condition
if(i<2) --First Condition
begin
INSERT INTO #mytemptable
SELECT VI.*,VU.FullName 
from dbo.Items VI inner join
     dbo.Users as VU 
     on VU.Id=VI.Id
end
Else if(2<i) --Second Condition
begin
INSERT INTO #mytemptable
SELECT VI.*,VU.FullName 
from dbo.Items VI inner join
     dbo.Users as VU 
     on VU.Id=VI.Id
end

select * from #mytemptable --show result

drop table #mytemptable --drop table if its needed

这段代码在SQL Server 2014中有效,我不知道它是否适用于SQL 2005。


-1

在这两种情况下,您都可以在SELECT INTO之前删除表,例如:

DECLARE @Id int 
SET @Id = 1  

IF (@Id = 2) BEGIN  
    IF OBJECT_ID('tempdb..#MyTestTable') IS NOT NULL DROP TABLE #MyTestTable
    SELECT 'ABC' AS Letters 
    INTO #MyTestTable; 
END ELSE BEGIN 
    IF OBJECT_ID('tempdb..#MyTestTable') IS NOT NULL DROP TABLE #MyTestTable 
    SELECT 'XYZ' AS Letters 
    INTO #MyTestTable; 
END 

评论后更新:

那很烦人。

两个单独的临时表怎么样?然后在 If/Else 逻辑之后,检查每个表是否存在,如果存在,则选择插入到第三个临时表中?这可能不会表现得很好,但是否重要取决于您需要它来做什么。


我试过了。在每个块的INSERT INTO语句之前, 我都放置了IF OBJECT_ID('tempdb..#MyTestTable') IS NOT NULL DROP TABLE #MyTestTable, 但结果相同。 - CuppM
不是我。但也许是因为你的建议也无法通过语法检查?我认为创建IF/ELSE之前的临时表比创建2个单独的临时表更费力。 - CuppM
是的,在逻辑之前创建临时表似乎是最好的选择,但是如果不了解你正在做什么,很难说得确切。 - Phil Sandler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接