8

我在获得一个非常简单的存储过程时遇到了一些困难。考虑以下文章表格片段:

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

一个简单的分层表,使用写时复制。编辑文章时,当前文章的 replace_by 字段设置为其新副本的 id。

我添加了一个 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 .. UNTIL和 with重写了相同的函数WHILE .. 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 ;
4

2 回答 2

2

mysql

如果查询没有返回任何行,则会出现错误代码为 1329 的警告(无数据),并且变量值保持不变

因此,当没有找到具有给定xy保持不变)的记录时,您有一个无限循环尝试SET y = (SELECT id ....)改为或SET y = null在您的 select 语句之前添加(它应该是循环中的第一个语句)

于 2011-08-11T19:30:32.637 回答
0

感觉就像您可能缺少replaced_by 列上的索引。如果replaced_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 调用,但比抱歉更安全。

试试看 !!!

于 2011-08-11T20:26:49.223 回答