如何从 PHP 快速制作多个略有不同的插入
Posted
技术标签:
【中文标题】如何从 PHP 快速制作多个略有不同的插入【英文标题】:How to make multiple slightly different inserts fast from PHP 【发布时间】:2012-07-21 04:43:07 【问题描述】:我正在执行大约 600 个 INSERT ON DUPLICATE KEY UPDATE
查询,其中每个插入可以有一组不同的列。
我现在的做法只是一个带有mysql_query()
的foreach 循环。真的很慢,php脚本由于最长执行时间30s而停止。
我不能使用INSERT INTO table (columns) VALUES (values 1), (values 2), ..., (values n)
,因为每个插入必须能够有一组不同的列。
我还查看了准备好的查询,但据我所见,这似乎也不适用于不同的列集。
我是数据库和 MYSQL 的新手。我只是在拼凑一些东西作为周末项目,现在我只想让项目的最后一部分工作。我很抱歉没有以正确的方式做到这一点。 (很抱歉也使用了已弃用的 php mysql 函数。)也许我应该去睡觉,稍后再重写。正确的做法是什么?
编辑:这里是关于表格类型的一些信息
-- phpMyAdmin SQL 转储 -- 版本 3.5.1 -- http://www.phpmyadmin.net -- -- 主机:本地主机 -- 生成时间:2012 年 7 月 22 日晚上 9:59 -- 服务器版本:5.5.24-log -- PHP版本:5.3.13 SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; SET time_zone = "+00:00"; -- -- 数据库:`trailerbay` -- -------------------------------------------------- -------- -- -- 表`movies`的表结构 -- 如果不存在“电影”,则创建表( `id` int(11) NOT NULL AUTO_INCREMENT, `imdb` int(7) NOT NULL, `title` 文本整理 utf8_unicode_ci, `releasedate` 日期 DEFAULT NULL, `enlistdate` 日期时间不为空, `runtime` 时间 DEFAULT NULL, `trailer` 文本 COLLATE utf8_unicode_ci NOT NULL, `plot` 文本整理 utf8_unicode_ci, `cover` 文本 COLLATE utf8_unicode_ci NOT NULL, 主键(`id`), 唯一键`id`(`id`), 唯一键 `imdb` (`imdb`) ) 引擎=InnoDB 默认字符集=utf8 排序=utf8_unicode_ci AUTO_INCREMENT=1145 ;【问题讨论】:
PSA:不要将mysql
模块用于 PHP 的新代码。事实上,如果你能帮助它,不要将它用于任何代码;它缺乏对参数插值的支持,这使得创建 SQL 注入漏洞变得容易。考虑改用至少mysqli
。
@millimoose PDO 在我看来更好:)
目前执行多长时间?
@Sam 因此是“至少”。这是阻力最小的路径,不一定是最佳选择。
@zerkms 执行速度太慢,耗时超过 30 秒。 mysqli 或 PDO 是否支持一个查询中的多个 SQL 语句?
【参考方案1】:
只需一次将其拆分为 50 个插入,将其划分为 12 个文件并按顺序执行它们。完成后重定向到下一个的简单 javascript。
如果它只是一次性交易,那可能是最简单的解决方案。
否则发布一些示例代码,以便我们提供一些更有帮助的答案。
【讨论】:
实际上 600 个查询太少了,所以我不推荐您的解决方案。修复根源总是更好,而不是后果 我知道,但查询时间必须超过 30 秒。他没有发布任何示例代码,所以这是我可以在没有代码的情况下给出的最佳建议。【参考方案2】:您的插入是否在 mysql 事务中执行?如果没有,这可能有助于提高您的性能。
http://kevin.vanzonneveld.net/techblog/article/improve_mysql_insert_performance/
【讨论】:
LOAD
在这种情况下不能使用【参考方案3】:
我不认为你可以在表格中有这么多列,以至于它们的开/关组合多得令人望而却步。
因此,对于每一行,您可以提取其列组合(例如,您可以按字母顺序对字段进行排序)并将其结构用作键:
// $tuple has been sorted based on keys
$syndrome = implode(',', array_keys($tuple));
$values = array_values($tuple);
if (isset($big_inserts[$syndrome]))
array_push($big_inserts[$syndrome], $values);
else
$big_inserts[$syndrome] = array($values);
在循环结束时,您会发现自己有一个 $big_inserts
数组,其中包含一定数量的键。每个键都会映射一组值,适合多次插入。
除非您真的非常不走运,否则您的“多个插入”将比您开始时的单个插入少得多。如果所有插入都具有相同的列,那么您将在 big_inserts 中只有一个键,保存所有元组。
现在,在 big_inserts 上循环,您可以为每个键准备一个语句。要发送到 PDO 的值数组是 $big_inserts[$key]
中所有元组的串联。
foreach($big_inserts as $fields => $lot)
$SQL = "INSERT INTO table ($fields) VALUES ";
// I need a (?.?.?---) tuple
$tuple = '('.implode(',', array_fill(0, count($lot[0]), '?')).')';
// How many tuples are in a lot?
$SQL .= implode(',', array_fill(0, count($lot), $tuple));
$values = array();
foreach($lot as $set)
$values = array_merge($values, $set);
// Now $SQL has all the '?'s, and $values contains all the values.
// Run the statement.
如果这还不够,您可能必须将语句分成块,将它们保存在会话中并按顺序执行每个块,也许使用单独的表来模拟“多往返”事务(以防连接丢失/用户关闭浏览器/任何一半的块已经执行,而另一半仍然存在。使用直接插入到具有相同结构的表中,然后当表准备好时,在事务中运行一个 INSERT INTO ... SELECT FROM 和提交后删除辅助表。
如果它们是简单的 INSERT,我会尝试暂时禁用一些索引,但您依赖它们进行 ON DUPLICATE KEY UPDATE,所以这是不行的。
【讨论】:
我有 9 列这样2^9=512
组合。嗯也许会起作用,但我现在使用事务解决了这个问题。但是非常感谢您的回答,也许我稍后会重写所有内容,然后我会考虑使用您的方法。以上是关于如何从 PHP 快速制作多个略有不同的插入的主要内容,如果未能解决你的问题,请参考以下文章
动态插入 iframe 的网页的 Html 看起来与原始页面略有不同