PHP mysqli_multi_query 在while循环中的事务

Posted

技术标签:

【中文标题】PHP mysqli_multi_query 在while循环中的事务【英文标题】:PHP mysqli_multi_query transaction in while loop 【发布时间】:2017-01-03 15:39:32 【问题描述】:

php 版本 5.3.3,mysql 5.0.95

需要将数据从现有表迁移到两个相同的表。原始数据在插入两个新表之前需要解析。 (该代码未显示,因为我希望隔离此问题。) 想使用事务来确保新表是相同的。

task_id 字段在 test_timecard 中是自增字段,在 test_timecar_2 中是 unsigned mediumint。

Engine 是两个表的 InnoDB。

单独查询有效:

$timecard_data_results = array();
$fill_old_data_array_def = " SELECT task_id, company_id, employee_id, location, task_name, task_start_time, task_end_time, tccomment FROM timecard WHERE company_id = '" . $company_request . "' AND employee_id = '" . $employee_request . "' AND DATE(task_start_time) < '" . $new_text_format_date . "' AND (DATE(task_end_time) > '2014-12-31' OR DATE(task_end_time) = '2000-01-01') ORDER BY task_start_time";
$timecard_data_results = mysqli_query($conn, $fill_old_data_array_def);

while($timecard_record = mysqli_fetch_assoc($timecard_data_results)) 

    $company_id = $timecard_record['company_id'];
    $employee_id = $timecard_record['employee_id'];
    $location = $timecard_record['location'];
    $task_name = $timecard_record['task_name'];
    $task_start_time = $timecard_record['task_start_time'];
    $task_end_time = $timecard_record['task_end_time'];
    $tccomment = $timecard_record['tccomment'];

    $troubleshoot_def = "INSERT INTO test_timecard (company_id, employee_id, location, task_name, task_start_time, task_end_time, tccomment) VALUES ('" . $company_id . "', '" . $employee_id . "', '" . $location . "', '" . $task_name . "', '" . $task_start_time . "', '" . $task_end_time . "', '" . $tccomment . "')"; 
    $troubleshoot_2_def = "INSERT INTO test_timecard_2 (task_id, company_id, employee_id, location, task_name, task_start_time, task_end_time, tccomment) VALUES (LAST_INSERT_ID(), '" . $company_id . "', '" . $employee_id . "', '" . $location . "', '" . $task_name . "', '" . $task_start_time . "', '" . $task_end_time . "', '" . $tccomment . "')";     
    $troubleshoot = mysqli_query ($conn, $troubleshoot_def);
    $troubleshoot_2 = mysqli_query ($conn, $troubleshoot_2_def);

与 mysqli_multi_query 的事务仅在两个表中插入一行。未报告任何错误。

$timecard_data_results = array();
$fill_old_data_array_def = " SELECT task_id, company_id, employee_id, location, task_name, task_start_time, task_end_time, tccomment FROM timecard WHERE company_id = '" . $company_request . "' AND employee_id = '" . $employee_request . "' AND DATE(task_start_time) < '" . $new_text_format_date . "' AND (DATE(task_end_time) > '2014-12-31' OR DATE(task_end_time) = '2000-01-01') ORDER BY task_start_time";
$timecard_data_results = mysqli_query($conn, $fill_old_data_array_def);

while($timecard_record = mysqli_fetch_assoc($timecard_data_results)) 

    $company_id = $timecard_record['company_id'];
    $employee_id = $timecard_record['employee_id'];
    $location = $timecard_record['location'];
    $task_name = $timecard_record['task_name'];
    $task_start_time = $timecard_record['task_start_time'];
    $task_end_time = $timecard_record['task_end_time'];
    $tccomment = $timecard_record['tccomment'];

    $troubleshoot_def = "START TRANSACTION; INSERT INTO test_timecard (company_id, employee_id, location, task_name, task_start_time, task_end_time, tccomment) VALUES ('" . $company_id . "', '" . $employee_id . "', '" . $location . "', '" . $task_name . "', '" . $task_start_time . "', '" . $task_end_time . "', '" . $tccomment . "'); INSERT INTO test_timecard_2 (task_id, company_id, employee_id, location, task_name, task_start_time, task_end_time, tccomment) VALUES (LAST_INSERT_ID(), '" . $company_id . "', '" . $employee_id . "', '" . $location . "', '" . $task_name . "', '" . $task_start_time . "', '" . $task_end_time . "', '" . $tccomment . "'); COMMIT;"; 
    $troubleshoot = mysqli_multi_query ($conn, $troubleshoot_def);
    

难倒。

【问题讨论】:

如果你打算使用 mysqli_multi_query(),那么它应该只被调用一次(在循环之外),当所有的 INSERT 语句被连接时。这也意味着 START TRANSACTION 必须在循环之前移出并在循环之后移出 COMMIT,尽管它们并不是真正需要的。回显查询字符串以确定是否填充了所有所需的语句。也检查以下相关问题:***.com/q/20143592/2298301 【参考方案1】:
$troubleshoot_def = "INSERT INTO test_timecard (company_id, employee_id, location, task_name, task_start_time, task_end_time, tccomment) VALUES ('" . $company_id . "', '" . $employee_id . "', '" . $location . "', '" . $task_name . "', '" . $task_start_time . "', '" . $task_end_time . "', '" . $tccomment . "')"; 
    $troubleshoot_2_def = "INSERT INTO test_timecard_2 (task_id, company_id, employee_id, location, task_name, task_start_time, task_end_time, tccomment) VALUES (LAST_INSERT_ID(), '" . $company_id . "', '" . $employee_id . "', '" . $location . "', '" . $task_name . "', '" . $task_start_time . "', '" . $task_end_time . "', '" . $tccomment . "')";     

这里有很多问题。首先,将几乎相同的数据插入到两个不同的表中根本没有任何意义。事实上,当操作完成时,您将拥有三个具有几乎相同数据的表,即 test_timecard_2test_timecardtimecard

其次,您要插入未转义的数据。由于数据来自您的另一个表,因此 sql 注入的可能性不大,但查询仍有可能失败。具体来说,我在谈论这样的代码:

VALUES ('" . $company_id . "', '" . $employee_id . "', '" . $location . "', '" . $task_name . "', '" . $task_start_time . "', '" . $task_end_time . "', '" . $tccomment . "')"; 

第三,您几乎不需要执行 SELECT - LOOP - INSERT,因为 mysql 有一个内置的 INSERT SELECT 命令。

INSERT INTO test_timecard (company_id, employee_id, location, task_name, task_start_time, task_end_time, tccomment) 
SELECT * FROM time_card

注意列正确(上面只是从您的代码的两个部分复制粘贴)

【讨论】:

抱歉误导。子表实际上会有一些父表中不存在的附加字段,因此使用 while 循环来解析原始数据。当我遇到事务问题时,我试图在不干扰我的问题中其他代码的情况下隔离那个混乱。创建新表是一次性过程,完全由内部人员完成,因此无需担心注入问题。 一些额外的字段不足以成为创建这些额外表格的充分理由。这违反了所有数据库规范化原则。 撇开额外表的问题,正确的方法是使用单个 INSERT SELECT 语句。而不是根据表的大小可能会产生数千或数百万个插入 感谢您的关注。数据库设计。在插入新表之前,我需要在一系列 if 语句中调整原始数据。同时,有必要将原始数据的副本存档在永远不会用于查询等的第二个表中。新表在很多方面与原始表不同,但我认为最清楚的是从原始帖子中提供的代码,因此重点将放在 while 循环内的事务使用上。从 Dhruv Saxena 的评论看来,我需要为插入创建另一个 php 数组。 不幸的是,这被证明是一个红鲱鱼。

以上是关于PHP mysqli_multi_query 在while循环中的事务的主要内容,如果未能解决你的问题,请参考以下文章

sql无法通过mysqli_multi_query执行

如果不使用 mysqli_multi_query,SQL 注入将无法工作

sqli-labs(41) and 两php函数的讲解

我收到一个错误“命令不同步;当我使用mysqli_multi_query时,你现在无法运行此命令

SQL——注入攻击——mysqli_multi_query()的使用

mysqli_multi_query数组问题