2012-01-03 60 views
7

Tôi có hai bảng:MySQL & bộ lồng nhau: chậm JOIN (không sử dụng index)

địa phương:

CREATE TABLE `localities` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(100) NOT NULL, 
    `type` varchar(30) NOT NULL, 
    `parent_id` int(11) DEFAULT NULL, 
    `lft` int(11) DEFAULT NULL, 
    `rgt` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `index_localities_on_parent_id_and_type` (`parent_id`,`type`), 
    KEY `index_localities_on_name` (`name`), 
    KEY `index_localities_on_lft_and_rgt` (`lft`,`rgt`) 
) ENGINE=InnoDB; 

locatings:

CREATE TABLE `locatings` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `localizable_id` int(11) DEFAULT NULL, 
    `localizable_type` varchar(255) DEFAULT NULL, 
    `locality_id` int(11) NOT NULL, 
    `category` varchar(50) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `index_locatings_on_locality_id` (`locality_id`), 
    KEY `localizable_and_category_index` (`localizable_type`,`localizable_id`,`category`), 
    KEY `index_locatings_on_category` (`category`) 
) ENGINE=InnoDB; 

địa phương bàn được thực hiện như một bộ lồng nhau .

Hiện tại, khi người dùng thuộc một số địa phương (thông qua một số địa điểm), anh ta cũng thuộc về tất cả tổ tiên của nó (các địa phương cấp cao hơn). Tôi cần một truy vấn sẽ chọn tất cả các địa phương mà tất cả người dùng thuộc về một chế độ xem.

Đây là cố gắng của tôi:

select distinct lca.*, lt.localizable_type, lt.localizable_id 
from locatings lt 
join localities lc on lc.id = lt.locality_id 
left join localities lca on (lca.lft <= lc.lft and lca.rgt >= lc.rgt) 

Vấn đề ở đây là phải mất quá nhiều thời gian để thực hiện.

tôi tham khảo ý kiến ​​GIẢI THÍCH:

+----+-------------+-------+--------+---------------------------------+---------+---------+----------------------------------+-------+----------+-----------------+ 
| id | select_type | table | type | possible_keys     | key  | key_len | ref        | rows | filtered | Extra   | 
+----+-------------+-------+--------+---------------------------------+---------+---------+----------------------------------+-------+----------+-----------------+ 
| 1 | SIMPLE  | lt | ALL | index_locatings_on_locality_id | NULL | NULL | NULL        | 4926 | 100.00 | Using temporary | 
| 1 | SIMPLE  | lc | eq_ref | PRIMARY       | PRIMARY | 4  | bzzik_development.lt.locality_id |  1 | 100.00 |     | 
| 1 | SIMPLE  | lca | ALL | index_localities_on_lft_and_rgt | NULL | NULL | NULL        | 11439 | 100.00 |     | 
+----+-------------+-------+--------+---------------------------------+---------+---------+----------------------------------+-------+----------+-----------------+ 
3 rows in set, 1 warning (0.00 sec) 

cuối cùng tham gia rõ ràng không sử dụng lft, chỉ số rgt như tôi mong đợi nó. Tôi tuyệt vọng.

CẬP NHẬT: Sau khi thêm điều kiện như @cairnz đề xuất, truy vấn mất quá nhiều thời gian để xử lý.

UPDATE 2: Các tên cột thay vì dấu hoa thị

truy vấn Cập nhật:

SELECT DISTINCT lca.id, lt.`localizable_id`, lt.`localizable_type` 
FROM locatings lt FORCE INDEX(index_locatings_on_category) 
JOIN localities lc 
    ON lc.id = lt.locality_id 
INNER JOIN localities lca 
    ON lca.lft <= lc.lft AND lca.rgt >= lc.rgt 
WHERE lt.`category` != "Unknown"; 

Cập nhật EXAPLAIN:

+----+-------------+-------+--------+-----------------------------------------+-----------------------------+---------+---------------------------------+-------+----------+-------------------------------------------------+ 
| id | select_type | table | type | possible_keys       | key       | key_len | ref        | rows | filtered | Extra           | 
+----+-------------+-------+--------+-----------------------------------------+-----------------------------+---------+---------------------------------+-------+----------+-------------------------------------------------+ 
| 1 | SIMPLE  | lt | range | index_locatings_on_category    | index_locatings_on_category | 153  | NULL       | 2545 | 100.00 | Using where; Using temporary     | 
| 1 | SIMPLE  | lc | eq_ref | PRIMARY,index_localities_on_lft_and_rgt | PRIMARY      | 4  | bzzik_production.lt.locality_id |  1 | 100.00 |             | 
| 1 | SIMPLE  | lca | ALL | index_localities_on_lft_and_rgt   | NULL      | NULL | NULL       | 11570 | 100.00 | Range checked for each record (index map: 0x10) | 
+----+-------------+-------+--------+-----------------------------------------+-----------------------------+---------+---------------------------------+-------+----------+-------------------------------------------------+ 

Bất kỳ trợ giúp đánh giá cao.

+0

bạn đã cố gắng không có lft và RFT trong cùng một chỉ mục? (một cho lft, một cho rft) – cairnz

+0

@cairnz Có, không thành công –

+0

Trả lời cập nhật cho mỗi lần cập nhật của bạn. – cairnz

Trả lời

2

Ah, nó chỉ xảy ra với tôi.

Vì bạn đang yêu cầu tất cả mọi thứ trong bảng, mysql quyết định sử dụng quét toàn bộ bảng để thay thế, vì nó cho thấy nó hiệu quả hơn.

Để nhận được một số cách sử dụng chính, hãy thêm một số bộ lọc để hạn chế tìm kiếm mọi hàng trong tất cả các bảng.

Cập nhật câu trả lời:

Truy vấn thứ hai của bạn không có ý nghĩa. Bạn đang tham gia để lca nhưng bạn có một bộ lọc trong đó, điều này phủ nhận việc tham gia trái của chính nó. Ngoài ra, bạn đang tìm kiếm dữ liệu trong bước cuối cùng của truy vấn, có nghĩa là bạn sẽ phải xem qua tất cả lt, lc và lca để tìm dữ liệu của bạn. Ngoài ra, bạn không có chỉ mục có cột 'loại' bên trái nhất trên các vị trí, do đó bạn vẫn cần quét toàn bộ bảng để tìm dữ liệu của mình.

Nếu bạn có một số dữ liệu mẫu và ví dụ về những gì bạn đang cố gắng đạt được, có lẽ sẽ dễ dàng hơn để trợ giúp.

+0

Cảm ơn, truy vấn nhanh hơn nhiều, nhưng vẫn mất quá nhiều. Tôi đã cập nhật câu hỏi của mình bằng truy vấn mới và giải thích. –

+0

Xin lỗi, đây có thể là một câu hỏi ngớ ngẩn, nhưng ý bạn là bằng cách thêm bộ lọc thì sao? –

+1

truy vấn của bạn phải xử lý bảng lt, tham gia vào lc, tham gia vào lca. bộ lọc bạn có trong lca, "bước" cuối cùng của truy vấn. sau đó nó có thể quét bảng lca cho các hàng phù hợp với loại! = "Không xác định" nhưng để đến thời điểm đó nó đã phải đọc lt và lc, nếu điều đó có ý nghĩa. bạn cũng có một trái tham gia vào bảng đó, có nghĩa là bạn có thể có hồ sơ NULL ở đó, nhưng bạn đang lọc nó trong một mệnh đề WHERE, loại bỏ tất cả các hồ sơ NULL (bằng một tham gia bên trong). Có lẽ bạn có nghĩa là bộ lọc của bạn được trên lc, hoặc trên lt. Nếu bạn lọc trên bảng lt, nó có ít hàng hơn để quét trong lc và lca. – cairnz

2

cố gắng thử nghiệm bằng cách buộc chỉ mục - http://dev.mysql.com/doc/refman/5.1/en/index-hints.html, có thể đó chỉ là vấn đề về trình tối ưu hóa.

+0

Cũng thay thế 'DISTINCT' bằng' GROUP BY' –

+0

Chúng tôi đã cố gắng với chỉ mục buộc, nhưng nó không thực sự hữu ích. –

+0

@FrancisAvila thay thế DISTINCT bằng GROUP BY không tạo ra bất kỳ sự khác biệt nào. –

0

Có vẻ như bạn đang muốn cha mẹ của một kết quả duy nhất.

Theo người được xác định bằng cách xác định Bộ lồng nhau trong SQL, Joe Celko tại http://www.ibase.ru/devinfo/DBMSTrees/sqltrees.html "Mô hình này là cách tự nhiên để hiển thị một phần vụ nổ, vì lắp ráp cuối cùng được làm bằng các cụm lồng nhau được chia thành các phần riêng biệt. "

Nói cách khác, Bộ lồng nhau được sử dụng để lọc trẻ em hiệu quả với số lượng độc lập tùy ý trong một bộ sưu tập. Bạn có hai bảng, nhưng tôi không thấy nơi các thuộc tính của tập hợp "locatings" không thể được de-normalized thành "địa phương"?

Nếu bảng các địa phương đã có một cột hình học, tôi không thể tìm thấy trên địa bàn một từ một "định vị" và sau đó chọn vào một bảng sử dụng một bộ lọc duy nhất: parent.lft < = row.left VÀ mẹ. rgt> = row.rgt?

CẬP NHẬT

Trong câu trả lời này https://stackoverflow.com/a/1743952/3018894, có một ví dụ từ http://explainextended.com/2009/09/29/adjacency-list-vs-nested-sets-mysql/ nơi ví dụ sau đây được tất cả các tổ tiên đến một độ sâu tùy ý của 100000:

SELECT hp.id, hp.parent, hp.lft, hp.rgt, hp.data 
FROM (
    SELECT @r AS _id, 
      @level := @level + 1 AS level, 
      (
      SELECT @r := NULLIF(parent, 0) 
      FROM t_hierarchy hn 
      WHERE id = _id 
      ) 
    FROM (
      SELECT @r := 1000000, 
        @level := 0 
      ) vars, 
      t_hierarchy hc 
    WHERE @r IS NOT NULL 
    ) hc 
JOIN t_hierarchy hp 
ON  hp.id = hc._id 
ORDER BY 
    level DESC 
Các vấn đề liên quan