【问题标题】:Executing multiple SQL queries in one statement with PHP使用 PHP 在一条语句中执行多个 SQL 查询
【发布时间】:2012-12-08 11:35:03
【问题描述】:

如何将多个查询合并为一个(可以吗?)

$query = "DELETE FROM aktywne_kody WHERE kodsms ='$kodSMSgracza' AND typkodu ='$id'";
mysql_query($query) or die(mysql_error());

$query = "INSERT INTO uzyte_kody (gracz, kodsms, typkodu) VALUES ('$nickGracza', '$kodSMSgracza', '$id')";
mysql_query($query) or die("Błąd MySQL X04");

$query = "INSERT INTO do_odebrania (gracz, itemDATA, itemQTY) VALUES ('$nickGracza', '$itemDATA', '$itemQTY')";
mysql_query($query) or die("Błąd MySQL X05");

顺便说一句,如果我在所有查询完成后执行 mysql_close($db) 会更好吗?

【问题讨论】:

  • 制作一个$query,并确保在每一行之后添加一个;
  • 为什么需要这个?大多数时候,可读性比效率低更重要。
  • 请不要在新应用程序中使用mysql_query。它已被弃用且危险。至少使用 PDO 来确保您的 data is escaped properly 在您因 SQL 注入错误而陷入严重麻烦之前。
  • ; 加入他们会得到You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO uzyte_kody (gracz, kodsms, typkodu) VALUES ('UltraMC', '00000000', '' at line 1
  • 继@tadman 关于避免mysql_query 之后,mysqli 为您提供multi_query,它正好允许这样做。

标签: php mysql


【解决方案1】:

65536 作为第五个参数传递给mysql_connect

例子:

$conn = mysql_connect('localhost','username','password', true, 65536 /* here! */) 
    or die("cannot connect");
mysql_select_db('database_name') or die("cannot use database");
mysql_query("
    INSERT INTO table1 (field1,field2) VALUES(1,2);

    INSERT INTO table2 (field3,field4,field5) VALUES(3,4,5);

    DELETE FROM table3 WHERE field6 = 6;

    UPDATE table4 SET field7 = 7 WHERE field8 = 8;

    INSERT INTO table5
       SELECT t6.field11, t6.field12, t7.field13
       FROM table6 t6
       INNER JOIN table7 t7 ON t7.field9 = t6.field10;

    -- etc
");

当您使用 mysql_fetch_* 或 mysql_num_rows 或 mysql_affected_rows 时,只有第一个语句有效。

比如下面的代码,第一条语句是INSERT,不能执行mysql_num_rows和mysql_fetch_*。 可以用mysql_affected_rows返回插入的行数。

$conn = mysql_connect('localhost','username','password', true, 65536) or die("cannot connect");
mysql_select_db('database_name') or die("cannot use database");
mysql_query("
    INSERT INTO table1 (field1,field2) VALUES(1,2);
    SELECT * FROM table2;
");

再比如,下面的代码,第一条语句是SELECT,不能执行mysql_affected_rows。但是你可以执行 mysql_fetch_assoc 得到第一个 SELECT 语句产生的行的键值对,或者你可以执行 mysql_num_rows 得到第一个 SELECT 语句的行数。

$conn = mysql_connect('localhost','username','password', true, 65536) or die("cannot connect");
mysql_select_db('database_name') or die("cannot use database");
mysql_query("
    SELECT * FROM table2;
    INSERT INTO table1 (field1,field2) VALUES(1,2);
");

【讨论】:

  • 如果有人想知道为什么会这样 - 这不是一个技巧,它只是 mySQL 客户端的未记录(在 PHP 文档中)功能。在源代码中,您可以轻松找到#define CLIENT_MULTI_STATEMENTS 65536 /* Enable/disable multi-stmt support */,这正是 OP 想要的。
  • 为了代码的可读性;将 CLIENT_MULTI_STATEMENTS 作为 mysql_connect 的第 5 个参数传递。
  • @Husni,你为什么使用像 65536 这样的“幻数”而不是正确的常量名?
  • 为什么不使用“准备”语句来避免 SQL 注入攻击? (它只是 PHP 中的一点额外代码)
【解决方案2】:

这可能是创建 sql 注入点“SQL Injection Piggy-backed Queries”。攻击者能够附加多个恶意 sql 语句。所以不要将用户输入直接附加到查询中。

安全考虑

API 函数 mysqli_query() 和 mysqli_real_query() 没有设置激活服务器中的多个查询所需的连接标志。对多个语句使用额外的 API 调用,以减少意外 SQL 注入攻击的可能性。攻击者可能会尝试添加语句,例如;删除数据库 mysql 或 ;选择睡眠 (999)。如果攻击者在语句字符串中添加SQL成功,但没有使用mysqli_multi_query,则服务器不会执行第二条注入的恶意SQL语句。

PHP Doc

【讨论】:

    【解决方案3】:

    使用mysqli,您可以使用mysqli_multi_query() 真正使用多个语句。

    Read more on multiple statements in the PHP Docs.

    【讨论】:

      【解决方案4】:

      您可以只添加单词 JOIN 或添加 ;在每一行之后(如@pictchubbate 所说)。由于可读性,这种方式更好,而且你不应该用 INSERT 干预 DELETE;往南走很容易。

      最后一个问题是一个有争议的问题,但据我所知,是的,您应该在一组查询后关闭。这主要适用于旧的普通 mysql/php 而不是 PDO、mysqli。在这些情况下,事情变得更加复杂(并且在辩论中变得更加激烈)。

      最后,我建议使用 PDO 或其他方法。

      【讨论】:

        最近更新 更多