“With”命令比使用临时表慢 Select * into #table1 from

Posted

技术标签:

【中文标题】“With”命令比使用临时表慢 Select * into #table1 from【英文标题】:"With" command slower than using temporary table Select * into #table1 from 【发布时间】:2019-05-08 08:01:46 【问题描述】:

为什么下面的查询要快很多?

查询 1:

    select distinct ID mid
INTO #t1
from A_Position a where a.situationdate=@SituationDate and 
    a.Portfolio_Name=@portfolio and a.Purpose=@purpose and ID!='TOTAL'

select distinct ID gid 
INTO #t2
from B_Position a where a.situationdate=@SituationDate and 
    a.Purpose=@purpose

select @check=COUNT(mid) from #t1 A INNER JOIN #t2 B ON A.mid = 
    B.gid

查询 1 比查询 2 快得多。

查询 2:

;With 
A as (
select distinct ID mid 
    from A_Position a where 
    a.situationdate=@SituationDate and a.Portfolio_Name=@portfolio and 
    a.Purpose=@purpose and ID!='TOTAL'), 

    B as(
select distinct ID gid 
    from B_Position a where 
    a.situationdate=@SituationDate and a.Purpose=@purpose)

select @check=COUNT(mid) from A INNER JOIN B ON A.mid = 
    B.gid

查询 3:

select @check=COUNT(*)  
        from (
    select distinct ID mid 
        from A_Position a where a.situationdate=@SituationDate and 
        a.Portfolio_Name=@portfolio and a.Purpose=@purpose and 
        ID!='TOTAL') A
    inner join (  select distinct ID gid 
        from B_Position a where 
        a.situationdate=@SituationDate and a.Purpose=@purpose) B on mid=gid

基本上,所有三个查询都有相同的结果,但查询 1 只需要 1-2 秒即可执行。另一方面,查询 2 或 3 需要 10 多分钟才能执行。为什么编写代码的方式会有如此巨大的差异? (为什么“With”慢)

【问题讨论】:

SQL Server 和 mysql 是两种截然不同的产品。请不要同时标记它们。请仅标记您正在使用的 RDBMS。我已经删除了 2 个冲突的标签。 可能不相关,但查询 2 缺少其他两个查询具有的 DISTINCT(用于 B_Position)。 【参考方案1】:

这是一个优化问题。如果您查看执行计划,就会明白为什么其中一个比其他的要快得多。

首先,后两个是一样的。将子查询表示为 CTE 或子查询不会更改 SQL Server 中的执行计划。

为什么临时表版本更快?简单的答案是它获得了更好的执行计划。

但这引出了问题。原因是用于连接两个表的算法。在 CTE/子查询版本中,SQL Server 必须猜测生成了多少行。根据这个数字,它会选择它认为最好的算法。

在临时表版本中,数据已经在一张表中,SQL Server不需要猜测。

因此,临时表可以产生更好的执行计划。让我警告几件事。使用临时表有更多开销——数据实际上需要存储在某个地方。它还限制了优化的可能性(在这种情况下恰好是好的,但在其他情况下可能不是)。

您应该能够添加提示以加快其他版本的速度。我猜想OPTION (HASH JOIN) 之类的东西。

您还可以设置索引来优化所有三个版本。

【讨论】:

【参考方案2】:

答案很可能与第一个查询实际上是三个查询有关,而另外两个查询只是一个。不过,您需要查看执行计划来确认它。

上面的第二个和第三个查询已经提前确定了对两个子集进行内部连接的最佳方法是什么。但是他们当时不知道这些子集中有多少数据,所以他们有猜测,在这种情况下,看起来他们猜错了,并选择了一个非常糟糕的方法。它可能已经决定先加入并稍后过滤,或者使用索引或其他方式会更快。如果您查看执行计划,请查找预期行和实际行大不相同的位。

第一个查询将两个子集作为单独的查询放入临时表中。当它开始连接时,它确切地知道它需要担心多少数据,并且它可以选择最好的方法来处理它。

【讨论】:

以上是关于“With”命令比使用临时表慢 Select * into #table1 from的主要内容,如果未能解决你的问题,请参考以下文章

莫让“学院派”限制我们的思维:在数组的中间位置删除数据一定比链表慢?

莫让“学院派”限制我们的思维:在数组的中间位置删除数据一定比链表慢?

SELECT INTO vs WITH AS:临时表方法谁更快?

函数中的 SELECT 语句 - 别名、with、临时表...等

ORACLE WITH AS 用法,创建临时表

ORACLE创建临时事务表global temporary table 和 查询时临时表with tempName as (select ) select