如何在 MySQL 上正确循环存储函数?
我在正确执行一个非常简单的存储过程时遇到了一些困难。 考虑以下文章表片段:
id replaced_by baseID
1 2 0
2 3 0
3 0 0
一个简单的分层表,使用写时复制。编辑文章时,当前文章的 Replaced_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
和 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 ;
I am having some difficulty getting a pretty simple stored procedure right.
Consider the following article table snippet:
id replaced_by baseID
1 2 0
2 3 0
3 0 0
A simple hierarchical table, using copy-on-write. When an article is edited, the replaced_by field of the current article is set to the id of it's new copy.
I've added a baseID field, which in the future should store the baseID of an article.
In my example above, there is one article (eg id 3). It's baseID would be 1.
To get the baseID, I have created the following stored procedure:
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 ;
It seems simple enough, until I actually call the function using:
SELECT getBaseID(3);
I would expect, the function to return 1. I'm even willing to understand it can take a slice of a second.
Instead, the machine's CPU goes up to 100% (mysqld).
I have even rewritten the same function using REPEAT .. UNTIL
and with WHILE .. DO
, with the same end result.
Can anyone explain why my CPU goes up 100% when it enters the loop?
Side note: I am trying to simply win time. I have created the exact same function in PHP, which performs okay, but our guess is that MySQL can do it slightly faster. We need to sift through about 18 million records. Any bit of time I can save is going to be worth it.
Thanks in advance for any assistance and/or pointers.
Solved 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 ;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
来自 mysql :
因此,当未找到给定
x
的记录时,就会出现无限循环(y
保持不变)尝试使用
SET y = (SELECT id ....)
或在 select 语句之前添加SET y = null
(它应该是循环中的第一个语句)From mysql :
So you have an infinite loop when no records found with a given
x
(y
remains unchanged)Try
SET y = (SELECT id ....)
instead or addSET y = null
before your select statement (it should be the first statement in the loop)感觉您可能缺少 Replaced_by 列上的索引。如果 Replaced_by 上没有索引,则每次迭代都会进行全表扫描。
之前确保该行存在
您还应该在检索两倍的 SQL 调用
,但安全总比后悔好。尝试一下!
It feels like you may be missing an index on the replaced_by column. If there no index on replaced_by, you are looking at a full table scan with every iteration.
You should also make sure the row exists before retrieving
Twice as many SQL calls but better safe than sorry.
Give it a Try !!!