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

Posted

技术标签:

【中文标题】如何循环遍历表的所有行? (MySQL)【英文标题】:How can I loop through all rows of a table? (MySQL) 【发布时间】:2011-08-14 14:45:22 【问题描述】:

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

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

我发现了类似“针对 A 中的每条记录”的内容,但这似乎不是您在 mysql 中的做法。

事情是对于我想要获取一个字段并对其进行转换的每一行,将其插入另一个表中,然后更新该行的一些字段。我可以将选择部分和插入放在一个语句中,但我也不知道如何在其中获取更新。所以我想循环。为了练习,我不想使用 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 对我来说它给出的错误是:My SQL> for (wp_mobiune_ecommune_user 中的每一行作为 x) 插入 wp_mobiune_ecommune_user (gender_new) 值 (x[gender]); RROR 1064 (42000):您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以了解在第 1 行的“for(wp_mobiune_ecommune_user 中的每一行作为 x) insert into wp_mobiune_ecommune”附近使用的正确语法 【参考方案1】:

由于循环的建议意味着对过程类型解决方案的请求。这是我的。

对从表中提取的任何单个记录起作用的任何查询都可以包装在一个过程中,使其遍历表的每一行,如下所示:

首先删除任何现有的同名过程,并更改分隔符,这样您的 SQL 就不会在您尝试编写过程时尝试运行每一行。

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”行反映了问题中的行。根据这个答案的 cmets,您需要确保您的查询对于您正在运行的任何 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;
;;

请记住将您将使用的变量声明为与查询表中的变量相同的类型。

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

【讨论】:

INSERT INTO table_B(ID, VAL) VALUES(ID, VAL) FROM table_A LIMIT i,1;给出一个语法错误。 似乎缺少'DECLARE done INT DEFAULT FALSE;'在 cursor_i 声明之后 done属性在哪里设置为true,我应该放置它还是mysql游标自动使用的保留字? 从 SQL 5.5 开始,INSERT INTO 调用会引发语法错误,正确的调用是 INSERT INTO table_B(ID, VAL) SELECT (ID, VAL) FROM table_A WHERE ID=i; 这需要一个生命周期,所以尝试通过更改限制和增量将批量大小增加到 1000:LIMIT i,1000;set i = i + 1000;【参考方案2】:

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

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,但之后的更新与插入有关,尤其是与所选行相关的特定插入。这就是我建议使用循环的原因,这样我就可以分别为每一行执行选择/插入/更新。 @Raffael1984:编辑您的问题以添加“导致特定插入的特定行”的条件,我们可以提供帮助。你真的不想走光标/循环路线 - 它效率极低。 哦……你知道我会非常乐意采用基于集合的查询方式!但是效率不是这里的动力,因为我的问题更多的是学术兴趣。桌子很小,我可以用手做。我真的很感激一个循环版本。我可能会再次发布该问题,提供更多详细信息,以便有人帮助我使用基于集合的样式。 同意@RajMore,光标/循环效率低下,下面是2个关于光标性能的链接供参考:1.rpbouman.blogspot.tw/2006/09/refactoring-mysql-cursors.html2.***.com/questions/11549567/… @RajMore 你能帮我吗question【参考方案3】:

CURSORS 是这里的一个选项,但通常不受欢迎,因为它们通常不能充分利用查询引擎。考虑调查“基于 SET 的查询”,看看您是否可以在不使用 CURSOR 的情况下实现您想要做的事情。

【讨论】:

我找不到很多关于基于集合的查询的信息。但我想你的意思是选择一种陈述。问题是我还需要执行更新。【参考方案4】:

Purple 先生的例子我在 mysql 触发器中使用的那样,

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

【讨论】:

您能否添加一些您的解决方案如何工作的信息?【参考方案5】:
    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
       

【讨论】:

以上是关于如何循环遍历表的所有行? (MySQL)的主要内容,如果未能解决你的问题,请参考以下文章

删除 HTML 表格中的所有行

Python for循环遍历一列的所有行

PHP MySQL循环遍历行并在日期更改时打印日期

Mysql中的Join详解

SQL Server 每 5 行循环遍历一个表

SQL server中遍历所有行的循环表达式怎么写