我有一张ID为ID的对象表,其中一些是基于其他对象的 为此我使用了一个名为path的字段,它列出了父母的字符串' ID
对象D(路径=" A,B,C")基于对象C,它基于B,基于A.
现在我想从所有对象中选择*,加上一个额外的列:count(后代)
(A有3(B,C和D)B有2(C和D),C有一(D) - D有零
"我"后代是path = myPath + myID(+ more?)
的对象数
- 这只能在SQL中实现(不用PHP循环)吗?
O id = a .... path ="" .......... a有5个后代
O id = b .... path =" a" ........ b有3
O id = c .... path =" a,b" ..... c有1
O id = d .... path =" a,b,c" .. d有0
O id = n .... path =" a,b" ..... n有0
O id = x .... path =" a" ........ x有0
答案 0 :(得分:2)
如果您需要经常查询,此表结构可能会出现问题。尽管MySQL有一种基本的读取方法,但很少建议在一个列中存储多个值。
根据您现有的要求,查询结果并非难以产生您想要的结果。使用LEFT JOIN
使用不同的别名将本身加入表中,您可以使用MySQL的FIND_IN_SET()
string function找到object
内的path
作为加入条件。
在加入之后,您可COUNT()
FIND_IN_SET()
来自LEFT JOIN
的匹配项,并且由于您使用了0
,因此对于没有后代的人,它会返回SELECT
o.*,
-- Count matches from the joined table
COUNT(odesc.object) AS num_descendants
FROM
paths o
-- Self join with FIND_IN_SET()
LEFT JOIN paths odesc ON FIND_IN_SET(o.object, odesc.path)
GROUP BY o.object
。
LIKE
在给出样本行的情况下,如果它正在运行并产生预期结果,则这是一个演示。 http://sqlfiddle.com/#!9/1fae7/1
现在,如果您的数据不像您的样本那样常规,那么可能仍然允许不完全遵循的路径,而只是将对象作为成员。添加额外的LEFT JOIN
条件可以强制 LEFT JOIN paths odesc ON
FIND_IN_SET(o.object, odesc.path)
-- Additional condition to ensure paths start the same
AND odesc.path LIKE CONCAT(COALESCE(o.path, ''), '%')
两侧的路径以相同的方式启动,这意味着一条路径扩展另一条路径。
FIND_IN_SET()
只是为了验证结果是否相同,http://sqlfiddle.com/#!9/1fae7/15
请注意,使用EXPLAIN
永远不会很快。这就是困难的原因 - MySQL没有很好的原生功能来分割字符串,也无法很好地利用索引。
我针对FIND_IN_SET()
查询运行+------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------------------------------------------+
| 1 | SIMPLE | o | index | NULL | path | 20 | NULL | 6 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | ox | index | NULL | path | 20 | NULL | 6 | Using where; Using index; Using join buffer (flat, BNL join) |
+------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------------------------------------------+
,并在两列中的每一列都有索引:
NULL
在更正源数据以使用尾随逗号和空字符串而不是EXPLAIN select paths.*, (select count(object) from paths ox where LEFT(ox.path,char_length( concat( paths.path, paths.object))) = concat(paths.path, paths.object ) )as descendants from paths;
+------+--------------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+--------------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| 1 | PRIMARY | paths | index | NULL | path | 20 | NULL | 6 | Using index |
| 2 | DEPENDENT SUBQUERY | ox | index | NULL | path | 20 | NULL | 6 | Using where; Using index |
+------+--------------------+-------+-------+---------------+------+---------+------+------+--------------------------+
之后,以下是注释中的从属子查询的说明:
LEFT JOIN
最后,使用subselect的修改数据表示为EXPLAIN SELECT
paths.*,
COUNT(ox.object)
FROM
paths
LEFT JOIN paths ox
ON LEFT(ox.path,char_length(concat(paths.path, paths.object))) = concat(paths.path, paths.object)
GROUP BY paths.object;
+------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------------------------------------------+
| 1 | SIMPLE | paths | index | NULL | path | 20 | NULL | 6 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | ox | index | NULL | path | 20 | NULL | 6 | Using where; Using index; Using join buffer (flat, BNL join) |
+------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------------------------------------------+
,MySQL可以更好地进行优化:
1) contents->add requirement
2) open the test case ->"test user-stories" tab
3) Links -> which adds a tested-by link for requirements
所有三个似乎都能够使用索引,但是您需要将它们与实际行集进行基准测试,以找出哪个最有效。重要的是,这些是针对最近的MariaDB版本运行的。如果你有一个较旧的MySQL,你的结果可能会有很大差异。
我发现修改原始数据以满足尾随逗号的要求有点令人反感。
答案 1 :(得分:1)
我假设你有一个表格,其中列为keyy,父亲为父母。我还假设父列中的每个父节点都被",#34;终止。 然后:
select t.*,
(select count(*)
from t tt
where tt.parents between concat( t.parents , t.keyy ,',' )
and concat(t.parents , t.keyy ,',zzzzzzzzzz' ) )as descendants
from t
如果您在列父项上有索引,则可以使用它。 也许你应该用更合理的东西取代zz。