在if和else语句中创建相同的表,它在SQL Server中显示错误

时间:2016-11-02 13:45:27

标签: sql-server

if @id is null
    select
        id
    into #id
    from table_info
else
    select
        if
    into #id     -- This shows : There is already an object name '#id' in the database.
    from table_info i
    where i.id=@id

这是我程序的第一部分,如果有id输入,它只会计算id的相关信息,如果没有,它会计算所有id。

我想我可以创建一个表并插入信息。有没有办法解决这个错误?

3 个答案:

答案 0 :(得分:3)

这可以通过让WHERE子句进行所有检查来轻松完成:

select id
into #id
from table_info i
where @id is null or i.id = @id

答案 1 :(得分:0)

您也可以使用ISNULL

select id
into #id
from table_info i
where i.id = ISNULL(@id, i.id)

答案 2 :(得分:0)

正如您所说,您可以先显式创建表格,然后使用INSERT INTO而不是SELECT INTO

CREATE TABLE #T (ID INT)

IF @id IS NULL
BEGIN
    INSERT #T (ID) 
    SELECT ID 
    FROM table_info;
END
ELSE
BEGIN
    INSERT #T (ID) 
    SELECT ID 
    FROM table_info 
    WHERE ID = @ID;     
END

或者您可以使用UNION ALL

SELECT ID
INTO #T
FROM table_info 
WHERE @ID IS NULL
UNION ALL
SELECT ID
FROM table_info 
WHERE ID = @ID;     

使用WHERE ID = @ID OR @ID IS NULL发布的其他解决方案可行,但我会谨慎处理,因为根据您的数据,您最终可能会得到次优的执行计划。

一个非常简单的例子:

IF OBJECT_ID(N'tempdb..#table_info', 'U') IS NOT NULL DROP TABLE #Table_info;
CREATE TABLE #table_info  (ID INT NOT NULL PRIMARY KEY, Filler CHAR(1));
INSERT #table_info (ID)
SELECT TOP 1000000 ROW_NUMBER() OVER(ORDER BY a.object_id)
FROM sys.all_objects a, sys.all_objects b;

现在,对于一些基准测试:

SELECT COUNT(Filler) FROM #table_info;
SELECT COUNT(Filler) FROM #table_info WHERE ID = 50000;

这给出了以下计划:

enter image description here

因此,如果我们指定一个ID,我们可以执行索引搜索而不是索引扫描,这要快得多(0%对100%)。

现在,参数测试:

DECLARE @SQL NVARCHAR(MAX) = 'SELECT COUNT(Filler) FROM #Table_info WHERE ID = @ID OR @ID IS NULL';
EXECUTE sp_executesql @SQL, N'@ID INT', 50000;
EXECUTE sp_executesql @SQL, N'@ID INT', NULL;

给出了:

enter image description here

问题在于,由于optmiser在编译时不知道@ID是否为null或具有值,因此必须构建一个适用于两者的计划,因此无论哪种方式都使用聚集索引扫描速度慢得多。

如果我们使用IF控制流程,就像在问题中一样,那么优化工具再次能够选择最佳计划:

DECLARE @SQL NVARCHAR(MAX) = 'IF @ID IS NULL 
                                SELECT COUNT(Filler) FROM #Table_info; 
                            ELSE  
                                SELECT COUNT(Filler) FROM #Table_info WHERE ID = @ID;';

EXECUTE sp_executesql @SQL, N'@ID INT', 50000;
EXECUTE sp_executesql @SQL, N'@ID INT', NULL;

计划:

enter image description here

n.b。使用UNION具有与此相同的效果,因为它是两个select语句,生成两个计划,两个都是最优的

您可以使用OPTION (RECOMPILE)确保使用OR根据参数的值获得最佳计划(并避免参数嗅探),因此有解决方法,您只需要解决哪种解决方案最适合您。我通常更喜欢明确地创建一个表,所以我通常倾向于这样做并保留你的IF语句。