从PHP内加载.sql文件

时间:2020-03-06 14:51:50  来源:igfitidea点击:

我正在为正在开发的应用程序创建安装脚本,并且需要从PHP内部动态创建数据库。我已经有了它来创建数据库,但是现在我需要加载几个.sql文件。我计划一次打开文件并mysql_query一行,直到我查看了模式文件并意识到它们不仅仅是每行一个查询。

那么,如何从PHP内加载sql文件(就像phpMyAdmin的import命令一样)?

解决方案

为什么不从phpMyAdmin中获取代码并使用呢?毕竟是开源的...

mysql_query("LOAD DATA LOCAL INFILE '/path/to/file' INTO TABLE mytable");

我们确定不是每行一个查询吗?文本编辑器可能会换行,但实际上每个查询可能都在一行中。

无论如何,olle的方法似乎是最好的。如果我们有理由一次运行一个查询,则应该能够逐行读取文件,然后在每个查询的末尾使用分号定界。与尝试拆分一个巨大的字符串相比,逐行读取一个文件要好得多,因为这将对服务器的内存更加友好。例子:

$query  = '';
$handle = @fopen("/sqlfile.sql", "r");

if ($handle) {
    while (!feof($handle)) {
        $query.= fgets($handle, 4096);

        if (substr(rtrim($query), -1) == ';') {
            // ...run your query, then unset the string
            $query = '';
        }
    }

    fclose($handle);
}

显然,如果要批量运行大量查询,则需要考虑事务和其余事务,但是对于新安装的脚本来说,这可能并不重要。

除非我们打算导入巨大的.sql文件,否则只需将整个文件读取到内存中,然后将其作为查询运行即可。

自从我使用PHP已经有一段时间了,所以,伪代码:

all_query = read_file("/my/file.sql")
con = mysql_connect("localhost")
con.mysql_select_db("mydb")
con.mysql_query(all_query)
con.close()

除非文件很大(例如,超过几兆字节),否则没有理由一次执行它,或者尝试将其拆分为多个查询(通过使用;进行拆分,正如我对cam8001的回答所说的那样) ,如果查询的字符串中包含分号,则会中断)。

我的建议是查看PHPMyBackup的源代码。这是一个自动化的PHP SQL加载程序。我们会发现mysql_query一次仅加载一个查询,并且PHPMyAdmin和PHPMyBackup之类的项目已经为我们正确解析SQL进行了艰苦的工作。请不要重新发明轮子:P

最简单的解决方案是使用shell_exec()以SQL脚本作为输入来运行mysql客户端。这可能会慢一些,因为它必须分叉,但是我们可以在几分钟内编写代码,然后重新开始做一些有用的事情。编写PHP脚本以运行任何SQL脚本可能会花费我们数周的时间。

支持SQL脚本比人们在此处描述的要复杂,除非我们确定脚本仅包含脚本功能的一部分。以下是普通SQL脚本中可能出现的一些示例,这些示例使编写脚本以逐行解释脚本的过程变得很复杂。

-- Comment lines cannot be prepared as statements
-- This is a MySQL client tool builtin command.  
-- It cannot be prepared or executed by server.
USE testdb;

-- This is a multi-line statement.
CREATE TABLE foo (
  string VARCHAR(100)
);

-- This statement is not supported as a prepared statement.
LOAD DATA INFILE 'datafile.txt' INTO TABLE foo;

-- This statement is not terminated with a semicolon.
DELIMITER //

-- This multi-line statement contains a semicolon 
-- but not as the statement terminator.
CREATE PROCEDURE simpleproc (OUT param1 INT)
BEGIN
  SELECT COUNT(*) INTO param1 FROM foo;
END
//

如果仅支持一部分SQL脚本(不包括上述某些极端情况),则编写一个PHP脚本来读取文件并在文件中执行SQL语句相对容易。但是,如果我们想支持任何有效的SQL脚本,则要复杂得多。

另请参阅我对以下相关问题的回答:

  • 在PHP中运行MySQL * .sql文件
  • 是否可以从另一个sql脚本中的存储过程中调用sql脚本?
  • PHP:在一个mysql_query语句中进行多个SQL查询

mysqli可以运行多个以`;'分隔的查询

我们可以读取整个文件并使用mysqli_multi_query()一次运行所有文件

但是,我将第一个说这不是最优雅的解决方案。

我感觉到,回答这个问题的每个人都不知道成为一个Web应用程序开发人员是什么样的,它允许人们在自己的服务器上安装该应用程序。尤其是,共享主机不允许我们像前面提到的" LOAD DATA"查询那样使用SQL。大多数共享主机也不允许我们使用shell_exec。

现在,要回答OP,我们最好的选择就是构建一个PHP文件,该文件将查询包含在一个变量中并可以运行它们。如果确定要解析.sql文件,则应该研究phpMyAdmin并获得一些想法,以这种方式从.sql文件中获取数据。看看其他具有安装程序的Web应用程序,我们会看到,它们没有使用.sql文件进行查询,而是将它们打包到PHP文件中,并通过mysql_query或者它们需要执行的任何操作来运行每个字符串。

我在这里看到的所有解决方案都没有涉及在服务器上创建存储过程时需要更改定界符的问题,在该服务器上,我不能指望可以访问LOAD DATA INFILE。我希望发现有人已经解决了这个问题,而不必搜寻phpMyAdmin代码来解决。与其他人一样,由于我自己编写GPL代码,因此我也在寻找别人采用GPL编写的方法。

一些PHP库可以解析由多个SQL语句组成的SQL文件,将其正确分解(自然不使用简单的";"分解),然后执行它们。

例如,检查Phing的PDOSQLExecTask