กรองชุดใหญ่อย่างมีประสิทธิภาพด้วยการแยก


9

สมมติว่าฉันมีตารางเดียว

CREATE TABLE Ticket (
    TicketId int NOT NULL,
    InsertDateTime datetime NOT NULL,
    SiteId int NOT NULL,
    StatusId tinyint NOT NULL,
    AssignedId int NULL,
    ReportedById int NOT NULL,
    CategoryId int NULL
);

ในตัวอย่างนี้TicketIdคือคีย์หลัก

ฉันต้องการให้ผู้ใช้งานสามารถสร้างข้อความค้นหา "เฉพาะกิจบางส่วน" ได้จากตารางนี้ ฉันพูดบางส่วนเพราะบางส่วนของแบบสอบถามจะได้รับการแก้ไขเสมอ:

  1. แบบสอบถามจะเสมอทำการกรองช่วงบน InsertDateTime
  2. แบบสอบถามจะเสมอ ORDER BY InsertDateTime DESC
  3. แบบสอบถามจะหน้าผลลัพธ์

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

SELECT
    TicketId
FROM (
    SELECT
        TicketId,
        ROW_NUMBER() OVER(ORDER BY InsertDateTime DESC) as RowNum
    FROM Ticket
    WHERE InsertDateTime >= '2013-01-01' AND InsertDateTime < '2013-02-01'
      AND StatusId IN (1,2,3)
      AND (CategoryId IN (10,11) OR CategoryId IS NULL)
    ) _
WHERE RowNum BETWEEN 1 AND 100;

ทีนี้สมมติว่าตารางมี 100,000,000 แถว

สิ่งที่ดีที่สุดที่ฉันสามารถทำได้คือดัชนีครอบคลุมที่มีคอลัมน์ "ทางเลือก" แต่ละคอลัมน์:

CREATE NONCLUSTERED INDEX IX_Ticket_Covering ON Ticket (
    InsertDateTime DESC
) INCLUDE (
    SiteId, StatusId, AssignedId, ReportedById, CategoryId
);

สิ่งนี้ทำให้ฉันมีแผนแบบสอบถามดังนี้

  • เลือก
    • กรอง
      • ด้านบน
        • โครงการลำดับ (คำนวณสเกลาร์)
          • ส่วน
            • ดัชนีค้นหา

มันดูดีทีเดียว ประมาณ 80% -90% ของค่าใช้จ่ายมาจากการดำเนินการดัชนีค้นหาซึ่งเหมาะ

มีกลยุทธ์ที่ดีกว่าสำหรับการค้นหาประเภทนี้หรือไม่?

ฉันไม่ต้องการลดการกรองข้อมูลเพิ่มเติมให้กับลูกค้าเพราะในบางกรณีชุดผลลัพธ์จากส่วน "แก้ไข" อาจเป็น 100 หรือ 1,000 ลูกค้าจะต้องรับผิดชอบในการเรียงลำดับและการเพจซึ่งอาจทำงานได้มากเกินไปสำหรับลูกค้า


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

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

คุณสนใจเรื่องการใช้พื้นที่จัดเก็บเท่าไหร่
Jon Seigel

@ JonSeigel มันขึ้นอยู่กับว่า ... แต่ฉันต้องการที่จะเห็นข้อเสนอแนะใด ๆ
โจเซฟ Daigle

2
และวิธีการ / แบบสอบถามของคุณเพื่อรับหน้า 2 ของผลลัพธ์คืออะไร? RowNum BETWEEN 101 AND 200?
ypercubeᵀᴹ

คำตอบ:


1

หากภาระงานนี้เป็นข้อความค้นหาส่วนใหญ่ที่ตรงกับตารางคุณอาจพิจารณา:

ALTER TABLE Ticket ADD CONSTRAINT PK_Ticket PRIMARY KEY NONCLUSTERED (TicketId);

CREATE UNIQUE CLUSTERED INDEX IX_Ticket_Covering ON Ticket (
    InsertDateTime ASC
);

การพิจารณา:

  • คุณสามารถใช้ datetime2 (SQL 2008+; ความแม่นยำที่ยืดหยุ่นได้)
  • InsertDateTime จะไม่ซ้ำกันภายในความแม่นยำของคุณ
  • หากเวลาไม่ถูก จำกัด sql ที่ไม่ซ้ำกันจะเพิ่มคอลัมน์ uniquifier ที่ซ่อนอยู่ของประเภท int สิ่งนี้ถูกเพิ่มไปยังดัชนี nonclutred ทั้งหมดเพื่อให้สามารถอ้างอิงบันทึกคลัสเตอร์ที่ถูกต้อง

ข้อดี:

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

1

ฉันเคยใช้เทคนิคนี้ในอดีต ตารางไม่ใหญ่จนเกินไป แต่เกณฑ์การค้นหาซับซ้อนกว่า

นี่เป็นเวอร์ชั่นย่อ

CREATE PROC usp_Search
    (
    @StartDate  Date,
    @EndDate    Date,
    @Sites      Varchar(30) = NULL,
    @Assigned   Int = NULL, --Assuming only value possible
    @StartRow   Int,
    @EndRow     Int
    )
AS
DECLARE @TblSites   TABLE (ID Int)
IF @Sites IS NOT NULL
BEGIN
    -- Split @Sites into table @TblSites
END
SELECT  TicketId
FROM    (
        SELECT  TicketId,
                ROW_NUMBER() OVER(ORDER BY InsertDateTime DESC) as RowNum
        FROM    Ticket
                LEFT JOIN @TblSites
                    Ticket.SiteID = @TblSites.ID
        WHERE   InsertDateTime >= @StartDate 
                AND InsertDateTime < @EndDate
                AND (
                    @Assigned IS NULL 
                    OR AssignedId = @Assigned 
                    )
        ) _
WHERE   RowNum BETWEEN @StartRow AND @EndRow;


0

ทำไมคุณไม่ลองแบ่งพาร์ติชั่นล่ะ? พร้อมใช้งานใน SQL 2008 ขึ้นไป แต่ต้องมีรุ่น Enterprise (หรือรุ่น Developer)

โดยทั่วไปคุณแบ่งตารางของคุณออกเป็นหลายพาร์ติชั่นและคุณกำหนดเกณฑ์พาร์ติชั่น (ฟังก์ชั่น) คุณจะเป็นช่วงวันที่หรือไม่

https://www.simple-talk.com/sql/database-administration/gail-shaws-sql-server-howlers/


-1

หากลูกค้ากรองในลักษณะเดียวกันซ้ำแล้วซ้ำอีกคุณสามารถสร้างดัชนีสำหรับการสืบค้นเหล่านั้นได้

เช่นลูกค้ากำลังกรอง SiteId และ StatusId คุณสามารถสร้างดัชนีเพิ่มเติม:

CREATE NONCLUSTERED INDEX IX_Ticket_InsertDateTime_SiteId_StatusId ON Ticket     
(InsertDateTime DESC,
 SiteId [ASC/DESC],
 StatusId [ASC/DESC] ) 
 INCLUDE ( ... );

ด้วยวิธีนี้คิวรีที่ 'พบบ่อย' ส่วนใหญ่สามารถทำงานได้อย่างรวดเร็ว

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