2011-08-11 37 views
4

我有一些困难得到一个非常简单的存储过程的权利。 考虑下面的文章表片断:如何在MySQL中正确循环存储的函数?

id replaced_by  baseID 
1    2   0 
2    3   0 
3    0   0 

一个简单的分级表,使用写入时复制。编辑文章时,当前文章的replaced_by字段被设置为其新副本的标识。

我已经添加了一个baseID字段,它在将来应该存储一篇文章的baseID。 在我上面的例子中,有一篇文章(例如id 3)。这是baseid之后将为1

要获得baseid之后,我创建了以下存储过程:

DELIMITER $$ 

CREATE FUNCTION getBaseID(articleID INT) RETURNS INT 
BEGIN 
    DECLARE x INT; 
    DECLARE y INT; 
    SET x = articleID; 
    sloop:LOOP 
     SELECT id INTO y FROM article WHERE replaced_by_articleID = x; 
     IF y IS NOT NULL THEN 
      SET x = y; 
      ITERATE sloop; 
     ELSE 
      LEAVE sloop; 
     END IF; 
    END LOOP; 
    RETURN x; 
END $$ 

DELIMITER ; 

这看起来很简单,直到我用实际调用该函数:

SELECT getBaseID(3); 

我期望,函数返回1.我甚至愿意理解它可以花一秒钟。 相反,机器的CPU上升到100%(mysqld)。

我甚至用REPEAT .. UNTILWHILE .. DO重写了相同的函数,结果相同。

任何人都可以解释为什么我的CPU进入循环时100%上升?

附注:我试图简单地赢得时间。我已经在PHP中创建了完全相同的函数,它可以执行,但我们的猜测是MySQL可以稍快些。我们需要筛选约1800万条记录。任何时候我可以节省的时间都是值得的。

在此先感谢您的任何帮助和/或指针。


解决SQL:

DELIMITER $$ 

CREATE FUNCTION getBaseID(articleID INT) RETURNS INT 
BEGIN 
    DECLARE x INT; 
    DECLARE y INT; 
    SET x = articleID; 
    sloop:LOOP 
     SET y = NULL; 
     SELECT id INTO y FROM article WHERE replaced_by_articleID = x; 
     IF y IS NULL THEN 
      LEAVE sloop; 
     END IF; 
     SET x = y; 
     ITERATE sloop; 
    END LOOP; 
    RETURN x; 
END $$ 

DELIMITER ; 

回答

1

mysql

如果查询返回没有行,与错误代码的警告1329发生时(无数据),并且变量值保持不变

因此,当没有记录时,你有一个无限循环与给定x发现(y保持不变) 尝试SET y = (SELECT id ....)代替或SELECT语句前加上SET y = null(应该是在循环的第一条语句)

+0

Omg,非常感谢。我确实读到了INTO位上的那个页面,但我不明白他们的意思是变量不会改变。 – djBo

0

这种感觉就像你可能会丢失在replaced_by列的索引。如果replacement_by上没有索引,那么每次迭代都会查看全表扫描。

ALTER TABLE article ADD INDEX (replaced_by); 

您还应该确保行存在之前检索

DELIMITER $$ 

CREATE FUNCTION getBaseID(articleID INT) RETURNS INT 
BEGIN 
    DECLARE x INT; 
    DECLARE y INT; 
    SET x = articleID; 
    sloop:LOOP 
     SELECT COUNT(1) INTO y FROM article WHERE replaced_by = x; 
     IF y > 0 THEN 
      SELECT id INTO y FROM article WHERE replaced_by = x; 
      SET x = y; 
     ELSE 
      LEAVE sloop; 
     END IF; 
    END LOOP; 
    RETURN x; 
END $$ 

DELIMITER ; 

两倍多的SQL调用,但有备无患。

试试吧!

+0

是的,索引是可用的。但搜索不存在的行就是我正在测试的内容。它表明我何时找到了baseID! – djBo