SQL Server - 表变量与具有union的select语句的临时表

时间:2017-01-26 13:18:47

标签: sql sql-server sql-server-2008

我需要从复杂的select查询中创建一些连接列,这是三个选择

的并集
SELECT C1,C2, ... FROM Source1
UNION
SELECT C1,C2,... FROM Source2
UNION
SELECT C1,C2 from Source3

现在,我没有在所有三个select语句中重复我的新列的生成逻辑,而是考虑使用表变量来临时存储联合结果,并在表变量的select中添加我的列。像

这样的东西
DECLARE @tv TABLE
(C1 varchar(max),
C2 varchar(max)
.
.
.)

INSERT INTO @tv
SELECT C1,C2, ... FROM Source1
UNION
SELECT C1,C2,... FROM Source2
UNION
SELECT C1,C2 from Source3

SELECT 
C1,
C2,
CASE WHEN ...
ELSE ''
END CN
FROM @tv

我已经阅读了有关使用表变量时要注意的性能注意事项。上面的查询有时可能会生成几千行,但在大多数情况下会生成几百行。在这种情况下切换到临时变量更好吗? UNION SELECT语句是否仍能并行运行?

2 个答案:

答案 0 :(得分:5)

将列生成逻辑放在一个地方是个好主意。我只想用子查询做到这一点:

SELECT s.*,
       (CASE WHEN ...
        ELSE ''
        END) as CN
FROM (SELECT C1,C2, ... FROM Source1
      UNION ALL
      SELECT C1,C2,... FROM Source2
      UNION ALL
      SELECT C1,C2 from Source3
     ) s;

注意:使用UNION ALL代替UNION,除非您特别希望产生删除重复项的开销。

答案 1 :(得分:3)

另一种选择是使用CTE,以及您对此的描述方式,这正是CTE的用途:

;WITH cte as
(
    SELECT C1,C2, ... FROM Source1
    UNION ALL
    SELECT C1,C2,... FROM Source2
    UNION ALL
    SELECT C1,C2 from Source3
) 
SELECT cte.*,
       (CASE WHEN ...
        ELSE ''
        END) as CN
FROM cte;

但CTE与表格变量与临时表格有关的一些事项:

tl; dr: CTE可在单个查询中重复使用,表变量和临时表可在许多查询中重用,并具有一些不同的内存/索引功能。)

  • CTE可以在您的查询中重复使用,最终可能比子查询更清晰,但有些人只是习惯于子查询。在某些情况下,我发现优化器在CTE方面比在子查询方面做得更好,但它通常都可以实现。
  • 表变量可以存储在内存中或磁盘上; CTE基本上也是这样工作的,但最大的区别是表变量可以有一个主键和添加的唯一键。你在这里没有使用它,所以没什么大不了的。另一个很大的区别是表变量可以声明为可重用的类型 - 如果这是一种你在很多地方使用的格式并且想要维护它的结构,这会有所帮助。如果您使用SQL 2014或2016,它们也很有帮助,其中表变量可以在内存表中声明为OLTP。临时表不能这样做,CTE也不能 - 这基本上是用于高争用场景的表的非常优化的版本,在具有无锁结构而不是正常的SQL锁存过程的内存中更好地处理。表变量也可以作为参数传递给其他存储过程。
  • 临时表可以添加任意数量的索引。它们也可以声明为全局临时表。他们将驻留在磁盘上,他们会将工作添加到您的tempdb。它们不能在内存OLTP表中声明。它们不能作为参数传递,但它们可以在过程之间共享(无论是作为全局临时表还是作为在同一批次中可访问的临时表)。
  • 在初始INSERT之后,临时变量和表变量都可以在其上运行其他DML语句 - 如果您需要更新/删除/插入可以工作的更多数据。 CTE无法做到这一点(如果您在CTE上运行DML,它将插入/更新/删除基础表)。