我有两张表ps_product
和ps_category
。产品表在本地数据库中有大约146690行,在远程数据库中有196000行,btree
和id_product
上有id_default_category
索引。类别表在btree
和id_category
列上有大约851行和id_parent
索引。
这些类别最多可分层次达到6级,我需要所有类别的所有产品。因此,如果产品53属于67类,而67类是子类别50,它是43的子类别......依此类推,直到1,即根类别,我将得到 53 - > 67-> 50- GT; 43-> 20→; 1→空
我已经找到了一个mysql查询,它使自己加入ps_category
6次来获取数据,并且在本地数据库上大约需要 0.8秒并且 - 5秒通过网络执行。有什么方法可以优化吗?查询:
SELECT
p.id_product, c.id_category, c1.id_category, c2.id_category, c3.id_category, c4.id_category, c5.id_category, c6.id_category
FROM `ps_category` c
left join ps_product p on p.id_category_default = c.id_category
left join ps_category c1 on c1.id_category = c.id_parent
left join ps_category c2 on c2.id_category = c1.id_parent
left join ps_category c3 on c3.id_category = c2.id_parent
left join ps_category c4 on c4.id_category = c3.id_parent
left join ps_category c5 on c5.id_category = c4.id_parent
left join ps_category c6 on c6.id_category = c5.id_parent
答案 0 :(得分:0)
尝试的一个选择是使用闭包表。
CREATE TABLE category_closure (
`a_catagory_id` SMALLINT UNSIGNED NOT NULL,
`d_category_id` SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (`a_category_id`,`d_category_id`)
) ENGINE=InnoDB
此表记录层次结构中各级别类别之间的关系。 a_category_id
指的是关系中的祖先,而d_category_id
指的是后代。
为了使表正常工作,必须将每个类别作为其自己的祖先和后代输入到表中。
INSERT INTO category_closure
(a_category_id, d_category_id)
SELECT
id_category,
id_category
FROM ps_category
然后,您可以在每个类别的id_parent
列中输入您已知的关系。
INSERT INTO category_closure
(a_category_id, d_category_id)
SELECT
id_parent,
id_category
FROM ps_category
WHERE id_parent IS NOT NULL
最后,你需要连接点。在do..while
循环中运行以下SELECT,只要从中返回行,将这些行插入闭包表并继续循环。
SELECT
cc1.a_category_id,
cc2.d_category_id
FROM category_closure cc1
INNER JOIN category_closure cc2
ON cc2.a_category_id = cc1.d_category_id
LEFT OUTER JOIN category_closure missing_cc
ON missing_cc.a_category_id = cc1.a_category_id
AND missing_cc.d_category_id = cc2.d_category_id
WHERE missing_cc.a_category_id IS NULL
这个查询的作用是它需要所有现有关系,并找到应该存在的内容。例如,您有以下链:
53> 67> 50
这意味着您将从前两个INSERT中获得以下记录: (50,50) (67,67) (53,53) (50,67) (67,53) (和其他人)。
现在,我们需要的是(50,53),因为53是50的后代。在上面的SELECT查询中,cc1
将匹配(50,67)记录。 cc2
将匹配(67,53)记录。这意味着missing_cc
正在尝试与来自cc1
(a_category_id
)的50和来自cc2
(d_category_id
)的53匹配。
由于最初不存在这样的记录,SELECT语句将返回这两行,您可以插入它们,然后重复。这一次,进一步向上发展。您不需要(或您的程序)知道有多少层,只需继续直到SELECT找不到更多结果。
最后,一旦构建了闭包表,就可以选择相关信息:
SELECT
p.id_product,
c.id_category,
GROUP_CONCAT(cc.a_category_id) AS parent_category_ids
FROM ps_category c
LEFT OUTER JOIN ps_product p
ON p.id_category_default = c.id_category
LEFT OUTER JOIN category_closure cc
ON cc.d_category_id = c.id_category
AND cc.a_category_id != c.id_category
GROUP BY c.id_category, p.id_product
这将选择所有类别,每个类别中的所有产品,然后为每个组合提供以逗号分隔的祖先类别列表。
现在,这实际上是相当多的重复信息,因为您可以将类别及其祖先与产品分开运行,但它实际上归结为您打算如何使用这些数据,是否可以分开查询与否。
注意:如果要添加和删除类别,则在删除所有当前关闭条目后,每次执行此操作时都必须重复此过程。有更好的方法可以做到这一点,而不是这里显示的,这样的方法甚至可以让你放弃id_parent
列的使用(特别是如果层次结构是非循环的),但这样的事情超出了这个范围问题
这个答案至少可以让你尝试一些东西,而不必改变你的应用程序或任何现有数据。