PARTITION BY içermeyen ROW_NUMBER () hala Segment yineleyici oluşturuyor


11

Sıralaması ve toplu pencere işlevleri, özellikle Segment ve Dizi Projesi yineleyicileri hakkında benim yaklaşan bir blog yazı üzerine yazıyorum. Bunu anlamanın yolu, Segmentin bir grubun sonunu / başlangıcını oluşturan bir akıştaki satırları tanımlamasıdır, bu nedenle aşağıdaki sorgu:

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

Bir satırın ne zaman önceki satırdan farklı bir gruba ait olduğunu söylemek için Segment'i kullanır. Sıralı Proje yineleyicisi daha sonra Segment yineleyicisinin çıktısının çıktısına bağlı olarak gerçek satır numarası hesaplamasını yapar.

Ancak bu mantığı kullanan aşağıdaki sorgunun bir Segment içermesi gerekmez, çünkü bölüm ifadesi yoktur.

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

Ancak, bu hipotezi denediğimde, her iki sorgu da bir Segment işleci kullanır. Tek fark, ikinci sorgunun GroupBySegment'te bir a'ya ihtiyaç duymamasıdır . Bu öncelikle Segment ihtiyacını ortadan kaldırmaz mı?

Misal

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
Herhangi bir bölüm ifadesi olmamasına rağmen, sanırım sonuç kümesini teknik olarak bölümlere ayırıyorsunuz, bu durumda yalnızca bir tane de olsa?
Mark Sinkinson

QP boş bir <GroupBy />bölüm gösterir, böylece segment gerçekten hiçbir şey yapmaz, segment sütununu Sekans Projesi işlecine çıkarır. Segment operatörünün orada olmasının nedeni, Dizi Projesi operatörünün çalışması için bu değere ihtiyaç duyması olabilir.
Mikael Eriksson

Benim teorim de bu. Ama optimizer genellikle bu tür gereksiz operatörleri, imho .. ortadan kaldırır
Daniel Hutmacher

Yanıtlar:


12

Aynı davranıştan söz eden bu 6 yaşındaki blog gönderisini buldum.

Kullanılsın veya kullanılmasın ROW_NUMBER()her zaman bir segment operatörü içerir gibi görünüyor PARTITION BY. Tahmin etmeliydim, bunun nedeni motorda bir sorgu planı oluşturmayı kolaylaştırmasıdır.

Çoğu durumda segmente ihtiyaç duyuluyorsa ve ihtiyaç duyulmadığı durumlarda, esasen sıfır maliyetli bir işlem değildir, bir pencere işlevi kullanıldığında her zaman plana dahil etmek çok daha kolaydır.


11

Yürütme planı için showplan.xsd'ye göre, bu GroupByolmadan görünür minOccursveya maxOccursbu nedenle [1..1] öğesinin zorunlu olması, içeriği zorunlu olarak zorunlu kılma özniteliklerine sahip değildir. ColumnReference( ColumnReferenceType) Türünün alt öğesinin minOccurs0 değeri vardır ve maxOccurssınırsız [0 .. *] vardır, bu da onu isteğe bağlı yapar , bu nedenle izin verilen boş öğedir. GroupByPlanı el ile kaldırmaya ve zorlamaya çalışırsanız beklenen hatayı alırsınız:

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].

İlginç bir şekilde, Segment operatörünü, şunun gibi görünen geçerli bir plan almak için manuel olarak kaldırabileceğinizi buldum:

resim açıklamasını buraya girin

Ancak bu planla (kullanarak OPTION ( USE PLAN ... )) çalıştırdığınızda , Segment Operatörü sihirli bir şekilde yeniden görünür. Sadece optimize edicinin sadece kaba bir kılavuz olarak XML planlarını aldığını göstermeye gider.

Test donanımım:

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>' )

Test planından XML planını kesin ve planı eksi Segmenti görüntülemek için .sqlplan olarak kaydedin.

PS Ben SQL planları etrafında doğrama gibi çok fazla zaman harcamak olmaz gibi beni bilir gibi zaman yeme meşgul çalışma ve asla yapmak bir şey olarak kabul biliyorum. Bekle !? :)


Ellerinde çok fazla zamanın var ... İyi iş çıkardın!
Mark Sinkinson

Mark ile aynı fikirde. Sormayı bile düşünmediğim şeyler öğreniyorum. Teşekkürler! :)
Daniel Hutmacher
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.