【问题标题】:Recursion in a single MySQL query?单个 MySQL 查询中的递归?
【发布时间】:2014-09-11 06:41:52
【问题描述】:

我已经建立了一个网络/树,现在,从一个节点开始,我试图选择位于这个节点和另一个节点之间的所有节点——全部在一个查询中。

假设有两张表,一张用于存储具有属性的对象,另一张用于存储它们的(直接)关系。让我们保持示例简单。

CREATE TABLE objects (id varchar(64) PRIMARY KEY);
INSERT INTO objects VALUES ('seat'), ('chair'), ('furniture'), ('barstool'), ('stool');

CREATE TABLE parentships ( parent varchar(64) NOT NULL, child varchar(64) NOT NULL, PRIMARY KEY (parent, child), CONSTRAINT fk_child FOREIGN KEY (child) REFERENCES objects (id), CONSTRAINT fk_parent FOREIGN KEY (parent) REFERENCES objects (id) );
INSERT INTO parentships VALUES ('furniture', 'seat'), ('seat', 'chair'), ('seat', 'stool'), ('stool', 'barstool');

家具是根/端节点。让我试着想象一下这个小网络/树。

furniture --- seat -+- chair
                    |
                    +- stool --- barstool

我假设我需要使用SUBSELECT、LOOP 和过程创建递归,但我以前从未使用过LOOP 或自制过程。也许你可以帮我解释一下,而我会继续自己搜索/尝试。

{magic query (parameter 'barstool')}

+-----------+
| parent    |
+-----------+   <--- This is what I want.
| stool     |
| chair     |
| seat      |
| furniture |
+-----------+

我已经找到了一种手动执行前两步迭代的方法。

SET @p = '';
SET @p = ( SELECT parent FROM parentships WHERE child IN ('barstool', @p) );

SELECT parent FROM parentships WHERE child IN ('barstool', @p);

但是循环的文档让我很困惑。这是我的尝试,但我收到了错误消息:

CREATE PROCEDURE find_all_ancestors(node varchar)
BEGIN
  SET @p = '';
  label1: LOOP
    SET @p = ( SELECT parent FROM parentships WHERE child IN (node, @p) );
    IF @p != 'furniture' THEN
      ITERATE label1
    END IF
    LEAVE label1
  END LOOP label1
  SET @x = @p
END

但是我收到了几条错误消息,从这个开始:

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'label1: LOOP
    SET @p = ( SELECT parent FROM parentships WHERE child ' at line 4

另外,无论如何,我都需要将迭代的@p 添加到中间结果数组中,而 MySQL 不允许使用数组。所以也许我完全使用了错误的方法。

干杯。

【问题讨论】:

    标签: mysql loops recursion subquery


    【解决方案1】:

    试试下面的版本 - 这是您的代码,只是稍作修改。 执行后,它会返回带有相应值的逗号分隔列表。

    我还在代码中放置了一些 cmets。

    DELIMITER // /* It enables the ; delimiter used in the procedure body to be passed through to the server rather than being interpreted by mysql itself*/
    
    DROP PROCEDURE IF EXISTS find_all_ancestors // 
    
    CREATE PROCEDURE find_all_ancestors(node varchar(100)) # /*Need to define number of characters for varchar */
    
    BEGIN
    
      SET @p = node;
      SET @s = '';
      SET @pc = '';
      SET @c = 0;
    
      label1: LOOP
    
        IF @p != 'furniture' AND @p IN (@s) = 0 THEN
    
            SET @c = @c + 1;
    
            SET @p = ( SELECT parent FROM parentships WHERE child IN (@p) LIMIT 1 );
    
            SET @s = CONCAT(@s,',',@p);
    
            IF @c = 2 AND @p IN (@s) = 0 THEN
    
                SET @pc = ( SELECT child FROM parentships WHERE parent IN (@p) LIMIT 1 );
    
                SET @s = CONCAT(@s,',',@pc);
    
            END IF;
    
            ITERATE label1;
    
        END IF;
    
        LEAVE label1;
    
      END LOOP label1;
    
        SELECT SUBSTRING(@s,2,100) AS RESULT FROM DUAL; 
    
    END //
    
    DELIMITER ;
    

    创建过程后,您可以运行它:

    CALL find_all_ancestors('barstool');
    

    也寻找一些关于分层检索的东西 - https://www.google.com/search?q=hierarchical+retrieval+in+mysql。可能对你有帮助。

    还有很多其他方法可以做这种事情。看你的需要。

    【讨论】:

    • 很抱歉让您等了这么久,但为了升级我的 PHP 配置,我破坏了我的 XAMPP,现在在修复之前我无法访问我的 MySQL 内容。 — 不过,您的方法看起来正是我正在寻找的。特别感谢代码中的 cmets。
    • 很抱歉这么晚才回复你,但这正是我最终设置和经常使用的东西(不记得设置是多么困难)。问题已回答。
    猜你喜欢
    • 2020-07-29
    • 2023-03-12
    • 2011-04-11
    • 1970-01-01
    • 1970-01-01
    • 2019-03-09
    • 2012-10-08
    • 2015-05-05
    • 1970-01-01
    相关资源
    最近更新 更多