如何遍历表的所有行?(MySQL)


89

我有一个表A,并且有一个主键ID。

现在我要遍历A中的所有行。

我发现类似“针对A中的每个记录”的内容,但这似乎并不是您在MySQL中的处理方式。

我想为每一行获取一个字段并对其进行转换,将其插入到另一个表中,然后更新该行的某些字段。我可以将select部分和insert放入一个语句中,但是我也不知道如何在其中进行更新。所以我想循环。实际上,除了MySQL,我不想使用其他任何东西。

编辑

我将不胜感激。

并且不需要将其放入程序中的解决方案。

编辑2

好吧,请考虑这种情况:

表A和B,每个都有ID和VAL字段。

现在,这是我想做的伪代码:

for(each row in A as rowA)
{
  insert into B(ID, VAL) values(rowA[ID], rowA[VAL]);
}

基本上是使用循环将A的内容复制到B中。

(这只是一个简化的示例,当然您不会为此使用循环。)


嗨,拉菲尔(Rafeel)为我带来了如下错误:我的SQL> for(wp_mobiune_ecommune_user中的每一行都以x){插入wp_mobiune_ecommune_user(gender_new)值(x [gender])}} RROR 1064(42000):您的SQL语法有错误;检查与您的MySQL服务器版本相对应的手册以获取正确的语法,以在'for(wp_mobiune_ecommune_user中的每一行以x开头){插入wp_mobiune_ecommune'中使用(第1行)
dinesh kandpal

Answers:


135

由于建议使用循环意味着需要过程类型的解决方案。这是我的。

任何对从表中获取的任何一条记录进行处理的查询都可以包装在一个过程中,以使其遍历表的每一行,如下所示:

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;

然后是您示例中的过程(为清楚起见,使用了table_A和table_B)

CREATE PROCEDURE ROWPERROW()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
SELECT COUNT(*) FROM table_A INTO n;
SET i=0;
WHILE i<n DO 
  INSERT INTO table_B(ID, VAL) SELECT (ID, VAL) FROM table_A LIMIT i,1;
  SET i = i + 1;
END WHILE;
End;
;;

然后不要忘记重置定界符

DELIMITER ;

并运行新程序

CALL ROWPERROW();

您可以在我从示例请求中简单复制的“ INSERT INTO”行中执行任何操作。

请注意,此处使用的“ INSERT INTO”行将反映问题中的行。根据此答案的注释,您需要确保查询在语法上对于所运行的SQL版本均正确。

在您的ID字段递增并从1开始的简单情况下,示例中的行可能变为:

INSERT INTO table_B(ID, VAL) VALUES(ID, VAL) FROM table_A WHERE ID=i;

将“ SELECT COUNT”行替换为

SET n=10;

仅让您在table_A的前10条记录中测试查询。

最后一件事。此过程也非常容易嵌套在不同的表中,这是我可以对一个表执行一个过程的唯一方法,该过程可将父表的每一行中不同数量的记录动态插入到新表中。

如果您需要它运行得更快,请确保尝试将其设置为基础,如果不是,那么就可以了。您也可以用游标形式重写上面的内容,但这可能不会提高性能。例如:

DROP PROCEDURE IF EXISTS cursor_ROWPERROW;
DELIMITER ;;

CREATE PROCEDURE cursor_ROWPERROW()
BEGIN
  DECLARE cursor_ID INT;
  DECLARE cursor_VAL VARCHAR;
  DECLARE done INT DEFAULT FALSE;
  DECLARE cursor_i CURSOR FOR SELECT ID,VAL FROM table_A;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
  OPEN cursor_i;
  read_loop: LOOP
    FETCH cursor_i INTO cursor_ID, cursor_VAL;
    IF done THEN
      LEAVE read_loop;
    END IF;
    INSERT INTO table_B(ID, VAL) VALUES(cursor_ID, cursor_VAL);
  END LOOP;
  CLOSE cursor_i;
END;
;;

请记住,声明要使用的变量与查询表中的变量相同。

我的建议是,尽可能使用基于集合的查询,并且在必要时仅使用简单的循环或游标。


2
从table_A LIMIT i,1插入table_B(ID,VAL)VALUES(ID,VAL)给出语法错误。
乔纳森(Jonathan)

1
似乎丢失了“ DECLARE done INT DEFAULT FALSE;” 在cursor_i声明之后
ErikL 2014年

1
在完成属性设置为true的位置,我应该将其放置或是mysql游标自动使用的保留字?
IgniteCoders 2015年

1
从SQL 5.5开始,INSERT INTO调用会引发语法错误,正确的调用是从table_A WHERE ID = i;插入INSERT INTO table_B(ID,VAL)SELECT(ID,VAL);
2016年

这将需要一辈子,所以试图通过改变限制和增量增加批量大小为1000:LIMIT i,1000;set i = i + 1000;
艾伦·

16

您应该真正使用基于集合的解决方案,其中涉及两个查询(基本插入):

INSERT INTO TableB (Id2Column, Column33, Column44)
SELECT id, column1, column2 FROM TableA

UPDATE TableA SET column1 = column2 * column3

对于您的转换:

INSERT INTO TableB (Id2Column, Column33, Column44)
SELECT 
    id, 
    column1 * column4 * 100, 
    (column2 / column12) 
FROM TableA

UPDATE TableA SET column1 = column2 * column3

现在,如果您的转换比这更复杂,并且涉及多个表,请发布另一个包含详细信息的问题。


范例为+1。尽管我怀疑这是否适用于我的现场b / c,但之后的更新与插入有关,尤其是所选行导致了哪些特定插入。这就是为什么我建议循环的原因,因此我可以分别为每行执行选择/插入/更新。
拉斐尔

2
@ Raffael1984:编辑您的问题,为“导致特定插入的特定行”添加条件,我们可以为您提供帮助。您真的不想走游标/循环路线-这效率极低。
Raj More

哦,好吧。。。您知道我很乐意采用基于集合的查询方式!但是这里的效率不是动力,因为我的问题更具有学术兴趣。桌子很小,我可以手工做。我真的很感谢循环版本。我可能会再次发布该问题,以提供更多详细信息,以允许有人帮助我使用基于集合的样式。
Raffael

同意@RajMore,游标/循环效率低下,以下是有关游标性能的2条链接供参考:1. rpbouman.blogspot.tw/2006/09/refactoring-mysql-cursors.html 2. stackoverflow.com/questions/11549567/ …
布朗尼·林2013年

@RajMore您能解决我的问题

4

CURSORS是这里的一个选项,但是由于它们经常不能充分利用查询引擎,因此人们通常会对此表示反对。考虑研究“基于SET的查询”,以了解是否可以在不使用CURSOR的情况下实现您想要的功能。


我找不到太多有关基于集合的查询的信息。但是我想你的意思是选择一种陈述。问题是我还需要执行更新。
Raffael

0

我在mysql触发器中使用过的Purple先生的示例,

begin
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
Select COUNT(*) from user where deleted_at is null INTO n;
SET i=0;
WHILE i<n DO 
  INSERT INTO user_notification(notification_id,status,userId)values(new.notification_id,1,(Select userId FROM user LIMIT i,1)) ;
  SET i = i + 1;
END WHILE;
end

2
您能否添加一些信息,您的解决方案如何工作?
TheTanic

-24
    Use this:

    $stmt = $user->runQuery("SELECT * FROM tbl WHERE ID=:id");
    $stmt->bindparam(":id",$id);
    $stmt->execute();

        $stmt->bindColumn("a_b",$xx);
        $stmt->bindColumn("c_d",$yy);


    while($rows = $stmt->fetch(PDO::FETCH_BOUND))
    {
        //---insert into new tble
    }   
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.