ROW_NUMBER () โดยไม่มี PARTITION BY ยังคงสร้างตัวทำซ้ำของเซกเมนต์


11

ฉันกำลังเขียนเกี่ยวกับโพสต์บล็อกที่กำลังจะมาถึงของฉันเกี่ยวกับการจัดอันดับและฟังก์ชั่นหน้าต่างรวมโดยเฉพาะการวนซ้ำของโครงการเซกเมนต์และลำดับ วิธีที่ฉันเข้าใจว่าเซ็กเมนต์ระบุแถวในสตรีมที่ประกอบด้วยจุดสิ้นสุด / จุดเริ่มต้นของกลุ่มดังนั้นเคียวรีต่อไปนี้:

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

จะใช้เซกเมนต์เพื่อบอกว่าแถวอยู่ในกลุ่มที่แตกต่างจากแถวก่อนหน้าหรือไม่ ตัววนซ้ำ Project Sequence จะทำการคำนวณจำนวนแถวตามจริงโดยขึ้นอยู่กับเอาต์พุตของเอาต์พุตของตัววนซ้ำของเซกเมนต์

แต่เคียวรีต่อไปนี้ซึ่งใช้ตรรกะนั้นไม่ควรรวมเซกเมนต์เนื่องจากไม่มีนิพจน์พาร์ติชัน

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
แม้ว่าจะไม่มีการแสดงออกของพาร์ติชัน แต่ฉันคิดว่าคุณยังคงแยกชุดผลลัพธ์ออกทางเทคนิคเป็นพาร์ติชันแม้ว่าจะมีเพียงหนึ่งในกรณีนี้หรือไม่
ทำเครื่องหมาย Sinkinson

QP แสดงค่าว่าง<GroupBy />ดังนั้นเซกเมนต์ไม่ได้ทำอะไรเลยเกือบจะเอาท์พุทคอลัมน์เซ็กเมนต์ไปยังโอเปอเรเตอร์โครงการลำดับ เหตุผลที่ผู้ประกอบการส่วนที่จะมีอาจเป็นไปได้ว่าผู้ประกอบการโครงการลำดับต้องการค่าที่จะทำงานของมัน
Mikael Eriksson

นั่นคือทฤษฎีของฉันเช่นกัน แต่เครื่องมือเพิ่มประสิทธิภาพมักจะหายไปกับผู้ประกอบการที่ไม่จำเป็นประเภทนี้ imho ..
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 ... )) Segment Operator จะปรากฏขึ้นอีกครั้งอย่างน่าอัศจรรย์ เพียงไปเพื่อแสดงเครื่องมือเพิ่มประสิทธิภาพจะใช้แผน 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 เพื่อดูแผนลบส่วน

ป.ล. ฉันจะไม่ใช้เวลามากในการสับเปลี่ยนแผนการ SQL ด้วยตนเองราวกับว่าคุณรู้จักฉันคุณจะรู้ว่าฉันคิดว่ามันเป็นงานยุ่ง ๆ ที่กินเวลาและสิ่งที่ฉันไม่เคยทำ โอ้โห! :)


คุณมีเวลามากเกินไปในมือของคุณ ... ทำได้ดีมาก!
ทำเครื่องหมาย Sinkinson

เห็นด้วยกับมาร์ค ฉันกำลังเรียนรู้สิ่งต่าง ๆ ที่ฉันไม่ได้คิดจะขอ ขอบคุณ! :)
Daniel Hutmacher
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.