用于父子类的Mysql函数

时间:2014-11-07 07:29:50

标签: php mysql

我有父子类别的表名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"

2 个答案:

答案 0 :(得分: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";

使用前导逗号来解决问题,假设您的表中没有否定id

您可以使用

执行它
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.*/

最后一点,即使它适用于您的功能,在这种情况下您也不需要选择表格。你可以用SELECT getBaseID(2322);简单地执行它。这个函数中的任何内容都不是指行或什么的。

答案 1 :(得分:1)

对于第一个循环,它将找到所有记录,其中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 ;