PARTITION BY가없는 ROW_NUMBER ()는 여전히 세그먼트 반복자를 생성합니다.


11

나는 순위 및 집계 창 기능, 특히 세그먼트 및 시퀀스 프로젝트 반복자에 대한 내 블로그 게시물을 작성하고 있습니다. 내가 이해하는 방식은 세그먼트가 그룹의 끝 / 시작을 구성하는 스트림의 행을 식별하므로 다음과 같은 쿼리입니다.

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

세그먼트를 사용하여 행이 이전 행이 아닌 다른 그룹에 속하는지 알려줍니다. 그런 다음 시퀀스 프로젝트 반복자는 세그먼트 반복기 출력의 출력을 기반으로 실제 행 번호 계산을 수행합니다.

그러나 해당 논리를 사용하는 다음 쿼리는 파티션 표현식이 없으므로 세그먼트를 포함하지 않아도됩니다.

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

그러나이 가설을 시도하면이 두 쿼리 모두 세그먼트 연산자를 사용합니다. 유일한 차이점은 두 번째 쿼리가 GroupBy세그먼트에서 필요하지 않다는 것 입니다. 그것이 세그먼트의 필요성을 먼저 제거하지 않습니까?

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
파티션 표현은 없지만이 경우 하나만 있지만 결과적으로 기술적으로 결과 집합을 파티션으로 분할하는 것 같습니다.
Mark Sinkinson

QP는 비어 있음을 표시 <GroupBy />하므로 세그먼트는 실제로 아무 것도 수행하지 않으며, 세그먼트 열을 시퀀스 프로젝트 연산자에 출력합니다. 세그먼트 연산자가 존재하는 이유는 시퀀스 프로젝트 연산자가 작업을 수행하기 위해 해당 값이 필요하기 때문일 수 있습니다.
Mikael Eriksson

저의 이론이기도합니다. 그러나 옵티마이 저는 일반적으로 이러한 종류의 불필요한 연산자를 제거합니다.
Daniel Hutmacher

답변:


12

나는이 6 살짜리 블로그 게시물에서 같은 행동을 언급했다.

사용 ROW_NUMBER()여부에 관계없이 항상 세그먼트 연산자가 포함 된 것 같습니다 PARTITION BY. 추측해야한다면 엔진에서 쿼리 계획을 쉽게 만들 수 있기 때문입니다.

세그먼트가 대부분의 경우에 필요하고, 필요하지 않은 경우 본질적으로 비용이 들지 않는 비 작동 인 경우, 윈도 잉 기능을 사용할 때 항상 계획에 포함시키는 것이 훨씬 간단합니다.


11

에 따르면 showplan.xsd 실행 계획, GroupBy없이 나타납니다 minOccurs또는 maxOccurs요소 강제하기 때문에 [1..1]에 기본 속성, 반드시 만족하지. ColumnReference( ColumnReferenceType) 유형 의 하위 요소 는 minOccurs0이며 maxOccurs제한이없는 [0 .. *]이므로 선택 사항 이므로 빈 요소가 허용됩니다. GroupBy계획 을 수동으로 제거 하고 강제로 시도 하면 예상 오류가 발생합니다.

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

마크에 동의하십시오. 내가 생각조차하지 않은 것들을 배우고 있습니다. 감사! :)
Daniel Hutmacher 2014
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.