PARTITION BYなしのROW_NUMBER()でもセグメントイテレータが生成される


11

私は、ランク付けおよび集計ウィンドウ関数、特にセグメントおよびシーケンスプロジェクトイテレータに関する私のブログ投稿を書いています。私が理解している方法は、Segmentがグループの終了/開始を構成するストリーム内の行を識別するため、次のクエリです。

SELECT ROW_NUMBER() OVER (PARTITION BY someGroup ORDER BY someOrder)

セグメントを使用して、行が前の行以外の別のグループに属していることを通知します。次に、Sequence Projectイテレーターは、Segmentイテレーターの出力に基づいて、実際の行番号を計算します。

ただし、そのロジックを使用する次のクエリには、セグメント式がないため、セグメントを含める必要はありません。

SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)

ただし、この仮説を試すと、これらのクエリは両方ともセグメント演算子を使用します。唯一の違いは、2番目のクエリはGroupByセグメントにa を必要としないことです。そもそもセグメントの必要性を排除していませんか?

CREATE TABLE dbo.someTable (
    someGroup   int NOT NULL,
    someOrder   int NOT NULL,
    someValue   numeric(8, 2) NOT NULL,
    PRIMARY KEY CLUSTERED (someGroup, someOrder)
);

--- Query 1:
SELECT ROW_NUMBER() OVER (PARTITION BY someGroup ORDER BY someOrder)
FROM dbo.someTable;

--- Query 2:
SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)
FROM dbo.someTable;

1
パーティション式はありませんが、この場合は1つだけですが、技術的には結果セットをパーティションに分割していると思いますか?
Mark Sinkinson、2014

QPは空を表示する<GroupBy />ため、セグメントは実際には何もしません。ほとんどの場合、セグメント列をシーケンスプロジェクトオペレーターに出力します。セグメントオペレーターが存在する理由は、シーケンスプロジェクトオペレーターが作業を行うためにその値を必要とするためです。
ミカエルエリクソン2014

それも私の理論です。しかし、オプティマイザーは通常、これらの種類の不要な演算子を排除します。imho..
Daniel Hutmacher

回答:


12

同じ動作について言及しているこの6年前のブログ投稿を見つけました。

使用されているROW_NUMBER()かどうかにかかわらPARTITION BYず、常にセグメント演算子が含まれているように見えます。推測しなければならないのは、エンジンでクエリプランを簡単に作成できるためです。

ほとんどの場合にセグメントが必要であり、それが不要な場合は、本質的にゼロコストの非演算であるので、ウィンドウ関数が使用されているときは常にそれを計画に含めるだけの方がはるかに簡単です。


11

実行計画のshowplan.xsdによると、属性または属性GroupByなしで表示されるため、デフォルトで[1..1]になり、要素は必須ではなく、必ずしもコンテンツではありません。タイプ()の子要素には0と無制限[0 .. *]があり、それをオプションにして、空の要素を許可します。を手動で削除してプランを強制しようとすると、予期したエラーが発生します。minOccursmaxOccursColumnReferenceColumnReferenceTypeminOccursmaxOccursGroupBy

Msg 6965, Level 16, State 1, Line 29
XML Validation: Invalid content. Expected element(s): '{http://schemas.microsoft.com/sqlserver/2004/07/showplan}GroupBy','{http://schemas.microsoft.com/sqlserver/2004/07/showplan}DefinedValues','{http://schemas.microsoft.com/sqlserver/2004/07/showplan}InternalInfo'. Found: element '{http://schemas.microsoft.com/sqlserver/2004/07/showplan}SegmentColumn' instead. Location: /*:ShowPlanXML[1]/*:BatchSequence[1]/*:Batch[1]/*:Statements[1]/*:StmtSimple[1]/*:QueryPlan[1]/*:RelOp[1]/*:SequenceProject[1]/*:RelOp[1]/*:Segment[1]/*:SegmentColumn[1].

興味深いことに、手動でセグメント演算子を削除して、次のような強制の有効な計画を取得できることを発見しました。

ここに画像の説明を入力してください

ただし、そのプランで実行すると(を使用してOPTION ( USE PLAN ... ))、セグメントオペレーターが魔法のように再表示されます。オプティマイザが大まかなガイドとしてXMLプランのみを使用することを示すために行きます。

私のテストリグ:

USE tempdb
GO
SET NOCOUNT ON
GO
IF OBJECT_ID('dbo.someTable') IS NOT NULL DROP TABLE dbo.someTable
GO
CREATE TABLE dbo.someTable (
    someGroup   int NOT NULL,
    someOrder   int NOT NULL,
    someValue   numeric(8, 2) NOT NULL,
    PRIMARY KEY CLUSTERED (someGroup, someOrder)
);
GO

-- Generate some dummy data
;WITH cte AS (
SELECT TOP 1000 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
    CROSS JOIN master.sys.columns c2
    CROSS JOIN master.sys.columns c3
)
INSERT INTO dbo.someTable ( someGroup, someOrder, someValue )
SELECT rn % 333, rn % 444, rn % 55
FROM cte
GO


-- Try and force the plan
SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)
FROM dbo.someTable
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="12.0.2000.8" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
  <BatchSequence>
    <Batch>
      <Statements>
        <StmtSimple StatementCompId="1" StatementEstRows="1000" StatementId="1" StatementOptmLevel="TRIVIAL" CardinalityEstimationModelVersion="120" StatementSubTreeCost="0.00596348" StatementText="SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)&#xD;&#xA;FROM dbo.someTable" StatementType="SELECT" QueryHash="0x193176312402B8E7" QueryPlanHash="0x77F1D72C455025A4" 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 DegreeOfParallelism="1" CachedPlanSize="16" CompileTime="0" CompileCPU="0" CompileMemory="88">
            <OptimizerHardwareDependentProperties EstimatedAvailableMemoryGrant="131072" EstimatedPagesCached="65536" EstimatedAvailableDegreeOfParallelism="4" />
            <RelOp AvgRowSize="15" EstimateCPU="8E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="1000" LogicalOp="Compute Scalar" NodeId="0" Parallel="false" PhysicalOp="Sequence Project" EstimatedTotalSubtreeCost="0.00596348">
              <OutputList>
                <ColumnReference Column="Expr1002" />
              </OutputList>
              <SequenceProject>
                <DefinedValues>
                  <DefinedValue>
                    <ColumnReference Column="Expr1002" />
                    <ScalarOperator ScalarString="row_number">
                      <Sequence FunctionName="row_number" />
                    </ScalarOperator>
                  </DefinedValue>
                </DefinedValues>

                <!-- Segment operator completely removed from plan -->
                <!--<RelOp AvgRowSize="15" EstimateCPU="2E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="1000" LogicalOp="Segment" NodeId="1" Parallel="false" PhysicalOp="Segment" EstimatedTotalSubtreeCost="0.00588348">
                  <OutputList>
                    <ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[someTable]" Column="someGroup" />
                    <ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[someTable]" Column="someOrder" />
                    <ColumnReference Column="Segment1003" />
                  </OutputList>
                  <Segment>
                    <GroupBy />
                    <SegmentColumn>
                      <ColumnReference Column="Segment1003" />
                    </SegmentColumn>-->


                    <RelOp AvgRowSize="15" EstimateCPU="0.001257" EstimateIO="0.00460648" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="1000" LogicalOp="Clustered Index Scan" NodeId="0" Parallel="false" PhysicalOp="Clustered Index Scan" EstimatedTotalSubtreeCost="0.00586348" TableCardinality="1000">
                      <OutputList>
                        <ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[someTable]" Column="someGroup" />
                        <ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[someTable]" Column="someOrder" />
                      </OutputList>
                      <IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" ForceScan="false" NoExpandHint="false" Storage="RowStore">
                        <DefinedValues>
                          <DefinedValue>
                            <ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[someTable]" Column="someGroup" />
                          </DefinedValue>
                          <DefinedValue>
                            <ColumnReference Database="[tempdb]" Schema="[dbo]" Table="[someTable]" Column="someOrder" />
                          </DefinedValue>
                        </DefinedValues>
                        <Object Database="[tempdb]" Schema="[dbo]" Table="[someTable]" Index="[PK__someTabl__7CD03C8950FF62C1]" IndexKind="Clustered" Storage="RowStore" />
                      </IndexScan>
                    </RelOp>

                <!--</Segment>
                </RelOp>-->
              </SequenceProject>
            </RelOp>

          </QueryPlan>
        </StmtSimple>
      </Statements>
    </Batch>
  </BatchSequence>
</ShowPlanXML>' )

テストリグからXMLプランを切り出し、それを.sqlplanとして保存して、プランからセグメントを差し引いて表示します。

PS私は、SQLプランを手動で切り刻むことに時間をかけすぎないようにしています。まるで、私がそれを時間のかかる忙しい仕事であり、私が決してやらないと思っていることを知っているようです。ちょっと待って!?:)


手に持っている時間が長すぎます...いい仕事です!
Mark Sinkinson、2014

マークに同意する。頼んでもいなかったことを学んでいます。ありがとう!:)
Daniel Hutmacher 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.