派生表是一个逻辑概念,可以在tempdb
中存储,在每次访问时通过重新评估基础语句来构建,甚至可以被优化掉。
临时表是一个物理概念。它是在tempdb
中创建并填充值的表。
哪个更好取决于它们所使用的查询、推导表所用的语句以及许多其他因素。
例如,SQL Server
中的公共表达式(CTE
)会在每次使用时被重新评估。以下查询:
WITH q (uuid) AS
(
SELECT NEWID()
)
SELECT *
FROM q
UNION ALL
SELECT *
FROM q
很可能会产生两个不同的NEWID()
。
在这种情况下,应该使用临时表,因为它保证了其值的持久性。
另一方面,这个查询:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn
FROM master
) q
WHERE rn BETWEEN 80 AND 100
使用派生表会更好,因为使用临时表会需要从master
中获取所有值,而这个解决方案只需使用在id
上的索引扫描前100
条记录。
这要看具体情况。
派生表的优点:
派生表是更大查询的一部分,将在整个查询的上下文中得到优化。如果查询优化有助于性能(通常会有帮助,但也有例外情况),这可能是一个优点。例如:如果你填充了一个临时表,然后在第二个查询中使用结果,实际上就将数据库引擎绑定到了一种执行方法(完全运行第一个查询,保存整个结果集,然后运行第二个查询),而使用派生表时,优化器可能能够找到更快的执行方法或访问路径。
派生表只存在于查询执行计划中——它是纯粹的逻辑结构。实际上并没有表格存在。
临时表的优点:
该表格“存在”——也就是说,在内存中,它被实体化为一个包含结果集的表格,并且可以重复使用。
在某些情况下,当你必须对数据进行一些复杂的转换时,性能可能会得到改善或阻塞会减少——例如,如果你想从繁忙的基础表中获取一个“Snapshot”行集,然后对该集合进行一些复杂的计算,在尽可能快地获取基础表中的行并将其解锁后,独立完成工作可能会减少竞争。在某些情况下,相对于并发优势,真正的临时表的开销很小。
最大的区别在于您可以在临时表上设置约束,包括主键。对于大型数据(我指的是数百万条记录),有时使用临时表可以获得更好的性能。我有一个需要5个连接的关键查询(每个连接都相似)。当我只有2个连接时,性能还可以,但第三个连接时性能变差,查询计划变得混乱。即使使用提示,我也无法纠正查询计划。尝试将连接重构为派生表,仍然存在相同的性能问题。使用临时表可以创建主键(然后在填充时按PK排序)。当SQL可以连接这5个表并使用PK时,性能从几分钟提高到几秒钟。我希望SQL支持派生表和CTE上的约束(即使只有PK)。