SQL Server: hiệu năng truy vấn (tìm kiếm 2 triệu hàng)


7

Có một điều thú vị cho tất cả các bạn SQL guru ngoài kia. Bây giờ tìm kiếm này chỉ mất vài giây, nhưng nó khá chuyên sâu và phải có cách tốt hơn. Có lẽ tôi đang mong đợi quá nhiều?

Ứng dụng tìm kiếm kỳ nghỉ đơn giản. 2 triệu ngày lễ. Phân trang / Sắp xếp khoảng 600.000 hàng.

Đây là lược đồ của bảng

CREATE TABLE [dbo].[Holiday](
        [Id] [int] NOT NULL,
        [PropertyId] [int] NOT NULL,
        [Price] [int] NOT NULL,
        [Rating] [int] NOT NULL,
        [Country] [char](2) NOT NULL,
        [ResortId] [int] NOT NULL,
        [DepartureAirport] [char](3) NOT NULL,
        [DestinationAirport] [char](3) NOT NULL,
        [DepartureDate] [datetime] NOT NULL,
        [Basis] [char](2) NOT NULL,
        [Duration] [int] NOT NULL,

     CONSTRAINT [PK_Holiday] PRIMARY KEY CLUSTERED ([Id] ASC)
  )

Như bạn thấy, khá đơn giản. Chúng tôi có một tài sản, giá cả, thời gian, sân bay khởi hành / điểm đến, v.v ... Bây giờ, càng nhiều lĩnh vực được cung cấp thì tìm kiếm càng nhanh. Nếu tôi có Sân bay Khởi hành, Tài sản và Ngày thì việc tìm kiếm rất nhanh. Tuy nhiên, nếu tôi chỉ có một Quốc gia và không có gì khác, có rất nhiều dữ liệu để xử lý.

Sử dụng xuất CSV này của bảng của tôi, có tổng cộng 2 triệu hàng và khoảng 666k chỉ với mã quốc gia của FR, đó là ví dụ của tôi.

Đây, là truy vấn tìm kiếm. Mà trả về hai bảng. Đầu tiên là một bản tóm tắt, vì vậy tổng số ngày nghỉ phù hợp với tiêu chí của bạn và có bao nhiêu thuộc tính duy nhất. Bảng thứ hai chứa kết quả thực tế từ tìm kiếm.

--Build a temp table, and store everything we need in it
CREATE TABLE #Pricing (PropertyId int, Duration int, HolidayId int, Rating int, Price int, StartDate datetime, PropertyRow int);

INSERT INTO #Pricing
  SELECT 
    PropertyId, Duration, [Id], [Rating], [Price], DepartureDate,
    ROW_NUMBER() OVER (PARTITION BY PropertyId ORDER BY Price ASC) as PropertyRow
  FROM 
    dbo.Holiday
  WHERE 
    DepartureDate > GETDATE() AND Country = 'FR'

--Get a total number of holidays, and total number of properties
SELECT 
    COUNT(*) AS TotalHolidaysCount, 
    COUNT(DISTINCT PropertyId) AS PropertyCount
FROM 
    #Pricing

--Build the final table, which will contain all the holidays we actually want to return
DECLARE @FinalResults TABLE (HolidayId int, RowNumber int);

INSERT INTO 
    @FinalResults
  SELECT 
    HolidayId, RowNumber
  FROM
    (SELECT 
         PropertyRow, HolidayId, 
         ROW_NUMBER() OVER (order by (CASE WHEN StartDate <= '01/Apr/2013' THEN 1 ELSE 0 END) ASC, [Price] ASC) as RowNumber
     FROM  
        #Pricing 
     WHERE 
        PropertyRow = 1) as SearchResults
WHERE 
    (RowNumber > (10 * (1 - 1)) and RowNumber <= (1 * 10))
ORDER BY 
    RowNumber;

SELECT
     *
FROM
    @FinalResults
    INNER JOIN dbo.Holiday ON HolidayId = Holiday.Id

DROP TABLE #Pricing

Bây giờ, tôi có thể xem xét Lập chỉ mục rõ ràng sẽ cải thiện hiệu suất. Nhưng điều làm tôi lo lắng là việc sử dụng các bảng tạm thời đáng kinh ngạc. Chắc chắn đây không phải là cách nó được thực hiện? Mất 5 giây để tìm kiếm những gì cuối cùng là một lượng nhỏ dữ liệu. Họ chỉ lý do họ được sử dụng là vì nhu cầu tham khảo dữ liệu sau này.

Nó có thể có giá trị khi chạy truy vấn hai lần, thay vì lưu trữ tất cả dữ liệu trong bộ nhớ? Có vẻ lãng phí khi chọn hơn 25% bảng vào bộ nhớ hết lần này đến lần khác.

Bất kỳ thông tin phản hồi hữu ích sẽ được đánh giá cao. Không tìm kiếm "câu trả lời", chỉ cần một số trợ giúp.

Rất cám ơn, Trưởng khoa


4
Chắc chắn mã này từ mệnh đề where cuối cùng: (RowNumber > (10 * (1 - 1)) and RowNumber <= (1 * 10))sẽ được viết tốt hơn nhiều như làRowNumber Between 1 and 10

Câu trả lời:


2

Rất khó để đề xuất chính xác một giải pháp không có khả năng chạy thử nghiệm hoặc xem DB được lập chỉ mục như thế nào. Nhưng dù sao tôi cũng sẽ thử.

Bạn cần phải tìm sự cân bằng, nếu truy vấn của bạn có khả năng trả về nhiều dữ liệu và chạy nhanh thì tôi sẽ chuyển sang truy vấn trên bảng chính hai lần, nếu nó có thể mất nhiều thời gian và trả về tương đối nhỏ số lượng hàng sau đó tôi sẽ gắn bó với cách tiếp cận bảng tạm thời.

Với thông tin bạn đã cung cấp trong câu hỏi, có vẻ như không có vấn đề gì với tốc độ của lựa chọn, trong trường hợp đó tôi có xu hướng đồng ý với bạn, chi phí bổ sung để chèn vào bảng tạm thời, sau đó chọn từ temp bảng có nhiều chi phí như thực hiện truy vấn chọn hai lần. Các truy vấn có thể được đơn giản hóa như sau:

SELECT  COUNT(*) [TotalHolidaysCount],
        COUNT(DISTINCT PropertyID) [PropertyCount]
FROM    dbo.Holiday
WHERE   DepartureDate > GETDATE() 
AND     Country = 'FR'


SELECT  *
FROM    (   SELECT  *, ROW_NUMBER() OVER (ORDER BY(CASE WHEN StartDate <= '01/Apr/2013' THEN 1 ELSE 0 END) ASC, [Price] ASC) [RowNumber]
            FROM    (   SELECT  h.*, ROW_NUMBER(PARTITION BY PropertyID, ORDER BY Price ASC) [PropertyRow]
                        FROM    dbo.Holiday
                        WHERE   DepartureDate > GETDATE() 
                        AND     Country = 'FR'
                    ) h
            WHERE   PropertyRow = 1
        ) h
WHERE   Rownumber BETWEEN 1 AND 10

Nếu bạn thấy đây là một lần nhấn hiệu suất và bạn muốn lưu trữ kết quả của lựa chọn Tôi sẽ có xu hướng sử dụng các biến bảng thay vì các bảng tạm thời và chỉ lưu trữ khóa chính của Holiday ID và tham gia nhanh vào kỳ nghỉ tham gia được lập chỉ mục của khóa chính = khóa chính như sau:

DECLARE @Results TABLE (ID INT NOT NULL PRIMARY KEY)
INSERT @Results
SELECT  ID
FROM    dbo.Holiday
WHERE   DepartureDate > GETDATE() 
AND     Country = 'FR'

SELECT  COUNT(*) [TotalHolidaysCount],
        COUNT(DISTINCT PropertyID) [PropertyCount]
FROM    dbo.Holiday h
        INNER JOIN @Results r
            ON r.ID = h.ID

SELECT  *
FROM    (   SELECT  *, ROW_NUMBER() OVER (ORDER BY(CASE WHEN StartDate <= '01/Apr/2013' THEN 1 ELSE 0 END) ASC, [Price] ASC) [RowNumber]
            FROM    (   SELECT  h.*, ROW_NUMBER(PARTITION BY PropertyID, ORDER BY Price ASC) [PropertyRow]
                        FROM    dbo.Holiday h
                                INNER JOIN @Results r
                                    ON r.ID = h.ID
                    ) h
            WHERE   PropertyRow = 1
        ) h
WHERE   Rownumber BETWEEN 1 AND 10

Bằng cách này, bạn đang lưu trữ ít dữ liệu nhất có thể (một cột số nguyên), trong khi vẫn giữ đủ dữ liệu để thực hiện tìm kiếm được lập chỉ mục nhanh trên dbo.Holiday.

Cuối cùng phải xem xét các kế hoạch thực hiện của bạn, tạo ra các chỉ mục thích hợp và thử nghiệm các cách tiếp cận khác nhau để tìm ra phương pháp phù hợp nhất với bạn.


Đồng ý với "kế hoạch, chỉ mục, thử nghiệm" để tìm ra cách tiếp cận tốt nhất.
pheedbaq

2

Không có chỉ mục, bất cứ khi nào truy vấn tìm kiếm được chạy, tất cả 2 triệu bản ghi phải được tìm kiếm để tìm các phiên bản 650K 'FR'. Với các chỉ mục, cơ sở dữ liệu về cơ bản có thể đi thẳng vào chúng. Ngay cả khi bạn để nguyên truy vấn tìm kiếm, tôi nghĩ các chỉ mục thích hợp sẽ giúp bạn cải thiện tốc độ.

Theo như bảng tạm thời, tôi thực sự không hiểu tại sao điều đó không thể được thực hiện như là một phụ trong truy vấn kết quả cuối cùng. Ngoài ra, tôi nghĩ các bảng tạm thời sẽ hữu ích hơn cho các kết nối liên tục đến DB hoặc nhóm người dùng. Nếu bạn chỉ tạo bảng tạm thời và ngay lập tức phá hủy nó ... thì về cơ bản nó chỉ được sử dụng như một truy vấn con.

Cập nhật: M_M đưa ra một điểm tốt trong nhận xét của mình về câu trả lời này. Tuy nhiên, tôi vẫn cảm thấy một chỉ số sẽ tốt hơn nếu phần lớn hoạt động không chỉ trên các tập hợp mà quốc gia là tiêu chí duy nhất . Đối với tôi (chỉ là ý kiến ​​của tôi), nó sẽ đi xuống mức độ thường xuyên cần có tập hợp con 'FR' mà không cần tiêu chí nào khác. Mặt khác, các chỉ mục có thể được sử dụng trong khả năng tìm kiếm.


1
Không chắc là chỉ mục không bao gồm sẽ được sử dụng nếu nó trả lại 25% + của bảng. Chi phí tìm kiếm bookmark 650K sẽ vượt xa chi phí cho một lần quét hàng 2MM.

Hmm, điểm tốt.
pheedbaq

Ngoài ra, các chỉ số thực sự làm cho nó nhanh hơn. Không phải vô cùng như vậy, nhưng nhanh hơn. Vấn đề vẫn nằm ở việc lưu trữ 650k hàng dữ liệu trong một bảng tạm thời. Đối với tôi, nghe có vẻ điên rồ khi làm điều đó.

@DeanThomas: Vâng Tôi không chắc chắn tôi thấy điểm của bảng tạm thời, bất kể những gì được thực hiện với chỉ mục: \, trừ khi có kết nối liên tục trong đó dữ liệu được sử dụng lại.
pheedbaq

1
Hmm, có thể. Nhưng ngay cả khi bạn chỉ thực hiện cùng một truy vấn con hai lần, máy chủ có thể đã có kế hoạch thực hiện cho nó (mặc dù không biết chắc chắn).
pheedbaq
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.