【问题标题】:Error 1064 with multiple calls to stored procedure in single statement在单个语句中多次调用存储过程时出现错误 1064
【发布时间】:2017-01-04 10:02:52
【问题描述】:

我正在努力提高 Web 应用程序的数据导入性能,其中一件显着提高了性能的事情是将数据库相关逻辑从服务器端代码移植到数据库中的存储过程和函数中。

此外,在处理大量类似查询时,将它们分批排队以便稍后在单个 SQL 事务中执行,可以显着减少导入过程的瓶颈。


不幸的是,我在此过程中遇到了一个问题,由于某种原因,我不能在单个 SQL 命令中多次调用存储过程。

在我连续两次调用LegacyUpdateProduct 的地方使用这个 SQL 来展示我的问题。

SQL

存储过程接受一些带数据的参数,不返回结果集,并且输出带有行 ID 的 @ID 变量,在这种情况下我们无论如何都不需要。

CALL LegacyUpdateProduct(92,'00010','ADAISD 345',18.81,'91020200','FSB92','GS8219','FSB92',TRUE,2,3,0,0,@ID);CALL LegacyUpdateProduct(277,'0002335600','TPASD 987',6.04,'0 002 335 ','UX79','','',TRUE,2,3,0,0,@ID);

PHP

在 PHP 中,任何人都希望通过两次调用 LegacyUpdateProduct 来简单地运行查询,一切都会好起来的。

$sql = "CALL LegacyUpdateProduct(92,'00010','ADAISD 345',18.81,'91020200','FSB92','GS8219','FSB92',TRUE,2,3,0,0,@ID);CALL LegacyUpdateProduct(277,'0002335600','TPASD 987',6.04,'0 002 335 ','UX79','','',TRUE,2,3,0,0,@ID);"
# With Code Igniter
$this->db->query($sql);
# Or with raw PHP
mysqli_query($this->db->conn_id, $sql);

遗憾的是,事实并非如此。使用 Code Igniter 或普通 PHP 运行此查询会引发 SQL 错误。

注意:我用细齿梳理了数据,并利用 Code Igniter DB 转义机制来产生更安全的查询。

错误号:1064 您的 SQL 语法有错误

最有趣的是,当我在 PHPMyAdmin 或 MySQL Workbench 中复制和粘贴据称错误的 SQL 时,它运行平稳,没有错误或警告。

实际上,在 PHPMyAdmin 和 MySQL Workbench 中,我能够在单个查询中运行多达 50 次对 LegacyUpdateProduct 的连续调用,而不会出现问题。

我已经通过 Google 和 SO 寻找可能在某种程度上相关或有帮助的问题,但没有发现太多可继续的问题,除了这可能是某种 Code Igniter 错误,但即使是普通的 PHP我发现了同样的问题。

在对另一个有点相关的 SO question 的回答中,它提到这可能是一个 Code Igniter 错误,但是它没有解释为什么使用普通的 PHP mysqli_query() 这个查询会失败。

在另一个 question 的 cmets 中,对于不同的错误,提示可能存在并发问题,如果不等待前一个完成,就无法对过程进行顺序调用。我对 MySQL 并发性的理解不够,无法提出索赔,但是,查询中的每个命令不应该在前一个命令之后执行。

帮助?

【问题讨论】:

  • 如果你想一次运行2条语句,你必须使用multi_query。或者只是将您的查询拆分为两个查询。 query 不知道如何使用第一个 ; 之后的代码,这就是您收到语法错误的原因。
  • 很有趣,用于在同一个 sql 语句中运行多个查询,从未考虑过 mysqli 可能已经实现了该操作方法以防止恶意意图。我已经探索了这条途径,并且我能够在第一次调用 multi_query 时在同一语句中运行所有存储过程,但是以下对 multi_query 的调用以某种方式下降。但更重要的是我没有注意到性能提升,我会阅读文档并更好地了解这种方法,更重要的是找出在 ; 之后执行查询的 MySQL 连接标志。
  • 为了节省您的时间,在; 之后启用执行的唯一方法是使用_multi。而且您不会从中获得太多性能提升(只要您保持连接打开)。总是喜欢单个查询!原因很多!例如。安全,参数绑定,更容易(_multi 要求您在继续之前阅读所有结果,这就是您的以下查询失败的原因),...对于一些技术见解(例如连接设置),请查看 @987654324 @
  • 我玩弄了一些客户端标志,但没有成功。源数据来自可靠的来源(无需用户输入),无论如何,我仍然使用 Code Igniter 进行数据转义和参数绑定来构建每个查询,这为整个过程带来了一定程度的安全性。那么,抛开最佳实践不谈,对于mysqli,您是否知道一种使用查询而不是multi_query 来运行多个查询的方法?
  • 没有这种方法。也没有一个理由这样做。只需通过单独的 query() 调用运行您的查询,就像您已经被告知的那样

标签: php mysql stored-procedures mysqli codeigniter-3


【解决方案1】:

在 PHP 中,任何人都希望通过两个调用简单地运行查询

没有像“两个调用的查询”这样的东西。您实际上拥有的是两个 SQL 查询。而 mysqli::query 每次调用只能运行一个 SQL 查询。

在处理大量类似查询时,将它们分批排队以便稍后在单个 SQL 命令中执行,显着减少了导入过程的瓶颈。

很可能是famous innodb's paranoid mode。要么关闭它,要么将你的更新包装在一个事务中。您将看到单独的 query() 调用没有明显减少。

【讨论】:

  • 抱歉,我在问题中错误地使用了 SQL 命令而不是 SQL 事务,您的答案中的第二个引号。每批查询都在一个事务中处理,我将启动事务,调用每个查询并提交事务。
  • 将我的调用包装在存储过程中是一个很好的建议。最初运行对存储过程的所有 58k 次调用需要 211 分钟,平均每次调用 0.22 秒。当我尝试以每个事务 1k 的批次运行它们时,总运行时间略高于 56 分钟,平均为 0.06 秒。
  • 您的提示再次非常有用。我对您的建议的第一种方法不够好,对改进不满意,回到绘图板并重新设计了导入部分。长话短说,现在缩短到大约 270 秒。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-07
  • 1970-01-01
  • 1970-01-01
  • 2019-04-01
  • 1970-01-01
  • 2014-08-01
  • 2016-11-10
相关资源
最近更新 更多