创建计划指南以缓存(延迟假脱机)CTE结果


19

我通常会通过首先构造一个使用正确计划的查询,然后将其复制到不使用该计划的类似查询中来创建计划指南。但是,有时这很棘手,尤其是在查询不完全相同的情况下。从头开始创建计划指南的正确方法是什么?

SQLKiwi提到了在SSIS中制定计划,是否有一种方法或有用的工具来帮助制定SQL Server的良好计划?

有问题的特定实例是以下CTE:SQLFiddle

with cte(guid,other) as (
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other;

有没有任何方法来使结果拿出正好3个不同的guidS和没有更多?我希望将来能够通过将计划指南与CTE类型的查询一起使用来更好地回答问题,这些指南被多次引用以克服某些SQL Server CTE怪癖。


Answers:


14

有什么方法可以使结果精确地显示3个不同的GUID,仅此而已?我希望将来能够通过将计划指南与CTE类型的查询一起使用来更好地回答问题,这些指南被多次引用以克服某些SQL Server CTE怪癖。

今天不行。非递归公用表表达式(CTE)被视为内联视图定义,并在优化之前在引用它们的每个位置(就像常规视图定义一样)扩展到逻辑查询树中。您查询的逻辑树是:

LogOp_OrderByCOL: Union1007 ASC COL: Union1015 ASC 
    LogOp_Project COL: Union1006 COL: Union1007 COL: Union1014 COL: Union1015
        LogOp_Join
            LogOp_ViewAnchor
                LogOp_UnionAll
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const

            LogOp_ViewAnchor
                LogOp_UnionAll
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const
                    LogOp_Project ScaOp_Intrinsic newid, ScaOp_Const

注意两个视图锚和对内部函数的六个调用newid在开始优化之前,。尽管如此,许多人认为优化器应该能够识别扩展的子树最初是单个引用的对象并相应地进行简化。还有几个Connect请求,允许显式实现CTE或派生表。

更通用的实现方式是使优化程序考虑具体化任意通用表达式以提高性能(CASE子查询是当今可能出现问题的另一个示例)。微软研究院发表论文于2007年就此(PDF),尽管迄今为止尚未实现。目前,我们仅限于使用表变量和临时表之类的显式实现。

SQLKiwi提到了在SSIS中制定计划,是否有一种方法或有用的工具来帮助制定SQL Server的良好计划?

这只是一厢情愿就我而言,,远远超出了修改计划指南的想法。从原则上讲,可以编写一种直接操作放映计划XML的工具,但是如果没有使用该工具的特定优化器工具,则可能会使用户感到沮丧(开发人员会想到这一点)。

在此问题的特定上下文中,这样的工具仍将无法以可由多个使用者使用的方式来实现CTE内容(在这种情况下,将两个输入都输入到交叉联接中)。优化器和执行引擎确实支持多用户假脱机,但仅出于特定目的-不能将其应用于该特定示例。

虽然我不确定,但我很有把握,即使查询与计划不完全相同,也可以遵循RelOps(嵌套循环,懒惰假脱机)-例如,如果您向CTE添加了4和5 ,它仍然继续使用相同的计划(似乎已在SQL Server 2012 RTM Express上进行了测试)。

这里有一定程度的灵活性。XML计划的宽泛形状用于指导最终计划的搜索(尽管许多属性被完全忽略,例如,交换中的分区类型),并且正常的搜索规则也相当宽松。例如,禁用了基于成本考虑的替代方案的早期修剪,允许显式引入交叉联接,并且忽略标量运算。

有太多细节无法深入,但是不能强行放置Filters和Compute Scalars,并且对形式的谓词进行了column = value概括,因此包含X = 1X = @X可用于包含X = 502或的查询的计划X = @Y。这种特殊的灵活性可以极大地帮助您找到一个自然的计划。

在特定的示例中,恒定的Union All始终可以实现为恒定扫描。所有联盟的投入数量无关紧要。


3

没有办法(SQL Server直到2012年的版本)针对CTE的两次出现重复使用单个后台处理程序。详细信息可以在SQLKiwi的答案中找到。再往下两次实现CTE的两种方法,这对于查询的性质是不可避免的。两种选择的净引导数均为6。

Martin的评论与Quassnoi的网站(有关指导CTE的计划)博客上的链接是此问题的部分启发。它描述了一种为相关子查询目的实现CTE的方法,尽管相关性可能导致多次对其进行评估,但该子查询仅被引用一次。这不适用于问题中的查询。

选项1-计划指南

从SQLKiwi的答案中得到一些提示,我将指南缩减到了最低限度,该最低限度仍然可以完成工作,例如,ConstantScan节点仅列出了2个标量运算符,它们可以充分扩展到任意数量。

;with cte(guid,other) as (
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other
OPTION(USE PLAN
N'<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.2" Build="11.0.2100.60" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
  <BatchSequence>
    <Batch>
      <Statements>
        <StmtSimple StatementCompId="1" StatementEstRows="1600" StatementId="1" StatementOptmLevel="FULL" StatementOptmEarlyAbortReason="GoodEnoughPlanFound" StatementSubTreeCost="0.0444433" StatementText="with cte(guid,other) as (&#xD;&#xA;  select newid(),1 union all&#xD;&#xA;  select newid(),2 union all&#xD;&#xA;  select newid(),3&#xD;&#xA;select a.guid, a.other, b.guid guidb, b.other otherb&#xD;&#xA;from cte a&#xD;&#xA;cross join cte b&#xD;&#xA;order by a.other, b.other;&#xD;&#xA;" StatementType="SELECT" QueryHash="0x43D93EF17C8E55DD" QueryPlanHash="0xF8E3B336792D84" RetrievedFromCache="true">
          <StatementSetOptions ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="true" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="true" />
          <QueryPlan NonParallelPlanReason="EstimatedDOPIsOne" CachedPlanSize="96" CompileTime="13" CompileCPU="13" CompileMemory="1152">
            <MemoryGrantInfo SerialRequiredMemory="0" SerialDesiredMemory="0" />
            <OptimizerHardwareDependentProperties EstimatedAvailableMemoryGrant="157240" EstimatedPagesCached="1420" EstimatedAvailableDegreeOfParallelism="1" />
            <RelOp AvgRowSize="47" EstimateCPU="0.006688" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="1600" LogicalOp="Inner Join" NodeId="0" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="0.0444433">
              <OutputList>
                <ColumnReference Column="Union1163" />
              </OutputList>
              <Warnings NoJoinPredicate="true" />
              <NestedLoops Optimized="false">
                <RelOp AvgRowSize="27" EstimateCPU="0.000432115" EstimateIO="0.0112613" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Sort" NodeId="1" Parallel="false" PhysicalOp="Sort" EstimatedTotalSubtreeCost="0.0117335">
                  <OutputList>
                    <ColumnReference Column="Union1080" />
                    <ColumnReference Column="Union1081" />
                  </OutputList>
                  <MemoryFractions Input="0" Output="0" />
                  <Sort Distinct="false">
                    <OrderBy>
                      <OrderByColumn Ascending="true">
                        <ColumnReference Column="Union1081" />
                      </OrderByColumn>
                    </OrderBy>
                    <RelOp AvgRowSize="27" EstimateCPU="4.0157E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Constant Scan" NodeId="2" Parallel="false" PhysicalOp="Constant Scan" EstimatedTotalSubtreeCost="4.0157E-05">
                      <OutputList>
                        <ColumnReference Column="Union1080" />
                        <ColumnReference Column="Union1081" />
                      </OutputList>
                      <ConstantScan>
                        <Values>
                          <Row>
                            <ScalarOperator ScalarString="newid()">
                              <Intrinsic FunctionName="newid" />
                            </ScalarOperator>
                            <ScalarOperator ScalarString="(1)">
                              <Const ConstValue="(1)" />
                            </ScalarOperator>
                          </Row>
                          <Row>
                            <ScalarOperator ScalarString="newid()">
                              <Intrinsic FunctionName="newid" />
                            </ScalarOperator>
                            <ScalarOperator ScalarString="(2)">
                              <Const ConstValue="(2)" />
                            </ScalarOperator>
                          </Row>
                        </Values>
                      </ConstantScan>
                    </RelOp>
                  </Sort>
                </RelOp>
                <RelOp AvgRowSize="27" EstimateCPU="0.0001074" EstimateIO="0.01" EstimateRebinds="0" EstimateRewinds="39" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Lazy Spool" NodeId="83" Parallel="false" PhysicalOp="Table Spool" EstimatedTotalSubtreeCost="0.0260217">
                  <OutputList>
                    <ColumnReference Column="Union1162" />
                    <ColumnReference Column="Union1163" />
                  </OutputList>
                  <Spool>
                    <RelOp AvgRowSize="27" EstimateCPU="0.000432115" EstimateIO="0.0112613" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Sort" NodeId="84" Parallel="false" PhysicalOp="Sort" EstimatedTotalSubtreeCost="0.0117335">
                      <OutputList>
                        <ColumnReference Column="Union1162" />
                        <ColumnReference Column="Union1163" />
                      </OutputList>
                      <MemoryFractions Input="0" Output="0" />
                      <Sort Distinct="false">
                        <OrderBy>
                          <OrderByColumn Ascending="true">
                            <ColumnReference Column="Union1163" />
                          </OrderByColumn>
                        </OrderBy>
                        <RelOp AvgRowSize="27" EstimateCPU="4.0157E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="40" LogicalOp="Constant Scan" NodeId="85" Parallel="false" PhysicalOp="Constant Scan" EstimatedTotalSubtreeCost="4.0157E-05">
                          <OutputList>
                            <ColumnReference Column="Union1162" />
                            <ColumnReference Column="Union1163" />
                          </OutputList>
                          <ConstantScan>
                            <Values>
                              <Row>
                                <ScalarOperator ScalarString="newid()">
                                  <Intrinsic FunctionName="newid" />
                                </ScalarOperator>
                                <ScalarOperator ScalarString="(1)">
                                  <Const ConstValue="(1)" />
                                </ScalarOperator>
                              </Row>
                              <Row>
                                <ScalarOperator ScalarString="newid()">
                                  <Intrinsic FunctionName="newid" />
                                </ScalarOperator>
                                <ScalarOperator ScalarString="(2)">
                                  <Const ConstValue="(2)" />
                                </ScalarOperator>
                              </Row>
                            </Values>
                          </ConstantScan>
                        </RelOp>
                      </Sort>
                    </RelOp>
                  </Spool>
                </RelOp>
              </NestedLoops>
            </RelOp>
          </QueryPlan>
        </StmtSimple>
      </Statements>
    </Batch>
  </BatchSequence>
</ShowPlanXML>'
);

选项2-远程扫描

通过增加查询费用并引入远程扫描,结果得以实现。

with cte(guid,other) as (
  select *
  from OPENQUERY([TESTSQL\V2012], '
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3') x)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other;

2

说真的,您不能从头开始制定xml执行计划。使用SSIS创建它们是科幻小说。是的,所有都是XML,但是它们来自不同的领域。在Paul关于该主题的博客中,他说的是“ SSIS允许的方式...”,所以可能您误解了吗?我不认为他说:“使用SSIS创建计划”,而是“不会是伟大的,是能够创建使用拖放计划式界面类似 SSIS之 ”。也许,对于一个非常简单的查询,您可以进行管理,但这很费力,甚至可能浪费时间。您可能会说繁忙的工作。

如果要为USE PLAN提示或计划指南创建计划,则有两种方法。例如,我可能会从表中删除记录(例如,数据库副本上的记录)以影响统计信息并鼓励优化器做出不同的决定。我还使用了表变量而不是查询中的所有表,因此优化器认为每个表都包含1条记录。然后在生成的计划中,将所有表变量替换为原始表名,并将其交换为计划。另一个选择是使用UPDATE STATISTICS的WITH STATS_STREAM选项来欺骗统计信息,这是克隆数据库的仅统计信息副本时使用的方法,例如

UPDATE STATISTICS 
    [dbo].[yourTable]([PK_yourTable]) 
WITH 
    STATS_STREAM = 0x0100etc, 
    ROWCOUNT = 10000, 
    PAGECOUNT = 93

过去,我花了一些时间修改xml执行计划,但最后发现,SQL只是显示“我没有使用它”,并且无论如何都要运行查询。

对于您的特定示例,我确定您知道可以在查询中使用set rowcount 3或TOP 3来获得该结果,但是我想这不是您的意思。将正确答案真的会:使用临时表。我会给予好评的是:)不是一个正确的答案应该是“花费数小时甚至数天削减了自己的自定义XML执行计划,您尝试将optimzer诱骗做的CTE一个懒惰的阀芯,其甚至可能不反正工作,看起来聪明但也无法维持”。

只是我的意见,不要在这里造成破坏性影响-希望能有所帮助。


说真的,XML计划是可忽略的?!,我认为这就是重点吗?如果它们无效,则应抛出。
crokusek

我指的是“计划指南不成功”事件。
wBob

2

有没有任何办法......

最后在SQL 2016 CTP 3.0中有一种方法,:)

使用Dmitry Pilugin 此处详细介绍的跟踪标志和扩展事件,您可以(任意地)从查询执行的中间阶段中找出三个唯一的Guid 。

注意:此代码不适用于CTE计划强制的生产或严重使用,仅是对新的跟踪标志的轻松了解和不同的处理方式:

-- Configure the XEvents session; with ring buffer target so we can collect it
CREATE EVENT SESSION [query_trace_column_values] ON SERVER 
ADD EVENT sqlserver.query_trace_column_values
ADD TARGET package0.ring_buffer( SET max_memory = 2048 )
WITH ( MAX_MEMORY = 4096 KB, EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY = 30 SECONDS, MAX_EVENT_SIZE = 0 KB, MEMORY_PARTITION_MODE = NONE, TRACK_CAUSALITY = OFF , STARTUP_STATE = OFF )
GO

-- Start the session
ALTER EVENT SESSION [query_trace_column_values] ON SERVER
STATE = START;
GO

-- Run the query, including traceflag
DBCC TRACEON(2486);
SET STATISTICS XML ON;
GO

-- Original query
;with cte(guid,other) as (
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other
option ( recompile )
go

SET STATISTICS XML OFF;
DBCC TRACEOFF(2486);
GO

DECLARE @target_data XML

SELECT @target_data = CAST( target_data AS XML )
FROM sys.dm_xe_sessions AS s 
    INNER JOIN sys.dm_xe_session_targets AS t ON t.event_session_address = s.address
WHERE s.name = 'query_trace_column_values'


--SELECT @target_data td

-- Arbitrarily fish out 3 unique guids from intermediate stage of the query as collected by XEvent session
;WITH cte AS
(
SELECT
    n.c.value('(data[@name = "row_id"]/value/text())[1]', 'int') row_id,
    n.c.value('(data[@name = "column_value"]/value/text())[1]', 'char(36)') [guid]
FROM @target_data.nodes('//event[data[@name="column_id"]/value[. = 1]][data[@name="row_number"]/value[. < 4]][data[@name="node_name"]/value[. = "Nested Loops"]]') n(c)
)
SELECT *
FROM cte a
    CROSS JOIN cte b
GO

-- Stop the session
ALTER EVENT SESSION [query_trace_column_values] ON SERVER
STATE = STOP;
GO

-- Drop the session
IF EXISTS ( select * from sys.server_event_sessions where name = 'query_trace_column_values' )
DROP EVENT SESSION [query_trace_column_values] ON SERVER 
GO

在版本(CTP3.2)-13.0.900.73(x64)上进行了测试,只是为了好玩。


1

我发现traceflag 8649(强制并行计划)在我的2008,R2和2012实例上的左侧guid列上引起了这种行为。我不需要在CTE正常运行的SQL 2005上使用该标志。我尝试在较高实例中使用SQL 2005中生成的计划,但该计划无法验证。

with cte(guid,other) as (
  select newid(),1 union all
  select newid(),2 union all
  select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other
option ( querytraceon 8649 )

使用提示,使用包含提示的计划指南或使用由查询生成的计划(在USE PLAN中都带有提示)均有效。 cte newid


感谢您再试一次。在2008/2012上使用或不使用该跟踪标志,查询看起来都没有什么不同。不确定是我的SQL Server实例还是您要显示的实例。我仍然看到18个向导。你看到了什么?
孔夫子2012年

左侧有3个不同的向导(向导列),每个向导重复3次。右侧有9个独特的guid(guidb列),因此至少左侧显示了您想要的笑声。我已经在其他答案中添加了图片,希望可以澄清一下。小步骤。我还应该注意,在SQL 2005中,我得到了6个唯一的向导,左侧3个,右侧3个。
wBob 2012年

还只是注意到,删除“全部”也会得到6个唯一的引导,每侧3个。
wBob 2012年

可以使跟踪标志不工作具有服务器MAXDOP 1
wBob
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.