2009-07-07 25 views
6

Tôi đã xây dựng một diễn đàn tùy chỉnh cho trang web của mình bằng cách sử dụng MySQL. Trang danh sách về bản chất là một bảng có các cột sau: Chủ đề, Cập nhật lần cuối# Trả lời.Tối ưu hóa truy vấn MySQL để tránh "Sử dụng ở đâu; Sử dụng tạm thời; Sử dụng filesort"

Các DB bảng có các cột sau:

id 
name 
body 
date 
topic_id 
email 

Một chủ đề có topic_id "0", và trả lời có topic_id của chủ đề cha mẹ của họ.

SELECT SQL_CALC_FOUND_ROWS 
    t.id, t.name, MAX(COALESCE(r.date, t.date)) AS date, COUNT(r.id) AS replies 
FROM 
    wp_pod_tbl_forum t 
LEFT OUTER JOIN 
    wp_pod_tbl_forum r ON (r.topic_id = t.id) 
WHERE 
    t.topic_id = 0 
GROUP BY 
    t.id 
ORDER BY 
    date DESC LIMIT 0,20; 

Có khoảng 2.100 tổng số mục trong bảng này và các truy vấn thường mất đến 6 giây. Tôi đã thêm một INDEX vào cột "topic_id", nhưng điều đó không giúp được gì nhiều. Có cách nào để tăng tốc truy vấn này với việc thực hiện tái cơ cấu đáng kể không?

EDIT: chưa hoạt động. Tôi dường như không thể lấy các ví dụ bên dưới để hoạt động bình thường.

Trả lời

7
SELECT id, name, last_reply, replies 
FROM (
     SELECT topic_id, MAX(date) AS last_reply, COUNT(*) AS replies 
     FROM wp_pod_tbl_forum 
     GROUP BY 
       topic_id 
     ) r 
JOIN wp_pod_tbl_forum t 
ON  t.topic_id = 0 
     AND t.id = r.topic_id 
UNION ALL 
SELECT id, name, date, 0 
FROM wp_pod_tbl_forum t 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM wp_pod_tbl_forum r 
     WHERE r.topic_id = t.id 
     ) 
     AND t.topic_id = 0 
ORDER BY 
     date DESC 
LIMIT 0, 20 

Nếu bảng của bạn là MyISAM hoặc id không phải là một PRIMARY KEY, bạn cần phải tạo ra một ondex composit trên (topic_id, id).

Nếu bảng của bạn là InnoDBid là một PRIMARY KEY, một chỉ số chỉ trên (topic_id) sẽ làm (id sẽ được mặc nhiên thêm vào chỉ mục).

Cập nhật

Truy vấn này có lẽ hầu hết sẽ còn hiệu quả hơn, với điều kiện là bạn có chỉ số trên (topic_id, id)(date, id):

Xem bài viết này trong blog của tôi để biết chi tiết thực hiện:

Truy vấn này hoàn thành trong 30 ms trên dữ liệu mẫu 100,000 hàng:

SELECT id, name, last_reply, 
     (
     SELECT COUNT(*) 
     FROM wp_pod_tbl_forum fc 
     WHERE fc.topic_id = fl.topic_id 
     ) AS replies 
FROM (
     SELECT topic_id, date AS last_reply 
     FROM wp_pod_tbl_forum fo 
     WHERE id = (
       SELECT id 
       FROM wp_pod_tbl_forum fp 
       WHERE fp.topic_id = fo.topic_id 
       ORDER BY 
         fp.date DESC, fp.id DESC 
       LIMIT 1 
       ) 
       AND fo.topic_id <> 0 
     ORDER BY 
       fo.date DESC, fo.id DESC 
     LIMIT 20 
     ) fl 
JOIN wp_pod_tbl_forum ft 
ON  ft.id = fl.topic_id 
UNION ALL 
SELECT id, name, date, 0 
FROM wp_pod_tbl_forum t 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM wp_pod_tbl_forum r 
     WHERE r.topic_id = t.id 
     ) 
     AND t.topic_id = 0 
ORDER BY 
     last_reply DESC, id DESC 
LIMIT 20 

Cả hai chỉ số được yêu cầu cho truy vấn này để có hiệu quả.

Nếu bảng của bạn là InnoDBid là một PRIMARY KEY, sau đó bạn có thể bỏ qua id từ indexes trên.

+0

Cột 'ngày' trong danh sách trường không rõ ràng ..? – Matt

+0

@Matt: xem cập nhật – Quassnoi

+0

@Quassnoi - bạn có thể giải thích những gì đang diễn ra không? "UNION ALL" có thay thế "last_reply" bằng "date" nếu chủ đề không có trả lời không? – Matt

1

Bạn có thể muốn chia nhỏ nó thành một tập hợp các truy vấn con (như truy vấn bên trong). Tôi cần giản đồ để thực sự chơi, nhưng nếu bạn

SELECT t.id, t.name, MAX(COALESCE(r.date, t.date)) AS date, COUNT(r.id) AS replies 
FROM (
    SELECT (id, name, date) 
    FROM wp_pod_tbl_forum 
    WHERE topic_id = 0 
) as t 
LEFT OUTER JOIN 
    wp_pod_tbl_forum r 
WHERE 
    r.topic_id = t.id 
GROUP BY 
    t.id 
ORDER BY 
    date DESC LIMIT 0,20; 

có thể giúp tăng tốc độ lên một chút, thậm chí có thể không phải là câu trả lời hay nhất (có thể có lỗi).

Có rất nhiều cách để làm điều đó, nhưng điều quan trọng nhất cần làm khi điều chỉnh SQL là giảm từng bộ càng nhiều càng tốt trước khi thực hiện một thao tác.

Các vấn đề liên quan