2017-02-21 75 views
1

我有一個嵌套集模型的類別表。每一行應包含其子類別的數量,以及那些文章的數量,如果沒有,則爲'0'。MySQL嵌套設置子類別計算慢

我以防萬一搜查,發現了兩個可能的解決方案,但沒有他們的工作:
MySQL & nested set: slow JOIN (not using index)
Why isn't MySQL using any of these possible keys?

創建表格類別:

CREATE TABLE `categories` (
    `GROUP_ID` varchar(255) CHARACTER SET utf8 NOT NULL, 
    `GROUP_NAME` varchar(255) CHARACTER SET utf8 NOT NULL, 
    `PARENT_ID` varchar(255) CHARACTER SET utf8 NOT NULL, 
    `TYPE` enum('root','node','leaf') CHARACTER SET utf8 NOT NULL DEFAULT 'node', 
    `LEVEL` tinyint(2) NOT NULL DEFAULT '0', 
    `GROUP_ORDER` int(11) NOT NULL, 
    `GROUP_DESCRIPTION` text CHARACTER SET utf8 NOT NULL, 
    `total_articles` int(11) unsigned NOT NULL DEFAULT '0', 
    `total_cats` int(11) unsigned NOT NULL DEFAULT '0', 
    `lft` smallint(5) unsigned NOT NULL DEFAULT '0', 
    `rgt` smallint(5) unsigned NOT NULL DEFAULT '0', 
    PRIMARY KEY (`GROUP_ID`), 
    KEY `PARENT_ID` (`PARENT_ID`), 
    KEY `lft` (`lft`), 
    KEY `rgt` (`rgt`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci 

total_cats是子類中的量行樹。
以下查詢將完全按照我的要求進行:所有子類別和文章數量。 但它很慢。在〜5000個類別和〜40000個文章上執行需要超過80秒的時間。total_articles的計算已由另一個腳本完成。 (如果存在的arent任何物品,所有行應持有0total_articles

查詢:

SELECT a.GROUP_ID,a.PARENT_ID,COUNT(b.GROUP_ID) as total_cats,(
    SELECT SUM(c.total_articles) 
    FROM categories c 
    WHERE c.PARENT_ID = a.GROUP_ID) as total_articles 
FROM categories as b 
    INNER JOIN categories as a 
    ON a.lft < b.lft AND a.rgt > b.rgt 
GROUP BY a.GROUP_ID 

它導致這樣的事情:

+-------------------------------------------+-------------------------------------+------------+----------------+ 
| GROUP_ID         | PARENT_ID       | total_cats | total_articles | 
+-------------------------------------------+-------------------------------------+------------+----------------+ 
| 69_69_1         | 69_69_0        |  4252 |    0 | 
| 69_69_Abfall__Wertstoffsammler___zubehoer | 69_69_NWEAB290h001     |   5 |    20 | 
| 69_69_Abisolierzangen      | 69_69_NWAAA458h001     |   4 |    56 | 
| 69_69_Abzieher_2       | 69_69_NWAAB944h001     |   23 |   476 | 
| 69_69_Abziehvorrichtung     | 69_69_Abzieher_2     |   3 |    18 | 
| 69_69_Aexte        | 69_69_NWEAA615h001     |   6 |    45 | 
| 69_69_Alarmgeraete_Melder     | 69_69_Sicherungstechnik__Heimschutz |   3 |    4 | 
| 69_69_Allgemeiner_Industriebedarf   | 69_69_Industrieausruestung   |   8 |    21 | 
| 69_69_Allgemeines_Schweisszubehoer  | 69_69_NWEAB113h001     |   27 |    97 | 
| 69_69_Anker__Befestigungstechnik__1  | 69_69_Befestigungstechnik   |   5 |   163 | 

的解釋是否有幫助:

+----+--------------------+-------+------+---------------+-----------+---------+------+------+------------------------------------------------+ 
| id | select_type  | table | type | possible_keys | key  | key_len | ref | rows | Extra           | 
+----+--------------------+-------+------+---------------+-----------+---------+------+------+------------------------------------------------+ 
| 1 | PRIMARY   | b  | ALL | lft,rgt  | NULL  | NULL | NULL | 4253 | Using temporary; Using filesort    | 
| 1 | PRIMARY   | a  | ALL | lft,rgt  | NULL  | NULL | NULL | 4253 | Range checked for each record (index map: 0xC) | 
| 2 | DEPENDENT SUBQUERY | c  | ref | PARENT_ID  | PARENT_ID | 767  | func | 7 | NULL           | 
+----+--------------------+-------+------+---------------+-----------+---------+------+------+------------------------------------------------+ 

正如你所看到的,它不使用索引。如果我把FORCE INDEX (lft,rgt)放在JOIN旁邊,查詢就會執行,但是沒有任何變化。還嘗試對列LFT和右添加索引:

ALTER TABLE `categories` ADD INDEX `nestedset` (`lft`, `rgt`); 

但是,這並不幫助都沒有。查詢仍然很慢。

有趣的是:如果類別表剛剛填充少量行, 260.但是,如果它達到1000+以上,它會變得越來越慢。

具有〜4000類別的示例數據:http://pastebin.com/BsViwFM5其大文件!
感謝您的幫助和提示!

+0

也許最好問dba.stackexchange? – davejal

+0

也許你是對的,但其他人也會問類似的情況,所以如果有人想遷移它,隨時可以這樣做=) – UnskilledFreak

+0

順便說一句,無論INT字出現在哪裏,它後面的數字都是毫無意義的 – Strawberry

回答

1

這是什麼樣的解釋?

SELECT a.GROUP_ID 
    , a.PARENT_ID 
    , COUNT(b.GROUP_ID) total_cats 
    , c.total_articles 
    FROM categories b 
    JOIN categories a 
    ON a.lft < b.lft 
    AND a.rgt > b.rgt 
    JOIN 
    (SELECT parent_id 
      , SUM(total_articles) total_articles 
     FROM categories 
     GROUP 
      BY parent_id 
    ) c 
    ON c.parent_id = a.GROUP_ID 
GROUP 
    BY a.GROUP_ID 
+0

您的查詢比我的查詢快,但運行時間約爲10秒。你可以在這裏找到解釋:http://pastebin.com/ZH7cTCGn – UnskilledFreak

+0

我想我會嘗試用lft,rgt的覆蓋索引。我看不到還有其他許多事情可以做 - 但其他人可能會進一步幫助 – Strawberry

+0

測試,並添加上面的2列索引nestedset。解釋仍然沒有顯示使用這個新密鑰,但查詢速度提高了1秒。無論如何,這是一個很好的改善80> 10! – UnskilledFreak

0

右左樹是一個可愛的「教科書」的技術。但是,正如你所發現的那樣,它並沒有擴大到「真實世界」。

EXPLAIN表明它掃描了所有的b,那麼對於每個這樣的行,它正在掃描所有的a。這是訂單(N^2) - 5000 * 5000 = 2500萬次操作。

其實,這個相對較新的操作(Range checked for each record (index map: 0xC))意味着它不是那麼糟糕。

優化器在找到'中介'方面確實做得不太好,因爲缺少信息的一點:範圍是否重疊。

您的任務可能可以通過切換到分層架構以及在應用程序代碼或「存儲例程」中「漫遊」樹來更好地實現。

通過MariaDB 10.2或MySQL 8.0,您可以編寫一個「遞歸CTE」,儘可能複雜的查詢遍歷樹。

+0

是的,我認爲它的主要「問題」是中介。我已經有了一個經過測試的遞歸函數,在php(parent-> groups等)中完成,但速度更慢。也許你可以提供一個示例查詢嗎? – UnskilledFreak

+0

範圍如何重疊!?!? – Strawberry

+0

他們呢?好的問題:/ – UnskilledFreak