"With" 命令比使用临时命令慢 table Select * into #table1 from
"With" command slower than using temporary table Select * into #table1 from
为什么下面的查询要快很多?
查询 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" 慢)
答案很可能与以下事实有关:第一个查询实际上是三个查询,而另外两个只是一个。不过,您需要查看执行计划来确认。
上面的第二个和第三个查询已经提前确定了对两个子集进行内部连接的最佳方法是什么。但他们当时并不知道这些子集中有多少数据,所以他们猜测,在这种情况下,他们似乎猜错了,并选择了一个非常糟糕的方法。它可能已经决定先加入然后过滤、使用索引或其他东西会更快。如果您查看执行计划,请查找预期行和实际行大不相同的位。
第一个查询将两个子集作为单独的查询获取到临时表中。当它开始连接时,它确切地知道它需要担心多少数据,并且它可以选择最好的方法来处理它。
这是一个优化问题。如果您查看执行计划,就会明白为什么其中一个比其他执行计划快得多。
一二二是一样的。将子查询表示为 CTE 或子查询不会更改 SQL 服务器中的执行计划。
为什么临时 table 版本更快?简单的答案是它获得了更好的执行计划。
但这回避了问题。原因是因为用于连接两个 table 的算法。在 CTE/subquery 版本中,SQL 服务器必须猜测生成了多少行。它根据这个数字选择它认为最好的算法。
在临时的table版本中,数据已经在一个table中,所以SQL服务器不需要猜测。
因此,临时 tables 可以 产生更好的执行计划。不过,让我警告几件事。使用临时 tables 有更多的开销——数据实际上需要存储在某个地方。它还限制了优化的可能性(在这种情况下恰好是好的,但在其他情况下可能不是)。
您应该可以添加提示以加快其他版本的速度。我猜是 OPTION (HASH JOIN)
。
您也许还可以设置索引来优化所有三个版本。
为什么下面的查询要快很多?
查询 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" 慢)
答案很可能与以下事实有关:第一个查询实际上是三个查询,而另外两个只是一个。不过,您需要查看执行计划来确认。
上面的第二个和第三个查询已经提前确定了对两个子集进行内部连接的最佳方法是什么。但他们当时并不知道这些子集中有多少数据,所以他们猜测,在这种情况下,他们似乎猜错了,并选择了一个非常糟糕的方法。它可能已经决定先加入然后过滤、使用索引或其他东西会更快。如果您查看执行计划,请查找预期行和实际行大不相同的位。
第一个查询将两个子集作为单独的查询获取到临时表中。当它开始连接时,它确切地知道它需要担心多少数据,并且它可以选择最好的方法来处理它。
这是一个优化问题。如果您查看执行计划,就会明白为什么其中一个比其他执行计划快得多。
一二二是一样的。将子查询表示为 CTE 或子查询不会更改 SQL 服务器中的执行计划。
为什么临时 table 版本更快?简单的答案是它获得了更好的执行计划。
但这回避了问题。原因是因为用于连接两个 table 的算法。在 CTE/subquery 版本中,SQL 服务器必须猜测生成了多少行。它根据这个数字选择它认为最好的算法。
在临时的table版本中,数据已经在一个table中,所以SQL服务器不需要猜测。
因此,临时 tables 可以 产生更好的执行计划。不过,让我警告几件事。使用临时 tables 有更多的开销——数据实际上需要存储在某个地方。它还限制了优化的可能性(在这种情况下恰好是好的,但在其他情况下可能不是)。
您应该可以添加提示以加快其他版本的速度。我猜是 OPTION (HASH JOIN)
。
您也许还可以设置索引来优化所有三个版本。