MySQL和嵌套集:慢JOIN(不使用索引)

我有两个表格:

地点:

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;

地方表被实施为嵌套集合。

现在,当用户属于某个地区时(通过某种定位),他也属于其所有祖先(更高级别的地区)。 我需要一个查询来选择所有用户所属的所有地方。

这是我的尝试:

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)

这里的问题是执行需要太多的时间。

我咨询了EXPLAIN:

+----+-------------+-------+--------+---------------------------------+---------+---------+----------------------------------+-------+----------+-----------------+
| 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)

显然,最后的加入并不像我所期望的那样使用lft,rgt索引。 我很绝望。

更新:在@cairnz建议添加条件之后,查询仍需要太多时间来处理。

更新2:列名而不是星号

更新的查询:

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";

更新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) |
+----+-------------+-------+--------+-----------------------------------------+-----------------------------+---------+---------------------------------+-------+----------+-------------------------------------------------+

任何帮助赞赏。


啊,它发生在我身上。

由于您要求表中的所有内容,因此mysql决定使用全表扫描,因为它认为它更高效。

为了获得一些关键用法,添加一些过滤器来限制查找所有表中的每一行。

更新答案:

你的第二个查询没有意义。 你将会加入到lca中,但是你有一个过滤器,这会抵消左连接本身。 另外,您还需要查询查询的最后一步中的数据,这意味着您必须查看lt,lc和lca中的所有内容才能找到您的数据。 此外,您还没有索引位置上最左侧的“类型”列,因此您仍然需要全表扫描才能找到您的数据。

如果你有一些样本数据和你试图实现的例子,它可能会更容易帮助。


尝试尝试强制索引 - http://dev.mysql.com/doc/refman/5.1/en/index-hints.html,也许这只是优化器问题。


看起来你想要单一结果的父母。

根据在SQL中定义嵌套集的人,Joe Celko在http://www.ibase.ru/devinfo/DBMSTrees/sqltrees.html“这个模型是一种显示零件爆炸的自然方式,因为最终的装配是由物理嵌套组件组成,分解成不同的部分。“

换句话说,嵌套集用于在单个集合中有效地筛选儿童到任意数量的独立级别。 你有两张桌子,但是我看不到集合“定位”的属性不能被解除标准化为“地点”?

如果地方表有一个几何列,我能不能从一个“定位”中找到一个地点,然后使用一个过滤器在一个表上选择:parent.lft <= row.left AND parent.rgt> = row.rgt ?

更新

在这个回答https://stackoverflow.com/a/1743952/3018894,有一个来自http://explainextended.com/2009/09/29/adjacency-list-vs-nested-sets-mysql/的例子,其中以下示例将所有祖先的深度设置为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
链接地址: http://www.djcxy.com/p/57117.html

上一篇: MySQL & nested set: slow JOIN (not using index)

下一篇: How do I use jQuery FIle Upload in a nested form?