【问题标题】:@ Symbol - a solution for Recursive SELECT query in Mysql?@Symbol——Mysql中递归SELECT查询的解决方案?
【发布时间】:2013-05-08 15:57:54
【问题描述】:

关于 Mysql 中的 Recursive SELECT 查询有很多问题,但大多数答案是“Mysql 中 Recursive SELECT 查询没有解决方案”。

其实有一定的解决方案&我想清楚的知道,所以这个问题是上一个问题的后续,可以在(how-to-do-the-recursive-select-query-in-mysql)找到

假设你有这张桌子:

col1 - col2 - col3
1    -  a   -  5
5    -  d   -  3
3    -  k   -  7
6    -  o   -  2
2    -  0   -  8

& 你想在 col1 中找到所有连接到值“1”的链接,即你想打印出来:

1 - a - 5
5 - d - 3
3 - k - 7

然后你可以使用这个简单的查询:

select col1, col2, @pv:=col3 as 'col3' from table1
join
(select @pv:=1)tmp
where col1=@pv

好的,但是,如果您的表在 col1 中有 2 条记录包含“1”,并且在 col1 中有 2 条包含“3”的记录,例如:

col1 - col2 - col3
1    -  a   -  5
1    -  m   -  9
5    -  d   -  3
3    -  k   -  7
6    -  o   -  2
3    -  v   -  10
2    -  0   -  8

然后,当用户在 col1 中搜索“1”时,它应该显示连接到 2“1”的所有链接,即它应该显示以下预期结果:

col1 - col2 - col3
1    -  a   -  5
1    -  m   -  9
5    -  d   -  3
3    -  k   -  7
3    -  v   -  10

所以,我的问题是我们如何修改上述查询,以便它显示所有链接,如上述预期结果?

编辑:@戈登, 但是如果我们省略 select distinct col1, col2 from 那么这个查询就意味着什么,你能解决这个问题吗(因为 childID 增加了,所以我们可以订购 table1 ):

select col1, col2,
         @pv:=(case when find_in_set(col3, @pv) then @pv else concat(@pv, ',', col3) 
               end) as 'col3'
  from (select * from table1 order by col1) tb1 join
      (select @pv:='1') tmp
      on find_in_set(col1, @pv) > 0

在这种情况下,我们不用担心顺序,例如,如果这是数据:

col1 - col2 - col3
4    -  a   -  5
1    -  d   -  2
1    -  k   -  4
2    -  o   -  3
6    -  k   -  8
8    -  o   -  9

输出将是:

col1 - col2 - col3
1    -  d   -  1,2
1    -  k   -  1,2,4
2    -  o   -  1,2,4,3

所以我们得到这个结果1,2,4,3 对吗? & 如果 col1 在1,2,4,3 中,我们只选择所有记录。然后我们可以得到最终的预期结果。

如果是这样,你能想出什么特殊情况排除我刚才提到的解决方案吗?

【问题讨论】:

  • 这个问题似乎在于它依赖于以正确顺序返回的记录。例如,如果我将您的测试数据插入到表中,但以相反的顺序插入,您的 SQL 仅返回第一行(其中 col1 = 1)。其他记录在找到其父母之前已被处理和丢弃。为了解决这个问题,您需要添加一个 order 子句,并且没有明显的方法可以为您的数据执行此操作。下面@GordonLinoff 的解决方案同样受到影响。
  • 是的,这是第二个问题
  • 也许,那些说递归 SELECT 没有解决方案的人想到了第二个问题。 (意思是,毕竟可能没有实际的、可靠的解决方案。)

标签: mysql sql recursive-query sql-optimization


【解决方案1】:

我一直想知道这样的事情是否可行:

select distinct col1, col2
from (select col1, col2,
             @pv:=(case when find_in_set(col3, @pv) then @pv else concat(@pv, ',', col3) 
                   end) as 'col3'
      from table1 join
          (select @pv:='1') tmp
          on find_in_set(col1, @pv) > 0
     ) t

这样的东西应该适用于小型数据集。但是,将所有 id 放在一个字符串中的想法仅限于字符串的容量。

【讨论】:

  • Thax u Gordon,它几乎就在那里,就像 Kicktart 所说,如果它以相反的顺序进行,你的查询将不起作用。你知道如何解决订单问题吗?
  • 该字符串可能不是问题,因为只有 20-30 个链接?你这么认为吗?
【解决方案2】:

在我有限的层级深度中,我使用了以下内容:

父母:

select * from mytable
join (
    select A.id Aid,B.id Bid, C.id Cid, D.id Did, E.id Eid, F.id Fid,G.id Gid, H.id Hid from mytable A
    left join mytable B on B.id=A.parent
    left join mytable C on C.id=B.parent
    left join mytable D on D.id=C.parent
    left join mytable E on E.id=D.parent
    left join mytable F on F.id=E.parent
    left join mytable G on G.id=F.parent
    left join mytable H on H.id=G.parent
    where A.id=9
) X
where id in (Aid,Bid,Cid,Did,Eid,Fid,Gid,Hid);

孩子们:

select * from mytable where id in (
select distinct id from mytable
join (
    select A.id Aid,B.id Bid, C.id Cid, D.id Did, E.id Eid, F.id Fid,G.id Gid, H.id Hid FROM mytable A
    left join mytable B on B.parent=A.id
    left join mytable C on C.parent=B.id
    left join mytable D on D.parent=C.id
    left join mytable E on E.parent=D.id
    left join mytable F on F.parent=E.id
    left join mytable G on G.parent=F.id
    left join mytable H on H.parent=G.id
    Where A.id=1
) X
where id in (Aid,Bid,Cid,Did,Eid,Fid,Gid,Hid)

);

【讨论】:

    【解决方案3】:

    有更多的发挥。由于项目的排序,无法使用用户变量使其工作。

    但是,如果您有一个合理的最大级别数,那么您可以执行以下操作:-

    SELECT CONCAT_WS('-', a.allCols, b.allCols, c.allCols, d.allCols, e.allCols, f.allCols, g.allCols, h.allCols, i.allCols, j.allCols, k.allCols, l.allCols, m.allCols)
    FROM (SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) a
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) b ON a.col3 = b.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) c ON b.col3 = c.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) d ON c.col3 = d.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) e ON d.col3 = e.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) f ON e.col3 = f.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) g ON f.col3 = g.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) h ON g.col3 = h.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) i ON h.col3 = i.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) j ON i.col3 = j.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) k ON j.col3 = k.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) l ON k.col3 = l.col1
    LEFT OUTER JOIN(SELECT col1, col3, CONCAT(col1, col2, col3) AS allCols FROM table1) m ON l.col3 = m.col1
    WHERE a.col1 = 1
    

    这可以处理多达 13 个级别(好吧,您的测试数据中只使用了几个级别),并且将为每一列提供一个逗号分隔的位,每一行用破折号 (-) 连接。

    【讨论】:

    • 但是链接的数量是未知的,即它可能只有3个链接或5个链接或20个链接
    • 没关系,只要你至少有尽可能多的连接。你说只有20~30个链接,应该很容易用这个覆盖。
    • 我坚持使用 Gordon 的解决方案。还是谢谢你。
    • 如果插入顺序不是问题,那就足够了。
    【解决方案4】:

    存储过程是最好的方法。因为只有当数据遵循相同的顺序时,Gordon 的解决方案才有效。

    如果我们有这样的表结构

    col1 - col2 - col3
    3 - k - 7
    5 - d - 3
    1 - a - 5
    6 - o - 2
    2 - 0 - 8
    

    它不会工作。

    这是实现相同的示例程序代码。

    delimiter //
    CREATE PROCEDURE chainReaction 
    (
        in inputNo int
    ) 
    BEGIN 
        declare final_id int default NULL;
        SELECT col3 into final_id from table1
        where col1 = inputNo;
        if( final_id is not null) then
            insert into results(select col1, col2, col3 from table1 where col1 = inputNo);
            CALL chainReaction(final_id);   
        end if;
    END//
    delimiter ;
    
    call chainReaction(1);
    select * from results;
    drop table if exists results;
    

    【讨论】:

      猜你喜欢
      • 2013-07-22
      • 2013-03-22
      • 1970-01-01
      • 2015-10-25
      • 1970-01-01
      • 1970-01-01
      • 2021-11-03
      • 2018-12-30
      • 2018-11-30
      相关资源
      最近更新 更多