相同的查询 - 不同的执行计划
SQL 2008。
我有一个测试表:
create table Sale
(
SaleId int identity(1, 1)
constraint PK_Sale primary key,
Test1 varchar(10) null,
RowVersion rowversion not null
constraint UQ_Sale_RowVersion unique
)
我用 10k 测试行填充它。
declare @RowCount int = 10000
while(@RowCount > 0)
begin
insert Sale default values
set @RowCount -= 1
end
我运行这两个查询:
-- Query #1
select *
from Sale
where RowVersion > 0x000000000001C310
-- Query #2
declare @LastVersion rowversion = 0x000000000001C310
select *
from Sale
where RowVersion > @LastVersion
我无法弄清楚为什么这两个查询有不同的执行计划。
查询 #1 是否针对 UQ_Sale_RowVersion 索引进行索引查找。
查询 #2 对 PK_Sale 进行索引扫描。
我希望查询 #2 执行索引查找。
我希望得到一些帮助。
谢谢。
[编辑]
尝试使用 datetime2 而不是 rowversion。同样的问题。
我也尝试强制使用索引(查询 #3),
select *
from Sale with (index = IX_Sale_RowVersion)
where RowVersion > @LastVersion
这似乎显示与查询 #1 相同的查询执行计划,但执行计划显示此查询 #3 是所有这 3 个查询中最昂贵的。
[编辑]执行计划:
<ShowPlanXML xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan"
Version="1.1"
Build="10.50.1600.1">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementText="-- Query #1

select *
from Sale
where RowVersion > 0x000000000001C310

-- Query #2

"
StatementId="1"
StatementCompId="1"
StatementType="SELECT"
StatementSubTreeCost="0.00657038"
StatementEstRows="1"
StatementOptmLevel="FULL"
QueryHash="0xE442FF9A4A2A630A"
QueryPlanHash="0x347569CFDEF2A13F"
StatementOptmEarlyAbortReason="GoodEnoughPlanFound"
ParameterizedText="(@1 varbinary(8000))SELECT * FROM [Sale] WHERE [RowVersion]>@1">
<StatementSetOptions QUOTED_IDENTIFIER="true"
ARITHABORT="true"
CONCAT_NULL_YIELDS_NULL="true"
ANSI_NULLS="true"
ANSI_PADDING="true"
ANSI_WARNINGS="true"
NUMERIC_ROUNDABORT="false"/>
<QueryPlan CachedPlanSize="24"
CompileTime="1"
CompileCPU="1"
CompileMemory="136">
<RelOp NodeId="0"
PhysicalOp="Nested Loops"
LogicalOp="Inner Join"
EstimateRows="1"
EstimateIO="0"
EstimateCPU="4.18e-006"
AvgRowSize="28"
EstimatedTotalSubtreeCost="0.00657038"
Parallel="0"
EstimateRebinds="0"
EstimateRewinds="0">
<OutputList>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</OutputList>
<NestedLoops Optimized="0">
<OuterReferences>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</OuterReferences>
<RelOp NodeId="1"
PhysicalOp="Index Seek"
LogicalOp="Index Seek"
EstimateRows="1"
EstimateIO="0.003125"
EstimateCPU="0.0001581"
AvgRowSize="19"
EstimatedTotalSubtreeCost="0.0032831"
TableCardinality="10000"
Parallel="0"
EstimateRebinds="0"
EstimateRewinds="0">
<OutputList>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</OutputList>
<IndexScan Ordered="1"
ScanDirection="FORWARD"
ForcedIndex="0"
ForceSeek="0"
NoExpandHint="0">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</DefinedValue>
</DefinedValues>
<Object Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Index="[UQ_Sale_RowVersion]"
IndexKind="NonClustered"/>
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<StartRange ScanType="GT">
<RangeColumns>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="0x000000000001C310">
<Const ConstValue="0x000000000001C310"/>
</ScalarOperator>
</RangeExpressions>
</StartRange>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
</IndexScan>
</RelOp>
<RelOp NodeId="3"
PhysicalOp="Clustered Index Seek"
LogicalOp="Clustered Index Seek"
EstimateRows="1"
EstimateIO="0.003125"
EstimateCPU="0.0001581"
AvgRowSize="16"
EstimatedTotalSubtreeCost="0.0032831"
TableCardinality="10000"
Parallel="0"
EstimateRebinds="0"
EstimateRewinds="0">
<OutputList>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
</OutputList>
<IndexScan Lookup="1"
Ordered="1"
ScanDirection="FORWARD"
ForcedIndex="0"
ForceSeek="0"
NoExpandHint="0">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
</DefinedValue>
</DefinedValues>
<Object Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Index="[PK_Sale]"
TableReferenceId="-1"
IndexKind="Clustered"/>
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="[AdventureWorks].[dbo].[Sale].[SaleId]">
<Identifier>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</Identifier>
</ScalarOperator>
</RangeExpressions>
</Prefix>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
</IndexScan>
</RelOp>
</NestedLoops>
</RelOp>
<ParameterList>
<ColumnReference Column="@1"
ParameterCompiledValue="0x000000000001C310"/>
</ParameterList>
</QueryPlan>
</StmtSimple>
<StmtSimple StatementText="
declare @LastVersion rowversion = 0x000000000001C310

"
StatementId="2"
StatementCompId="2"
StatementType="ASSIGN"/>
<StmtSimple StatementText="
select *
from Sale
where RowVersion > @LastVersion"
StatementId="3"
StatementCompId="3"
StatementType="SELECT"
StatementSubTreeCost="0.0328005"
StatementEstRows="3000"
StatementOptmLevel="FULL"
QueryHash="0xE442FF9A4A2A630A"
QueryPlanHash="0x0C6238F821406F2B"
StatementOptmEarlyAbortReason="GoodEnoughPlanFound">
<StatementSetOptions QUOTED_IDENTIFIER="true"
ARITHABORT="true"
CONCAT_NULL_YIELDS_NULL="true"
ANSI_NULLS="true"
ANSI_PADDING="true"
ANSI_WARNINGS="true"
NUMERIC_ROUNDABORT="false"/>
<QueryPlan CachedPlanSize="16"
CompileTime="1"
CompileCPU="1"
CompileMemory="144">
<RelOp NodeId="0"
PhysicalOp="Clustered Index Scan"
LogicalOp="Clustered Index Scan"
EstimateRows="3000"
EstimateIO="0.0216435"
EstimateCPU="0.011157"
AvgRowSize="28"
EstimatedTotalSubtreeCost="0.0328005"
TableCardinality="10000"
Parallel="0"
EstimateRebinds="0"
EstimateRewinds="0">
<OutputList>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</OutputList>
<IndexScan Ordered="0"
ForcedIndex="0"
NoExpandHint="0">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</DefinedValue>
</DefinedValues>
<Object Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Index="[PK_Sale]"
IndexKind="Clustered"/>
<Predicate>
<ScalarOperator ScalarString="[AdventureWorks].[dbo].[Sale].[RowVersion]>[@LastVersion]">
<Compare CompareOp="GT">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Column="@LastVersion"/>
</Identifier>
</ScalarOperator>
</Compare>
</ScalarOperator>
</Predicate>
</IndexScan>
</RelOp>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>
SQL 2008.
I have a test table:
create table Sale
(
SaleId int identity(1, 1)
constraint PK_Sale primary key,
Test1 varchar(10) null,
RowVersion rowversion not null
constraint UQ_Sale_RowVersion unique
)
I populate it with 10k test rows.
declare @RowCount int = 10000
while(@RowCount > 0)
begin
insert Sale default values
set @RowCount -= 1
end
I run these two queries:
-- Query #1
select *
from Sale
where RowVersion > 0x000000000001C310
-- Query #2
declare @LastVersion rowversion = 0x000000000001C310
select *
from Sale
where RowVersion > @LastVersion
I can't figure out why these two queries have different execution plan.
Query #1 does index seek against UQ_Sale_RowVersion index.
Query #2 does index scan against PK_Sale.
I want query #2 to do index seek.
I would appreciate some help.
Thank you.
[Edit]
Tried using datetime2 instead of rowversion. The same issue.
I tried to force using index too (query #3)
select *
from Sale with (index = IX_Sale_RowVersion)
where RowVersion > @LastVersion
This seemed to show the same query execution plan as the query #1, but execution plan showed this query #3 as the most expensive among all those 3 queries.
[Edit] Execution plan:
<ShowPlanXML xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan"
Version="1.1"
Build="10.50.1600.1">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementText="-- Query #1
select *
from Sale
where RowVersion > 0x000000000001C310
-- Query #2
"
StatementId="1"
StatementCompId="1"
StatementType="SELECT"
StatementSubTreeCost="0.00657038"
StatementEstRows="1"
StatementOptmLevel="FULL"
QueryHash="0xE442FF9A4A2A630A"
QueryPlanHash="0x347569CFDEF2A13F"
StatementOptmEarlyAbortReason="GoodEnoughPlanFound"
ParameterizedText="(@1 varbinary(8000))SELECT * FROM [Sale] WHERE [RowVersion]>@1">
<StatementSetOptions QUOTED_IDENTIFIER="true"
ARITHABORT="true"
CONCAT_NULL_YIELDS_NULL="true"
ANSI_NULLS="true"
ANSI_PADDING="true"
ANSI_WARNINGS="true"
NUMERIC_ROUNDABORT="false"/>
<QueryPlan CachedPlanSize="24"
CompileTime="1"
CompileCPU="1"
CompileMemory="136">
<RelOp NodeId="0"
PhysicalOp="Nested Loops"
LogicalOp="Inner Join"
EstimateRows="1"
EstimateIO="0"
EstimateCPU="4.18e-006"
AvgRowSize="28"
EstimatedTotalSubtreeCost="0.00657038"
Parallel="0"
EstimateRebinds="0"
EstimateRewinds="0">
<OutputList>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</OutputList>
<NestedLoops Optimized="0">
<OuterReferences>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</OuterReferences>
<RelOp NodeId="1"
PhysicalOp="Index Seek"
LogicalOp="Index Seek"
EstimateRows="1"
EstimateIO="0.003125"
EstimateCPU="0.0001581"
AvgRowSize="19"
EstimatedTotalSubtreeCost="0.0032831"
TableCardinality="10000"
Parallel="0"
EstimateRebinds="0"
EstimateRewinds="0">
<OutputList>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</OutputList>
<IndexScan Ordered="1"
ScanDirection="FORWARD"
ForcedIndex="0"
ForceSeek="0"
NoExpandHint="0">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</DefinedValue>
</DefinedValues>
<Object Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Index="[UQ_Sale_RowVersion]"
IndexKind="NonClustered"/>
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<StartRange ScanType="GT">
<RangeColumns>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="0x000000000001C310">
<Const ConstValue="0x000000000001C310"/>
</ScalarOperator>
</RangeExpressions>
</StartRange>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
</IndexScan>
</RelOp>
<RelOp NodeId="3"
PhysicalOp="Clustered Index Seek"
LogicalOp="Clustered Index Seek"
EstimateRows="1"
EstimateIO="0.003125"
EstimateCPU="0.0001581"
AvgRowSize="16"
EstimatedTotalSubtreeCost="0.0032831"
TableCardinality="10000"
Parallel="0"
EstimateRebinds="0"
EstimateRewinds="0">
<OutputList>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
</OutputList>
<IndexScan Lookup="1"
Ordered="1"
ScanDirection="FORWARD"
ForcedIndex="0"
ForceSeek="0"
NoExpandHint="0">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
</DefinedValue>
</DefinedValues>
<Object Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Index="[PK_Sale]"
TableReferenceId="-1"
IndexKind="Clustered"/>
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="[AdventureWorks].[dbo].[Sale].[SaleId]">
<Identifier>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</Identifier>
</ScalarOperator>
</RangeExpressions>
</Prefix>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
</IndexScan>
</RelOp>
</NestedLoops>
</RelOp>
<ParameterList>
<ColumnReference Column="@1"
ParameterCompiledValue="0x000000000001C310"/>
</ParameterList>
</QueryPlan>
</StmtSimple>
<StmtSimple StatementText="
declare @LastVersion rowversion = 0x000000000001C310
"
StatementId="2"
StatementCompId="2"
StatementType="ASSIGN"/>
<StmtSimple StatementText="
select *
from Sale
where RowVersion > @LastVersion"
StatementId="3"
StatementCompId="3"
StatementType="SELECT"
StatementSubTreeCost="0.0328005"
StatementEstRows="3000"
StatementOptmLevel="FULL"
QueryHash="0xE442FF9A4A2A630A"
QueryPlanHash="0x0C6238F821406F2B"
StatementOptmEarlyAbortReason="GoodEnoughPlanFound">
<StatementSetOptions QUOTED_IDENTIFIER="true"
ARITHABORT="true"
CONCAT_NULL_YIELDS_NULL="true"
ANSI_NULLS="true"
ANSI_PADDING="true"
ANSI_WARNINGS="true"
NUMERIC_ROUNDABORT="false"/>
<QueryPlan CachedPlanSize="16"
CompileTime="1"
CompileCPU="1"
CompileMemory="144">
<RelOp NodeId="0"
PhysicalOp="Clustered Index Scan"
LogicalOp="Clustered Index Scan"
EstimateRows="3000"
EstimateIO="0.0216435"
EstimateCPU="0.011157"
AvgRowSize="28"
EstimatedTotalSubtreeCost="0.0328005"
TableCardinality="10000"
Parallel="0"
EstimateRebinds="0"
EstimateRewinds="0">
<OutputList>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</OutputList>
<IndexScan Ordered="0"
ForcedIndex="0"
NoExpandHint="0">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="SaleId"/>
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="Test1"/>
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</DefinedValue>
</DefinedValues>
<Object Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Index="[PK_Sale]"
IndexKind="Clustered"/>
<Predicate>
<ScalarOperator ScalarString="[AdventureWorks].[dbo].[Sale].[RowVersion]>[@LastVersion]">
<Compare CompareOp="GT">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[AdventureWorks]"
Schema="[dbo]"
Table="[Sale]"
Column="RowVersion"/>
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Column="@LastVersion"/>
</Identifier>
</ScalarOperator>
</Compare>
</ScalarOperator>
</Predicate>
</IndexScan>
</RelOp>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
查询 2 使用变量。
在编译批处理时,SQL Server 不知道变量的值,因此只能退回到与
OPTIMIZE FOR (UNKNOWN)
非常相似的启发式方法,对于
>
它将假设 30% 的行最终会匹配(或示例数据中的 3000 行)。这可以在如下的执行计划图中看到。这明显超出了 12 行 (0.12%),即 此查询的临界点是使用聚集索引扫描还是非聚集索引查找和键查找。您需要使用 OPTION (RECOMPILE) 来使其考虑实际变量值,如下面的第三个计划所示。
脚本
Query 2 uses a variable.
At the time the batch is compiled SQL Server does not know the value of the variable so just falls back to heuristics very similar to
OPTIMIZE FOR (UNKNOWN)
For
>
it will assume that 30% of the rows will end up matching (or 3000 rows in your example data). This can be seen in the execution plan image as below. This is significantly over and above the 12 rows (0.12%) which is the tipping point for this query in whether it uses a clustered index scan or a non clustered index seek and key lookups.You would need to use
OPTION (RECOMPILE)
to get it to take account of the actual variable value as shown in the third plan below.Script
尝试为您需要检索的实际数据创建覆盖索引并避免
select *
,具体取决于表中的数据,这是强制 SQL Server 不提示的唯一确定的事情< /em> 并回退到扫描。另外,由于我们正在处理参数化,因此值得尝试看看
针对未知进行优化
是否对这里的执行计划有任何影响。Try creating a covering index for the actual data that you need to retrieve and avoid
select *
, depending on the data in your table that's the only sure thing that will force SQL Server to not tip and fallback to a scan.Also, since we're dealing with parameterization, it's worth trying to see if
optimize for unknown
has any impact on the execution plan here.