【问题标题】:Mysql Function for parent child category父子类的Mysql函数
【发布时间】:2014-11-07 07:29:50
【问题描述】:

我有父子类别的表名 pctable。

  id      parent_id
  2322    0
  2323    2322
  2324    2322
  2335    2322
  2336    2322
  2337    2322
  4869    2322
  5121    2322
  6033    2322
  6783    2322
  1061    2323
  4870    4869
  4871    4869
  4872    4869
  4873    4869
  6034    6033
  6059    6033

我写了一个mysql函数来获取逗号分隔字符串中的所有父子。

DELIMITER $$
    DROP FUNCTION IF EXISTS getBaseID $$
    CREATE FUNCTION getBaseID(articleID varchar(1024)) RETURNS TEXT
    BEGIN
        DECLARE x TEXT;
        DECLARE y TEXT;
        DECLARE rtext TEXT;
        SET rtext = "";
        SET x = articleID;
        sloop:LOOP
            SET y = NULL;
            SELECT SQL_CACHE GROUP_CONCAT(id) INTO y FROM pctable WHERE parent_id IN(x);
            IF y IS NULL THEN
                LEAVE sloop;
            END IF;  
            SET x = y;
            SET rtext = CONCAT(rtext,',',x);
            ITERATE sloop;
        END LOOP;
        RETURN rtext;
    END $$

    DELIMITER ;

当我调用函数时,它返回了错误的数据。

  SELECT getBaseID(2322) FROM pctable LIMIT 1;

它回来了

 "2323,2324,2335,2336,2337,4869,5121,6033,6783,1061"

但它应该返回

  "2323,2324,2335,2336,2337,4869,5121,6033,6783,1061,4870,4871,4872,4873,6034,6059"

【问题讨论】:

  • 仅供参考,您应该查看Nested Sets 以管理分层数据。

标签: php mysql


【解决方案1】:

你的代码的问题是,这个

WHERE parent_id IN(x);

x 被替换为它的值实际上变成了这样:

WHERE parent_id IN(',2323,2324,2335,2336,2337,4869,5121,6033,6783,1061');

请注意其中的单引号。您认为您传递了一系列数字,但实际上您传递了一个文本字符串。另请注意,您的代码前面有这个逗号。因此,即使它不会传递一个文本字符串,它也会因为语法错误而失败。你需要做的是使用动态sql。这仅适用于存储过程,而不是函数。

我已经为你重写了你的代码:

DELIMITER $$
DROP PROCEDURE IF EXISTS getBaseID $$
CREATE PROCEDURE getBaseID(IN articleID varchar(1024), OUT rtext TEXT)
BEGIN
    SET rtext = "-1";
    SET @x = articleID;
    sloop:LOOP
        SET @y = NULL;
        SET @sql = CONCAT('SELECT GROUP_CONCAT(id) INTO @y FROM pctable WHERE parent_id IN(', @x, ');');
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        IF @y IS NULL THEN
            LEAVE sloop;
        END IF;  
        SET @x = @y;
        SET rtext = CONCAT(rtext, ',', @x);
        ITERATE sloop;
    END LOOP;
END $$

DELIMITER ;

我添加了

    SET rtext = "-1";

为了克服前导逗号的问题,假设您的表中没有否定的ids。

你会执行它

CALL getBaseID(2322, @my_result); /*This executes the function and writes the result into the variable you pass as second parameter.*/
SELECT @my_result;                /*Then you select the result.*/
  • sqlfiddle 中实时查看它的工作情况

最后一点,即使它适用于您的函数,在这种情况下您也不需要选择您的表。您可以简单地使用SELECT getBaseID(2322); 执行它,此函数中的任何内容都没有引用行或其他内容。

【讨论】:

    【解决方案2】:

    对于第一个循环,它会找到 parent_id 为 IN ("2322") 的所有记录,得到 2323,2324,2335,2336,2337,4869,5121,6033 和 6783。下一个循环它将找到所有parent_id 为 IN ("2323,2324,2335,2336,2337,4869,5121,6033,6783") 的记录。在基本理论中,这将一无所获,但我认为当它将一个 INT 与一个 CHAR 进行比较时,它会将 CHAR 转换为一个 INT,因此只需将所有内容都放在第一个逗号并有效地寻找返回的 IN ("2323") 1061. 由于 1061 不是任何记录的父项,这意味着 y 在下一次循环时为空,因此它会退出。

    快速解决方法是:-

    DELIMITER $$
        DROP FUNCTION IF EXISTS getBaseID $$
        CREATE FUNCTION getBaseID(articleID varchar(1024)) RETURNS TEXT
        BEGIN
            DECLARE x TEXT;
            DECLARE y TEXT;
            DECLARE rtext TEXT;
            SET rtext = "";
            SET x = articleID;
            sloop:LOOP
                SET y = NULL;
                SELECT SQL_CACHE GROUP_CONCAT(id) INTO y FROM pctable WHERE FIND_IN_SET(parent_id, x);
                IF y IS NULL THEN
                    LEAVE sloop;
                END IF;  
                SET x = y;
                SET rtext = CONCAT(rtext,',',x);
                ITERATE sloop;
            END LOOP;
            RETURN rtext;
        END $$
    
        DELIMITER ;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-11
      • 1970-01-01
      • 1970-01-01
      • 2013-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-10
      相关资源
      最近更新 更多