如何使用 PDO 在 PHP 中创建包含 DELIMITER 的存储过程?

Posted

技术标签:

【中文标题】如何使用 PDO 在 PHP 中创建包含 DELIMITER 的存储过程?【英文标题】:How to create a stored procedure containing a DELIMITER in PHP with PDO? 【发布时间】:2017-09-28 01:25:27 【问题描述】:

在我的模型中,我定义了一些程序。代码(由 mysql Workbench 生成)包含 DELIMITER 定义,因此过程如下所示:

-- schema
CREATE DATABASE ...
CREATE TABLE foo ...
-- procedures
DELIMITER $$
...
  BEGIN
    DECLARE ... ;
    OPEN ... ;
    SET ... ;
    ... ;
  END$$
DELIMITER ;

现在我需要通过PDO 将SQL“导入”到数据库中。我尝试将它作为PDO#exec(...) 的输入传递,但注意到,执行在第一个DELIMITER 定义的行停止。

我不想删除 DELIMITER 语句。所以 SQL 代码应该保持不变。

如何使用PDO执行包含DELIMITER语句的SQL代码?

【问题讨论】:

【参考方案1】:

当我尝试使用 PostgreSQL 时,我遇到了同样的问题。问题似乎 PDO 只允许您执行 1 个查询 1 次。如前所述:PDO::exec() executes an SQL statement in a single function call, returning the number of rows affected by the statement。在php manual

你可以试试这个:

$stmt = $db->prepare($sql);
$stmt->execute();

或者mysqli:multi_query。 php manual

这里是我的全班同学:http://sandbox.onlinephpfunctions.com/code/f0528fda6d7bd097c3199f1f3c019805a163ae3a

【讨论】:

他询问的是 PDO,而不是 mysqli 实际上,如果它与 MySQLi 一起使用,我也会很高兴。无论如何,我看不出有理由否决这个问题! 感谢您的回答!刚刚用MySQLi#multi_query(...)试了一下,结果还是一样——DELIMITER后面的代码被忽略了。如果您仍然有 PostgreSQL 的解决方案,请您发布它的代码。谢谢! 感谢您的更新!刚刚试用了$statement = $pdo->prepare($sql); $statement->execute();。但不幸的是,它不起作用。 是的,它只在我尝试用我手动检测的--DELIMITER 符号爆炸时才有效。你的情况可能是-- procedures【参考方案2】:

分隔符是 PDO 不需要的东西。您可以按原样运行查询

$pdo->query("CREATE DATABASE ...");
$pdo->query("CREATE TABLE foo ...");
$pdo->query("BEGIN
    DECLARE ... ;
    OPEN ... ;
    SET ... ;
    ... ;
  END");

就这么简单

【讨论】:

感谢您的回答!是的,它会工作。甚至$pdo->exec($myBugSqlStatementWithoutSelimiterStatements) 也可以。但正如我在回答中所说,我不想删除 DELIMITER 语句。实际上,我想让它工作而不需要手动执行每条语句。【参考方案3】:

来自 cmets:

我不想删除 DELIMITER 语句。实际上我想让它在不手动执行每个语句的情况下工作

这不是它的工作原理。

要了解原因,您需要了解mysql CLI(以及任何其他可以读取和执行此类转储文件的程序)实际上是如何处理它的。

DELIMITER 不是服务器能理解的。

DELIMITER 用于告诉客户端解析器当前语句分隔符应该是什么,以便客户端解析器可以正确拆分语句并一次传递一个语句到执行服务器。

来自文档。请注意mysql,每次在这里使用时,指的是mysql 客户端实用程序——而不是服务器。

如果您使用mysql 客户端程序来定义包含分号字符的存储程序,则会出现问题。默认情况下,mysql 本身将分号识别为语句分隔符,因此您必须临时重新定义分隔符以使 mysql 将整个存储的程序定义传递给服务器。

要重新定义mysql 分隔符,请使用delimiter 命令。 [...]分隔符更改为//,以使整个定义能够作为单个语句传递给服务器,然后在调用过程之前恢复为;。这使得过程主体中使用的; 分隔符能够传递到服务器,而不是由mysql 本身解释。

https://dev.mysql.com/doc/refman/5.7/en/stored-programs-defining.html

因此,要处理这样的文件,您需要一个客户端解析器,它可以执行与 mysql 相同的操作...在这里,您正在编写的代码 (需要) 客户端语句解析器。所以你是需要编写逻辑来处理分隔符的人。

要做你想做的事,你必须解释 DELIMITER 语句,用它们来跟踪当前语句分隔符,但不要将它们发送到服务器。

然后,您必须一次读取一行输入,缓冲您已读取的内容,直到在行尾找到指定的分隔符,并将生成的语句发送到服务器 - 从您发送的内容中排除实际的语句分隔符...例如,您不会在过程主体之后发送结尾$$(除非当前语句分隔符是;,您可以发送或不发送 - 服务器不在乎。)然后清空缓冲区并再次开始读取,直到您看到另一个分隔符实例(并将语句发送到服务器)或匹配 DELIMITER 语句并设置您的代码当前分隔符变量以匹配它,以便您正确识别下一条语句的结尾。

【讨论】:

很好的解释。这也回答了我之前如何实现这一点的问题。谢谢

以上是关于如何使用 PDO 在 PHP 中创建包含 DELIMITER 的存储过程?的主要内容,如果未能解决你的问题,请参考以下文章

在 PHP 中创建与 PDO 的连接时出错

PHP PDO 在事务中创建多个表

在 XAMPP 中创建数据库(MySQL PDO)并将其与 php 代码连接

PDO-数据库访问抽象层

PHP pdo 实例作为私有静态属性

如何在 PHP 中创建下拉查找字段?