ปัญหาประสิทธิภาพการค้นหา SQL แบบเรียกซ้ำ [ปิด]


9

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

นามธรรม

ฉันมีฐานข้อมูลทำให้สามารถจัดเก็บข้อมูลได้อย่างเป็นธรรมชาติและมีคุณสมบัติที่ไม่ได้มาตรฐานหลายอย่างที่กระบวนการทางธุรกิจของฉันต้องการ คุณสมบัติดังต่อไปนี้:

  1. การอัปเดต / ลบแบบไม่ทำลายและไม่ปิดกั้นถูกนำไปใช้ผ่านวิธีแทรกอย่างเดียวซึ่งช่วยให้การกู้คืนข้อมูลและการบันทึกอัตโนมัติ (การเปลี่ยนแปลงแต่ละครั้งจะเชื่อมโยงกับผู้ใช้ที่ทำการเปลี่ยนแปลงนั้น)
  2. ข้อมูลหลายตัวแปร (อาจมีข้อมูลเดียวกันหลายรุ่น)
  3. สิทธิ์ระดับฐานข้อมูล
  4. ความสอดคล้องในที่สุดกับข้อกำหนด ACID และสร้าง / ปรับปรุง / ลบรายการที่ปลอดภัย
  5. ความสามารถในการย้อนกลับหรือส่งต่อมุมมองข้อมูลปัจจุบันของคุณไปยังจุดใดก็ได้อย่างรวดเร็ว

อาจมีคุณสมบัติอื่น ๆ ที่ฉันลืมพูดถึง

โครงสร้างฐานข้อมูล

ข้อมูลผู้ใช้ทั้งหมดจะถูกเก็บไว้ในItemsตารางเป็นสตริงที่เข้ารหัส JSON ( ntext) การดำเนินการฐานข้อมูลทั้งหมดดำเนินการผ่านสองขั้นตอนการจัดเก็บGetLatestและInsertSnashotอนุญาตให้ดำเนินการกับข้อมูลคล้ายกับวิธีการที่ GIT ดำเนินการกับไฟล์ต้นฉบับ

ข้อมูลผลลัพธ์ถูกเชื่อมโยง (รวมเข้าด้วยกัน) ที่ส่วนหน้าลงในกราฟที่เชื่อมโยงอย่างสมบูรณ์ดังนั้นจึงไม่จำเป็นต้องทำการสืบค้นฐานข้อมูลในกรณีส่วนใหญ่

นอกจากนี้ยังสามารถเก็บข้อมูลในคอลัมน์ SQL ปกติแทนที่จะเก็บไว้ในรูปแบบที่เข้ารหัส Json อย่างไรก็ตามมันเพิ่มความเครียดความซับซ้อนโดยรวม

กำลังอ่านข้อมูล

GetLatestผลลัพธ์ที่มีข้อมูลในรูปแบบของคำแนะนำให้พิจารณาแผนภาพต่อไปนี้เพื่ออธิบาย:

แผนภาพโครงสร้าง

แผนภาพแสดงวิวัฒนาการของการเปลี่ยนแปลงที่เคยทำกับบันทึกเดียว ลูกศรบนไดอะแกรมแสดงเวอร์ชันตามการแก้ไขที่เกิดขึ้น (ลองนึกภาพว่าผู้ใช้กำลังอัปเดตข้อมูลบางอย่างออฟไลน์ขนานไปกับการอัปเดตที่สร้างโดยผู้ใช้ออนไลน์กรณีดังกล่าวจะแนะนำความขัดแย้งซึ่งโดยทั่วไปคือข้อมูลสองรุ่น แทนหนึ่ง)

ดังนั้นการโทรGetLatestภายในช่วงเวลาป้อนข้อมูลต่อไปนี้จะส่งผลให้เวอร์ชันบันทึกดังต่อไปนี้:

GetLatest 0, 15  => 1       <= The data is created upon it's first occurance
GetLatest 0, 25  => 2       <= Inserting another version on top of first one overwrites the existing version
GetLatest 0, 30  => 3       <= The overwrite takes place as soon as the data is inserted
GetLatest 0, 45  => 3, 4    <= This is where the conflict is introduced in the system
GetLatest 0, 55  => 4, 5    <= You can still edit all the versions
GetLatest 0, 65  => 4, 6    <= You can still edit all the versions
GetLatest 0, 75  => 4, 6, 7 <= You can also create additional conflicts
GetLatest 0, 85  => 4, 7, 8 <= You can still edit records
GetLatest 0, 95  => 7, 8, 9 <= You can still edit records
GetLatest 0, 105 => 7, 8    <= Inserting a record with `Json` equal to `NULL` means that the record is deleted
GetLatest 0, 115 => 8       <= Deleting the conflicting versions is the only conflict-resolution scenario
GetLatest 0, 125 => 8, X    <= The conflict can be based on the version that was already deleted.
GetLatest 0, 135 => 8, Y    <= You can delete such version too and both undelete another version on parallel within one Snapshot (or in several Snapshots).
GetLatest 0, 145 => 8       <= You can delete the undeleted versions by inserting NULL.
GetLatest 0, 155 => 8, Z    <= You can again undelete twice-deleted versions
GetLatest 0, 165 => 8       <= You can again delete three-times deleted versions
GetLatest 0, 10000 => 8     <= This means that in order to fast-forward view from moment 0 to moment `10000` you just have to expose record 8 to the user.
GetLatest 55, 115  => 8, [Remove 4], [Remove 5] <= At moment 55 there were two versions [4, 5] so in order to fast-forward to moment 115 the user has to delete versions 4 and 5 and introduce version 8. Please note that version 7 is not present in results since at moment 110 it got deleted.

เพื่อให้GetLatestการสนับสนุนอินเตอร์เฟซที่มีประสิทธิภาพเช่นแต่ละระเบียนควรมีคุณลักษณะพิเศษบริการBranchId, RecoveredOn, CreatedOn, UpdatedOnPrev, UpdatedOnCurr, UpdatedOnNext, UpdatedOnNextIdที่ใช้โดยGetLatestจะคิดออกว่าจะบันทึกตกเพียงพอลงในช่วงเวลาที่มีให้สำหรับGetLatestข้อโต้แย้ง

การแทรกข้อมูล

เพื่อรองรับความสอดคล้องในที่สุดความปลอดภัยและประสิทธิภาพของการทำธุรกรรมข้อมูลจะถูกแทรกลงในฐานข้อมูลผ่านขั้นตอนหลายขั้นตอนพิเศษ

  1. ข้อมูลจะถูกแทรกลงในฐานข้อมูลโดยไม่สามารถสอบถามได้โดยGetLatestกระบวนงานที่เก็บไว้

  2. มีการจัดทำข้อมูลสำหรับGetLatestขั้นตอนการจัดเก็บข้อมูลจะพร้อมใช้งานในสถานะปกติ (เช่นdenormalized = 0) ในขณะที่ข้อมูลอยู่ในสถานะปกติสาขาบริการBranchId, RecoveredOn, CreatedOn, UpdatedOnPrev, UpdatedOnCurr, UpdatedOnNext, UpdatedOnNextIdมีการคำนวณที่ช้าจริงๆ

  3. เพื่อเร่งความเร็วในการจัดเก็บข้อมูลจะถูกทำให้เป็นมาตรฐานในทันทีที่มีการGetLatestจัดเก็บสำหรับกระบวนการ

    • ตั้งแต่ขั้นตอนที่ 1,2,3 ดำเนินการภายในธุรกรรมที่แตกต่างกันจึงเป็นไปได้ที่ความล้มเหลวของฮาร์ดแวร์อาจเกิดขึ้นในระหว่างการดำเนินการแต่ละครั้ง ปล่อยให้ข้อมูลอยู่ในสถานะสื่อกลาง สถานการณ์ดังกล่าวเป็นเรื่องปกติและแม้ว่าจะเกิดขึ้นข้อมูลจะหายเป็นปกติภายในการInsertSnapshotโทรครั้งต่อไป รหัสสำหรับส่วนนี้สามารถพบได้ในระหว่างขั้นตอนที่ 2 และ 3 ของInsertSnapshotขั้นตอนการจัดเก็บ

ปัญหา

คุณลักษณะใหม่ (จำเป็นโดยธุรกิจ) บังคับให้ฉัน refactor พิเศษDenormalizerมุมมองที่ความสัมพันธ์ขึ้นทั้งหมดที่มีด้วยกันและจะใช้สำหรับทั้งสองและGetLatest InsertSnapshotหลังจากนั้นฉันก็เริ่มประสบปัญหาด้านประสิทธิภาพ หากเริ่มแรกSELECT * FROM Denormalizerดำเนินการเพียงเสี้ยววินาทีดังนั้นตอนนี้มันใช้เวลาเกือบ 5 นาทีในการประมวลผล 10,000 บันทึก

ฉันไม่ใช่ DB pro และฉันใช้เวลาเกือบหกเดือนในการสร้างโครงสร้างฐานข้อมูลปัจจุบัน และฉันใช้เวลาสองสัปดาห์แรกในการทำ refactorings แล้วพยายามหาสาเหตุที่ทำให้เกิดปัญหาการทำงานของฉัน ฉันหามันไม่เจอ ฉันให้การสำรองฐานข้อมูล(ซึ่งคุณสามารถค้นหาได้ที่นี่)เนื่องจาก schema (ที่มีดัชนีทั้งหมด) มีขนาดค่อนข้างใหญ่เพื่อให้เหมาะกับ SqlFiddle ฐานข้อมูลยังมีข้อมูลที่ล้าสมัย (10,000+ รายการ) ที่ฉันใช้เพื่อการทดสอบ . นอกจากนี้ฉันกำลังจัดเตรียมข้อความสำหรับการDenormalizerดูที่ได้รับการปรับโครงสร้างใหม่และช้าลงอย่างเจ็บปวด:

ALTER VIEW [dbo].[Denormalizer]
AS
WITH Computed AS
(
    SELECT  currItem.Id,
            nextOperation.id AS NextId,
            prevOperation.FinishedOn AS PrevComputed,
            currOperation.FinishedOn AS CurrComputed,
            nextOperation.FinishedOn AS NextComputed

    FROM Items currItem 
    INNER JOIN dbo.Operations AS currOperation ON currItem.OperationId = currOperation.Id

    LEFT OUTER JOIN dbo.Items AS prevItem ON currItem.PreviousId = prevItem.Id
    LEFT OUTER JOIN dbo.Operations AS prevOperation ON prevItem.OperationId = prevOperation.Id 
    LEFT OUTER JOIN
    (
        SELECT MIN(I.id) as id, S.PreviousId, S.FinishedOn
        FROM Items I
        INNER JOIN
        (
            SELECT I.PreviousId, MIN(nxt.FinishedOn) AS FinishedOn
            FROM dbo.Items I
            LEFT OUTER JOIN dbo.Operations AS nxt ON I.OperationId = nxt.Id
            GROUP BY I.PreviousId
        ) AS S ON I.PreviousId = S.PreviousId 
        GROUP BY S.PreviousId, S.FinishedOn
    ) AS nextOperation ON nextOperation.PreviousId = currItem.Id

    WHERE currOperation.Finished = 1 AND currItem.Denormalized = 0
),

RecursionInitialization AS
(
    SELECT  currItem.Id,
            currItem.PreviousId,
            currItem.UUID,
            currItem.Json,
            currItem.TableName,
            currItem.OperationId,
            currItem.PermissionId,
            currItem.Denormalized,
            currItem.Id AS BranchID,
            COALESCE (C.PrevComputed, C.CurrComputed) AS CreatedOn,
            COALESCE (C.PrevComputed, CAST(0 AS BIGINT)) AS RecoveredOn,
            COALESCE (C.PrevComputed, CAST(0 AS BIGINT)) AS UpdatedOnPrev,
            C.CurrComputed AS UpdatedOnCurr,
            COALESCE (C.NextComputed, CAST(8640000000000000 AS BIGINT)) AS UpdatedOnNext,
            C.NextId AS UpdatedOnNextId,

            0 AS RecursionLevel

    FROM Items AS currItem
    INNER JOIN Computed AS C ON currItem.Id = C.Id
    WHERE currItem.Denormalized = 0

    UNION ALL

    SELECT  currItem.Id,
            currItem.PreviousId,
            currItem.UUID,
            currItem.Json,
            currItem.TableName,
            currItem.OperationId,
            currItem.PermissionId,
            currItem.Denormalized,
            currItem.BranchId,
            currItem.CreatedOn,
            currItem.RecoveredOn,
            currItem.UpdatedOnPrev,
            currItem.UpdatedOnCurr,
            currItem.UpdatedOnNext,
            currItem.UpdatedOnNextId,

            0 AS RecursionLevel

    FROM Items AS currItem
    WHERE currItem.Denormalized = 1
),
Recursion AS
(
    SELECT *
    FROM RecursionInitialization AS currItem

    UNION ALL

    SELECT  currItem.Id,
            currItem.PreviousId,
            currItem.UUID,
            currItem.Json,
            currItem.TableName,
            currItem.OperationId,
            currItem.PermissionId,
            currItem.Denormalized,

            CASE
                WHEN prevItem.UpdatedOnNextId = currItem.Id
                THEN prevItem.BranchID
                ELSE currItem.Id
            END AS BranchID,

            prevItem.CreatedOn AS CreatedOn,

            CASE
                WHEN prevItem.Json IS NULL
                THEN CASE
                            WHEN currItem.Json IS NULL
                            THEN prevItem.RecoveredOn
                            ELSE C.CurrComputed
                        END
                ELSE prevItem.RecoveredOn
            END AS RecoveredOn,

            prevItem.UpdatedOnCurr AS UpdatedOnPrev,

            C.CurrComputed AS UpdatedOnCurr,

            COALESCE (C.NextComputed, CAST(8640000000000000 AS BIGINT)) AS UpdatedOnNext,

            C.NextId,

            prevItem.RecursionLevel + 1 AS RecursionLevel
    FROM Items currItem
    INNER JOIN Computed C ON currItem.Id = C.Id
    INNER JOIN Recursion AS prevItem ON currItem.PreviousId = prevItem.Id
    WHERE currItem.Denormalized = 0
)
SELECT  item.Id,
        item.PreviousId,
        item.UUID,
        item.Json,
        item.TableName,
        item.OperationId,
        item.PermissionId,
        item.Denormalized,
        item.BranchID,
        item.CreatedOn,
        item.RecoveredOn,
        item.UpdatedOnPrev,
        item.UpdatedOnCurr,
        item.UpdatedOnNext,
        item.UpdatedOnNextId

FROM Recursion AS item
INNER JOIN
(
    SELECT Id, MAX(RecursionLevel) AS Recursion
    FROM Recursion AS item
    GROUP BY Id
) AS nested ON item.Id = nested.Id AND item.RecursionLevel = nested.Recursion
GO

คำถาม)

มีสถานการณ์สมมติสองสถานการณ์ที่นำมาพิจารณากรณีที่ denormalized และ normalized:

  1. เมื่อมองไปที่การสำรองข้อมูลดั้งเดิมสิ่งที่ทำให้SELECT * FROM Denormalizerช้าลงอย่างเจ็บปวดฉันรู้สึกว่ามีปัญหากับส่วนที่เรียกซ้ำของมุมมอง Denormalizer ฉันพยายาม จำกัดdenormalized = 1แต่การกระทำของฉันไม่ได้รับผลกระทบ

  2. หลังจากทำงานUPDATE Items SET Denormalized = 0ก็จะทำให้GetLatestและSELECT * FROM Denormalizerวิ่งเข้าไป ( แต่เดิมคิดว่าจะเป็น) สถานการณ์ช้าจะมีวิธีการสิ่งที่ความเร็วขึ้นเมื่อเราคำนวณสาขาบริการBranchId, RecoveredOn, CreatedOn, UpdatedOnPrev, UpdatedOnCurr, UpdatedOnNext,UpdatedOnNextId

ขอบคุณล่วงหน้า

PS

ฉันพยายามที่จะยึดติดกับ SQL มาตรฐานเพื่อให้การสืบค้นง่ายต่อการพกพาไปยังฐานข้อมูลอื่นเช่น MySQL / Oracle / SQLite สำหรับอนาคต แต่ถ้าไม่มี sql มาตรฐานที่อาจช่วยฉันตกลงกับการสร้างฐานข้อมูลเฉพาะ


1
ในเรื่องเกี่ยวกับ SQL มาตรฐานและ DB ที่คุณแสดงรายการ: คุณกำลังใช้ CTE ที่นี่และพวกเขาไม่ได้รับการสนับสนุนโดย mySQL และมีรูปแบบไวยากรณ์บางอย่างระหว่างการปรับใช้ที่สำคัญ นอกจากนี้พวกเขายังเป็นรั้วการเพิ่มประสิทธิภาพใน postgres ซึ่งอาจเป็นกังวลเกี่ยวกับประสิทธิภาพขนาดใหญ่ ไม่มีสิ่งใดที่จะหยุดคุณไม่ให้ใช้ต้นไม้ในรูปแบบ "รายการ adjacency" โดยปกติแล้วเครื่องมือเหล่านี้จะเป็นเครื่องมือที่เหมาะสมกับงาน เตรียมพร้อมสำหรับการทำงานพิเศษใด ๆ ที่จำเป็นเมื่อการโยกย้ายไปยัง DBMS อื่นกลายเป็นความจริง
David Spillett

ขอบคุณฉันพยายามติดกับ SQL มาตรฐานมากที่สุด เหตุผลที่เป็นที่ฉันเชื่อว่าควรลดจำนวนปัญหาในอนาคตเมื่อจะต้องโยกย้ายรหัสที่มีอยู่ไปยังฐานข้อมูลอื่น ๆ มันไม่ได้เป็นไปได้เสมอไป นอกจากนี้ยังมีปัจจัยเวลาซึ่งเป็นส่วนหนึ่งของสมการ ฉันใช้เวลาครึ่งปีเพื่อกำหนดโครงสร้างฐานข้อมูลปัจจุบัน ... ฉันต้องการให้มีโครงสร้างแบบมาตรฐานเท่านั้น แต่ถ้ามันต้องใช้เวลาอีก 10 ปีก็ไม่ใช่วิธีที่จะไป ... ดังนั้นหากคุณเห็นว่ามีอีกมาก refactoring มาตรฐานที่เป็นไปได้ผมจะมีความสุขที่จะยอมรับมัน ...
Lu4

1
ไม่ฟังดูเหมือนเป็นวิธีปฏิบัติที่จะจัดการกับลำดับความสำคัญและภาวะแทรกซ้อนที่แตกต่างกันในกรณีเช่นนี้ ฉันแค่ทิ้งประเด็นที่ผุดขึ้นมาในใจในกรณีที่คุณยังไม่เจอเลย (ดีกว่าที่จะรู้ตอนนี้แม้ว่ามันจะเป็นไปไม่ได้ / ในทางปฏิบัติที่จะทำอะไรเกี่ยวกับเรื่องนี้ในตอนนี้ )
David Spillett

คำตอบ:


9

@ Lu4 .. ฉันโหวตให้ปิดคำถามนี้เป็น "Tip of Iceberg" แต่การใช้คำใบ้แบบสอบถามคุณจะสามารถเรียกใช้ภายใน 1 วินาที คำถามนี้สามารถถูก refactored และสามารถใช้CROSS APPLYแต่มันจะเป็นกิ๊กให้คำปรึกษาและไม่ได้เป็นคำตอบในเว็บไซต์ถาม & ตอบ

ข้อความค้นหาของคุณจะทำงานเป็นเวลา 13+ นาทีบนเซิร์ฟเวอร์ของฉันด้วย 4 CPU และ RAM 16GB

ป้อนคำอธิบายรูปภาพที่นี่

ฉันเปลี่ยนข้อความค้นหาของคุณให้ใช้OPTION(MERGE JOIN)และทำงานน้อยกว่า 1 วินาที

set nocount on 
set statistics io on
set statistics time on
;WITH Computed AS
(
    SELECT  currItem.Id,
            nextOperation.id AS NextId,
            prevOperation.FinishedOn AS PrevComputed,
            currOperation.FinishedOn AS CurrComputed,
            nextOperation.FinishedOn AS NextComputed

    FROM Items currItem 
    INNER JOIN dbo.Operations AS currOperation ON currItem.OperationId = currOperation.Id

    LEFT OUTER JOIN dbo.Items AS prevItem ON currItem.PreviousId = prevItem.Id
    LEFT OUTER JOIN dbo.Operations AS prevOperation ON prevItem.OperationId = prevOperation.Id 
    LEFT OUTER JOIN
    (
        SELECT MIN(I.id) as id, S.PreviousId, S.FinishedOn
        FROM Items I
        INNER JOIN
        (
            SELECT I.PreviousId, MIN(nxt.FinishedOn) AS FinishedOn
            FROM dbo.Items I
            LEFT OUTER JOIN dbo.Operations AS nxt ON I.OperationId = nxt.Id
            GROUP BY I.PreviousId
        ) AS S ON I.PreviousId = S.PreviousId 
        GROUP BY S.PreviousId, S.FinishedOn
    ) AS nextOperation ON nextOperation.PreviousId = currItem.Id

    WHERE currOperation.Finished = 1 AND currItem.Denormalized = 0
),

RecursionInitialization AS
(
    SELECT  currItem.Id,
            currItem.PreviousId,
            currItem.UUID,
            currItem.Json,
            currItem.TableName,
            currItem.OperationId,
            currItem.PermissionId,
            currItem.Denormalized,
            currItem.Id AS BranchID,
            COALESCE (C.PrevComputed, C.CurrComputed) AS CreatedOn,
            COALESCE (C.PrevComputed, CAST(0 AS BIGINT)) AS RecoveredOn,
            COALESCE (C.PrevComputed, CAST(0 AS BIGINT)) AS UpdatedOnPrev,
            C.CurrComputed AS UpdatedOnCurr,
            COALESCE (C.NextComputed, CAST(8640000000000000 AS BIGINT)) AS UpdatedOnNext,
            C.NextId AS UpdatedOnNextId,

            0 AS RecursionLevel

    FROM Items AS currItem
    INNER JOIN Computed AS C ON currItem.Id = C.Id
    WHERE currItem.Denormalized = 0

    UNION ALL

    SELECT  currItem.Id,
            currItem.PreviousId,
            currItem.UUID,
            currItem.Json,
            currItem.TableName,
            currItem.OperationId,
            currItem.PermissionId,
            currItem.Denormalized,
            currItem.BranchId,
            currItem.CreatedOn,
            currItem.RecoveredOn,
            currItem.UpdatedOnPrev,
            currItem.UpdatedOnCurr,
            currItem.UpdatedOnNext,
            currItem.UpdatedOnNextId,

            0 AS RecursionLevel

    FROM Items AS currItem
    WHERE currItem.Denormalized = 1
),
Recursion AS
(
    SELECT *
    FROM RecursionInitialization AS currItem

    UNION ALL

    SELECT  currItem.Id,
            currItem.PreviousId,
            currItem.UUID,
            currItem.Json,
            currItem.TableName,
            currItem.OperationId,
            currItem.PermissionId,
            currItem.Denormalized,

            CASE
                WHEN prevItem.UpdatedOnNextId = currItem.Id
                THEN prevItem.BranchID
                ELSE currItem.Id
            END AS BranchID,

            prevItem.CreatedOn AS CreatedOn,

            CASE
                WHEN prevItem.Json IS NULL
                THEN CASE
                            WHEN currItem.Json IS NULL
                            THEN prevItem.RecoveredOn
                            ELSE C.CurrComputed
                        END
                ELSE prevItem.RecoveredOn
            END AS RecoveredOn,

            prevItem.UpdatedOnCurr AS UpdatedOnPrev,

            C.CurrComputed AS UpdatedOnCurr,

            COALESCE (C.NextComputed, CAST(8640000000000000 AS BIGINT)) AS UpdatedOnNext,

            C.NextId,

            prevItem.RecursionLevel + 1 AS RecursionLevel
    FROM Items currItem
    INNER JOIN Computed C ON currItem.Id = C.Id
    INNER JOIN Recursion AS prevItem ON currItem.PreviousId = prevItem.Id
    WHERE currItem.Denormalized = 0
)
SELECT  item.Id,
        item.PreviousId,
        item.UUID,
        item.Json,
        item.TableName,
        item.OperationId,
        item.PermissionId,
        item.Denormalized,
        item.BranchID,
        item.CreatedOn,
        item.RecoveredOn,
        item.UpdatedOnPrev,
        item.UpdatedOnCurr,
        item.UpdatedOnNext,
        item.UpdatedOnNextId

FROM Recursion AS item
INNER JOIN
(
    SELECT Id, MAX(RecursionLevel) AS Recursion
    FROM Recursion AS item
    GROUP BY Id
) AS nested ON item.Id = nested.Id AND item.RecursionLevel = nested.Recursion
OPTION (MERGE JOIN)

set nocount oFF 
set statistics io OFF
set statistics time OFF

ป้อนคำอธิบายรูปภาพที่นี่

โปรดทราบว่าคุณไม่สามารถใช้คำแนะนำการสืบค้นในมุมมองได้ดังนั้นคุณต้องหาทางเลือกอื่นในการทำให้มุมมองของคุณเป็น SP หรือวิธีแก้ปัญหาใด ๆ


1
ขอบคุณมากสำหรับสิ่งนี้โดยคำนึงว่าคำถามนั้นไกลจากมาตรฐานสแต็คโฟลว์โฟลว์ที่ทำให้ความพยายามของคุณมีค่าสำหรับฉันเป็นสองเท่า ฉันจะทำการบ้านของฉันกับ CROSS และลองคิดดู OPTION (MERGE JOIN) มันไม่ชัดเจนในขณะนี้สิ่งที่ดูเหมือนจะมีปัญหากับแบบสอบถามที่ แต่ผมค่อนข้างมั่นใจว่าผมจะคิดออกขอขอบคุณอีกครั้ง
Lu4

@ Lu4 ปัญหาคือเครื่องมือเพิ่มประสิทธิภาพการสืบค้นไม่ได้เลือก (หรือสร้าง) แผนการดำเนินการที่ดีที่สุด คำแนะนำการค้นหาในกรณีนี้ 'กระตุ้น' เครื่องมือเพิ่มประสิทธิภาพให้ใช้กลยุทธ์เฉพาะเพื่อดำเนินการเข้าร่วม ดูคำแนะนำการเข้าร่วม (Transact-SQL)สำหรับรายละเอียดเพิ่มเติม
Kenny Evitt

CROSS APPLYดีมาก แต่ฉันขอแนะนำให้อ่านแผนการดำเนินการและวิธีวิเคราะห์ก่อนที่จะลองใช้คำแนะนำการสืบค้น
Kenny Evitt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.