【问题标题】:PHP how to run sql query one part at a time?PHP如何一次运行sql查询?
【发布时间】:2009-12-14 21:10:14
【问题描述】:

我有一个大约有 100 万行的表。我正在做一个简单的程序,从每一行打印出一个字段。但是,当我开始使用 mysql_pconnect 和 mysql_query 时,查询需要很长时间,我假设查询需要完成,然后才能打印出第一行。有没有办法一次处理一点数据?

--已编辑-- 我不是要检索一小部分数据,我正在寻找一种方法来一次处理数据块(比如获取 10 行、打印 10 行、获取 10 行、打印 10 行等),而不是而不是等待查询检索 100 万行(谁知道多长时间)然后开始打印。

【问题讨论】:

  • 尝试使用flush() (br2.php.net/flush)
  • 即使您确实在屏幕 10 上一次显示 100 万行,要传输到浏览器和显示的数据量也是非常可笑的。如果您不使用某种分页,可能会使大多数用户的浏览器崩溃。
  • 好吧,据我所知,他可能正在打印到 /dev/null。
  • 知道打印输出的位置很重要。您不能将 10 行打印到浏览器,获取接下来的 10 行并再次将它们打印到浏览器(您可能可以使用 javascript 来执行此操作,但仅使用 PHP 是不可能的)。
  • 既然您不同意彼得斯的回答,也许您可​​以提供更多信息。很明显,您不是在寻找分页 ;-) 但正如彼得所说,它不应该等待整个查询......那么,输出在哪里?如果它通过http,那么网络服务器可以做一些缓冲,例如..(有人提到flush()可能会有所帮助)尝试使用php-cgi并检查是否有区别..

标签: php sql mysql performance


【解决方案1】:

打印一百万个字段需要一些时间。检索一百万条记录需要一些时间。时间加起来。

您是否分析过您的代码?我不确定在这种情况下使用限制会产生如此巨大的差异。

做这样的事情

while ($row = mysql_fetch_object($res)) {
   echo $row->field."\n";
}

一次输出一条记录。它不会等待返回整个结果集。

如果您使用的是浏览器,您将需要更多的东西。

这样的

ob_start();
$i = 0;
while ($row = mysql_fetch_object($res)) {
   echo $row->field."\n";
   if (($i++ % 1000) == 0) {
       ob_flush();
   }
}
ob_end_flush();

【讨论】:

    【解决方案2】:

    您真的要打印一百万个字段吗?

    通常的解决方案是在您的 Web 应用程序中使用某种输出分页,只显示部分结果。在SELECT 查询中,您可以使用LIMIT 关键字仅返回部分数据。这是基本的 SQL 东西,真的。示例:

    SELECT * FROM table WHERE (some conditions) LIMIT 40,20
    

    显示 20 个条目,从第 40 个开始(我可能会出现一个错误)。

    可能需要将ORDER BYLIMIT 一起使用,以防止在请求之间顺序随机更改。

    【讨论】:

      【解决方案3】:

      这通常是分页所需要的。您可以在选择查询中使用限制关键字。搜索极限here

      LIMIT 子句可用于限制 SELECT 语句返回的行数。 LIMIT 接受一个或两个数字参数,它们都必须是非负整数常量(使用准备好的语句时除外)。

      有两个参数,第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数。初始行的偏移量为0(不是1):

      SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15
      

      要检索从某个偏移量到结果集末尾的所有行,您可以为第二个参数使用一些较大的数字。此语句检索从第 96 行到最后一行的所有行:

      SELECT * FROM tbl LIMIT 95,18446744073709551615;
      

      使用一个参数,该值指定从结果集开头返回的行数:

      SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows
      

      换句话说,LIMIT row_count 等价于 LIMIT 0, row_count。

      【讨论】:

        【解决方案4】:

        您也许可以使用 Mysqli::use_result

        结合flush将数据集输出到浏览器。我知道刷新可用于以增量状态将数据输出到浏览器,因为我以前使用它来做到这一点,但是我不确定 mysqli::use_result 是否是检索不完整结果集的正确函数。

        【讨论】:

          【解决方案5】:

          这就是我在 Oracle 中做类似事情的方式。我不确定它会如何跨越:

          declare
          my_counter integer := 0;
          begin
          for cur in (
          select id from table
          ) loop
            begin
              -- do whatever your trying to do
              update table set name = 'steve' where id = cur.id;
              my_counter := my_counter + 1;
              if my_counter > 500 then
                my_counter := 0;
                commit;
              end if;
              end;
            end loop;
            commit;
          end;
          

          【讨论】:

            【解决方案6】:

            使用基本 mysql 驱动程序的示例。

            define( 'CHUNK_SIZE', 500 );
            
            $result = mysql_query( 'select count(*) as num from `table`' );
            $row = mysql_fetch_assoc( $result );
            
            $totalRecords = (int)$row['num'];
            
            $offsets = ceil( $totalRecords / CHUNK_SIZE );
            
            for ( $i = 0; $i < $offsets; $i++ )
            {
              $result = mysql_query( "select * from `table` limit " . CHUNK_SIZE . " offset " . ( $i * CHUNK_SIZE ) );
              while ( $row = mysql_fetch_assoc( $result ) )
              {
                // your per-row operations here
              }
              unset( $result, $row );
            }
            

            这将遍历整个行卷,但一次只执行 500 行以降低内存使用量。

            【讨论】:

              【解决方案7】:

              听起来你正在达到 mysql 服务器中各种缓冲区大小的限制......你可以做的一些方法是在 SQL 语句中指定你想要的字段以减少这个缓冲区大小,或者玩弄各种管理设置。

              或者,您可以使用分页 like 方法,但将其全部输出在一页上...

              (伪代码)

               function q($part) {
                    $off = $part*SIZE_OF_PARTITIONS;
                    $size = SIZE_OF_PARTITIONS;
              
                    return( execute_and_return_sql('SELECT `field` FROM `table` LIMIT $off, $size'));
                  }
              
                  $ii = 0;
              
                  while ($elements = q($ii)) {
                    print_fields($elements);
                    $ii++;
                  }
              

              【讨论】:

                【解决方案8】:

                使用 mysql_unbuffered_query() 或者如果使用 PDO,请确保 PDO::MYSQL_ATTR_USE_BUFFERED_QUERYfalse

                另见this similar question

                编辑:正如其他人所说,您可能希望将此与在每批处理后刷新输出缓冲区结合起来,具体取决于您的情况。

                【讨论】:

                  猜你喜欢
                  • 2015-10-17
                  • 2018-01-28
                  • 1970-01-01
                  • 2018-12-19
                  • 1970-01-01
                  • 2021-07-19
                  • 2021-12-25
                  • 1970-01-01
                  相关资源
                  最近更新 更多