Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/57.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 计算同一表中的子体数_Php_Mysql_Sql_Inner Join - Fatal编程技术网

Php 计算同一表中的子体数

Php 计算同一表中的子体数,php,mysql,sql,inner-join,Php,Mysql,Sql,Inner Join,我有一个ID为的对象表,其中一些基于其他对象。 为此,我使用了一个名为path的字段,它列出了家长ID的字符串 对象D路径=A,B,C基于对象C,对象C基于B,对象B基于A 现在,我想从所有对象中选择*,再加上一个额外的列:countsubstands A有3个B,C和db有2个C和D,C只有一个D-D为零 我的后代是path=myPath+myID+more的对象数? -这是否仅在SQL中可能,而在PHP中没有循环 O id=a。。。。路径=。。。。。。。。。。a有5个后代 O id=b。。。

我有一个ID为的对象表,其中一些基于其他对象。 为此,我使用了一个名为path的字段,它列出了家长ID的字符串

对象D路径=A,B,C基于对象C,对象C基于B,对象B基于A

现在,我想从所有对象中选择*,再加上一个额外的列:countsubstands A有3个B,C和db有2个C和D,C只有一个D-D为零

我的后代是path=myPath+myID+more的对象数? -这是否仅在SQL中可能,而在PHP中没有循环

O id=a。。。。路径=。。。。。。。。。。a有5个后代 O id=b。。。。路径=a。。。。。。。。b有3个 O id=c。。。。路径=a,b。。。。。c有1个 O id=d。。。。路径=a、b、c。。d有0 O id=n。。。。路径=a,b。。。。。n有0
O id=x。。。。路径=a。。。。。。。。x有0

如果需要频繁查询,此表结构可能会出现问题。很少建议在一个列中存储多个值,尽管MySQL有一种基本的读取方法

但是,考虑到您现有的需求,查询结果并不是很难产生您想要的结果。使用左连接以不同的别名连接表本身,可以使用MySQL在路径中定位对象作为连接条件

在连接之后,可以从\u集中的FIND\u计算匹配项,并且由于使用了左连接,因此对于没有后代的连接,它将返回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条件可以强制左侧连接两侧的路径以相同的方式开始,这意味着一条路径扩展另一条路径

 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永远不会很快。这正是造成这一困难的原因——MySQL没有很好的用于拆分字符串的本机功能,也无法很好地利用索引

增编: 我对FIND_IN_SET查询运行了EXPLAIN,两列中的每一列都有一个索引:

+------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------------------------------------------+
| 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之后,下面是注释中的从属子查询的解释:

最后,使用subselect修改的数据表示为左连接,MySQL可以更好地优化:

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) |
+------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------------------------------------------+
这三种方法似乎都可以使用索引,但您需要将它们与实际行集进行对比,以找出哪种方法最有效。重要的是,这些都是针对最近的MariaDB版本运行的。如果您使用的是较旧的MySQL,那么结果可能会有很大差异


我发现修改原始数据以满足尾随逗号的要求有些令人不快。

我假设您有一个表,其中id列为keyy,父项列为parents。我还假设parents列中的每个父项都以终止,。 然后:

如果列父级上有索引,则可以使用它。 也许你应该用更合理的东西来代替zz

见:

退房请澄清。您是否试图根据最长链推断子体的数量,或者例如,表中的对象C是否已经将D作为其路径值?如果我理解的话,由于多值列的原因,如果所有行都存在路径,这将非常困难。但是,如果这些都没有在整个链中填写,那么在SQL中就很难做到。请从表中发布一个行示例以消除歧义。这些行都已填写-所有祖先都在路径字段中按顺序排列。考虑到您刚才包含的示例,我想我仍然误解了。根据我的理解,a应该有5个后代,而不是4个,因为b、c、d、n、x在它们的路径中都有a*。这是一个错误吗?太棒了-有了FIND_IN_SET,即使是一棵树上有洞也能工作,而且有多行父母。。。当然是一件令人憎恶的事。。。但是如果它很慢。。。我无法计算路径完全匹配myPath+,+myID的对象数。。使用SUBSTRING和CHAR_LENGTH更原始,但更快?@T4NK3R这些字符串操作中的任何一个可能都不会比在_集中查找_快多少,如果有的话,除非可以严格使用左锚定比较来完成,以便可以使用索引。我现在必须运行,但是如果我想到一个我尝试过的解决方案2或3不起作用,我会在以后编辑添加它。我想得越多,避免在集合中查找就越复杂-因为你需要对空路径和逗号等进行特殊的大小写处理。我无法让小提琴处理你的左连接和分组。。。但是这个匹配看起来对索引更友好:left ox.path,char_lengthcontato.path,o.object=concato.path,o.object这不会很有效,因为串联会导致值
像A,X,当A…X的完整路径不存在时,它永远不存在。只要样本中有像X或N这样的实例,其中的路径不是之前所有路径的精确组合,我认为您将无法依赖子字符串。我多次尝试使用通配符,如CONCAT和SUBSTR,但这些行总是导致不正确的结果。我还有几件事要用通配符试试,比如…也很有效。这显然是什么?其中LEFTtt.parents,char_lengthcont t.parents,t.keyy=concat t.parents,t.keyy-索引freindly lefty的单个匹配实际上不适用于路径为a的X的情况。它不会被识别为a的后代。LEFTCHAR_索引版本将在该测试集中为c返回一个不正确的值,即2个后代,而不是1个后代。这些方法只适用于完全连续的路径A、B、C、D……N……辛罗兰的数据X不是A的后代。X与A并列:对于他的数据,这个左撇子版本有效:选择t.*,从t tt中选择count*,其中LEFTtt.parents,char_lengthcontat t.parents,t.keyy,=concat t.parents,t.keyy,,作为TTE的后代,上面的额外komma只需要区分C和C2-如果所有ID的长度相同,就不需要了
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) |
+------+-------------+-------+-------+---------------+------+---------+------+------+--------------------------------------------------------------+
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