从 PHP 中加载 .sql 文件

Posted

技术标签:

【中文标题】从 PHP 中加载 .sql 文件【英文标题】:Loading .sql files from within PHP 【发布时间】:2010-09-13 22:51:35 【问题描述】:

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

那么,我如何从 PHP 中加载一个 sql 文件(就像 phpMyAdmin 使用它的 import 命令所做的那样)?

【问题讨论】:

尤其是 phpMyAdmin 的源代码本身 :) @Anonymous :我一直在寻找“最好的”方式。这个地方应该是一个一站式的答案商店(未来也是如此),因此我觉得在其他地方回答问题是很好的。但是,我确实先在别处寻找,但找不到很好的答案。 RE: phpMyAdmin - 它的来源有点帮助,但非常依赖于它的其他功能,不适合我非常严格的时间框架和复杂性要求。 致所有支持答案的人,除了公认的答案:问题是如何从 PHP 中加载脚本。 LOAD DATA 解决方案解决了 MySQL 方面的问题。此外,MySQL 可能在另一台机器上运行,并且无法访问执行 PHP 脚本的文件系统,这应该予以考虑。 Execute mysql .sql dump files via php mysqli 【参考方案1】:

phpBB 使用一些函数来解析他们的文件。他们的评论相当好(多么例外!)所以你可以很容易地知道他们做了什么(我从http://www.frihost.com/forums/vt-8194.html得到了这个解决方案)。这是我经常使用的解决方案:

<?php
ini_set('memory_limit', '5120M');
set_time_limit ( 0 );
/***************************************************************************
*                             sql_parse.php
*                              -------------------
*     begin                : Thu May 31, 2001
*     copyright            : (C) 2001 The phpBB Group
*     email                : support@phpbb.com
*
*     $Id: sql_parse.php,v 1.8 2002/03/18 23:53:12 psotfx Exp $
*
****************************************************************************/

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

/***************************************************************************
*
*   These functions are mainly for use in the db_utilities under the admin
*   however in order to make these functions available elsewhere, specifically
*   in the installation phase of phpBB I have seperated out a couple of
*   functions into this file.  JLH
*
\***************************************************************************/

//
// remove_comments will strip the sql comment lines out of an uploaded sql file
// specifically for mssql and postgres type files in the install....
//
function remove_comments(&$output)

$lines = explode("\n", $output);
$output = "";

// try to keep mem. use down
$linecount = count($lines);

$in_comment = false;
for($i = 0; $i < $linecount; $i++)

    if( preg_match("/^\/\*/", preg_quote($lines[$i])) )
    
        $in_comment = true;
    

    if( !$in_comment )
    
        $output .= $lines[$i] . "\n";
    

    if( preg_match("/\*\/$/", preg_quote($lines[$i])) )
    
        $in_comment = false;
    


unset($lines);
return $output;


//
// remove_remarks will strip the sql comment lines out of an uploaded sql file
//
function remove_remarks($sql)

$lines = explode("\n", $sql);

// try to keep mem. use down
$sql = "";

$linecount = count($lines);
$output = "";

for ($i = 0; $i < $linecount; $i++)

    if (($i != ($linecount - 1)) || (strlen($lines[$i]) > 0))
    
        if (isset($lines[$i][0]) && $lines[$i][0] != "#")
        
            $output .= $lines[$i] . "\n";
        
        else
        
            $output .= "\n";
        
        // Trading a bit of speed for lower mem. use here.
        $lines[$i] = "";
    


return $output;



//
// split_sql_file will split an uploaded sql file into single sql statements.
// Note: expects trim() to have already been run on $sql.
//
function split_sql_file($sql, $delimiter)

// Split up our string into "possible" SQL statements.
$tokens = explode($delimiter, $sql);

// try to save mem.
$sql = "";
$output = array();

// we don't actually care about the matches preg gives us.
$matches = array();

// this is faster than calling count($oktens) every time thru the loop.
$token_count = count($tokens);
for ($i = 0; $i < $token_count; $i++)

    // Don't wanna add an empty string as the last thing in the array.
    if (($i != ($token_count - 1)) || (strlen($tokens[$i] > 0)))
    
        // This is the total number of single quotes in the token.
        $total_quotes = preg_match_all("/'/", $tokens[$i], $matches);
        // Counts single quotes that are preceded by an odd number of backslashes,
        // which means they're escaped quotes.
        $escaped_quotes = preg_match_all("/(?<!\\\\)(\\\\\\\\)*\\\\'/", $tokens[$i], $matches);

        $unescaped_quotes = $total_quotes - $escaped_quotes;

        // If the number of unescaped quotes is even, then the delimiter did NOT occur inside a string literal.
        if (($unescaped_quotes % 2) == 0)
        
            // It's a complete sql statement.
            $output[] = $tokens[$i];
            // save memory.
            $tokens[$i] = "";
        
        else
        
            // incomplete sql statement. keep adding tokens until we have a complete one.
            // $temp will hold what we have so far.
            $temp = $tokens[$i] . $delimiter;
            // save memory..
            $tokens[$i] = "";

            // Do we have a complete statement yet?
            $complete_stmt = false;

            for ($j = $i + 1; (!$complete_stmt && ($j < $token_count)); $j++)
            
            // This is the total number of single quotes in the token.
            $total_quotes = preg_match_all("/'/", $tokens[$j], $matches);
            // Counts single quotes that are preceded by an odd number of backslashes,
            // which means they're escaped quotes.
            $escaped_quotes = preg_match_all("/(?<!\\\\)(\\\\\\\\)*\\\\'/", $tokens[$j], $matches);

            $unescaped_quotes = $total_quotes - $escaped_quotes;

            if (($unescaped_quotes % 2) == 1)
            
                // odd number of unescaped quotes. In combination with the previous incomplete
                // statement(s), we now have a complete statement. (2 odds always make an even)
                $output[] = $temp . $tokens[$j];

                // save memory.
                $tokens[$j] = "";
                $temp = "";

                // exit the loop.
                $complete_stmt = true;
                // make sure the outer loop continues at the right point.
                $i = $j;
            
            else
            
                // even number of unescaped quotes. We still don't have a complete statement.
                // (1 odd and 1 even always make an odd)
                $temp .= $tokens[$j] . $delimiter;
                // save memory.
                $tokens[$j] = "";
            

             // for..
         // else
    


return $output;


$dbms_schema = 'yourfile.sql';

$sql_query = @fread(@fopen($dbms_schema, 'r'), @filesize($dbms_schema)) or die('problem ');
$sql_query = remove_remarks($sql_query);
$sql_query = split_sql_file($sql_query, ';');

$host = 'localhost';
$user = 'user';
$pass = 'pass';
$db = 'database_name';

// mysql_* is deprecated, prefer using mysqli_* instead
// mysql_connect($host,$user,$pass) or die('error connection');
// mysql_select_db($db) or die('error database selection');
$connection = mysqli_connect($host,$user,$pass) or die('error connection');
mysqli_select_db($connection, $db) or die('error database selection');

$i=1;
foreach($sql_query as $sql)
    echo $i++;
    echo "<br />";
    // mysql_* is deprecated, prefer using mysqli_* instead
    // mysql_query($sql) or die('error in query');
    mysqli_query($connection, $sql) or die('error in query');

【讨论】:

这应该是被接受的。工作就像一个魅力,谢谢。 这是我遇到的这个问题的最佳解决方案 感谢 phpBB Group 并感谢 Abu Sadat,您设法注意到这段代码也可用于其他项目。再次感谢 谢谢!在​​我这边也很好用!如果您的 mysql 已被弃用,只需更改为 mysqli 函数。 php.net/manual/en/mysqli.query.php 请注意,PhpBB 是 GPL 许可的。因此,如果您在项目中使用此代码,则还必须以 GPL 许可的形式发布您的项目。【参考方案2】:
    $sql = file_get_contents("sql.sql");

似乎是最简单的答案

【讨论】:

确实是最简单的答案。但是这里的人们想要使他们的生活复杂化。阻止用户发布问题是他们唯一知道最好做的事情!【参考方案3】:

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

无论如何,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);

显然,如果您要批量运行大量查询,则需要考虑事务和其他问题,但对于新安装脚本而言,这可能不是什么大问题。

【讨论】:

这并不总是有效.. 如果您有类似的查询怎么办.. SELECT example FROM blah WHERE something = "something;" if (substr(rtrim($query, -1) == ';') 不正确。应该是: if (substr(rtrim($query), -1) == ' ;') file() 读取分行的文件就好了,代码也干净了很多。 file() 的问题是它会将整个文件一次全部读入内存,这对于大文件来说并不理想。【参考方案4】:

这是来自我正在从事的一个项目。基本上采用任何文本文件并提取 SQL 语句,同时忽略 cmets 和无故换行符。

<?php

  /*
     ingestSql(string) : string

     Read the contents of a SQL batch file, stripping away comments and
     joining statements that are broken over multiple lines with the goal
     of producing lines of sql statements that can be successfully executed
     by PDO exec() or execute() functions.

     For example:
       -- My SQL Batch
       CREATE TABLE foo(
         bar VARCHAR(80),
         baz INT NOT NULL);

     Becomes:
       CREATE TABLE foo(bar VARCHAR(80), baz INT NOT NULL);
  */

  function ingestSql($sqlFilePath=__DIR__ . "/create-db.sql") 
    $sqlFile = file($sqlFilePath);
    $ingestedSql = "";
     $statement = "";
    foreach($sqlFile as $line) 

      // Ignore anything between a double-dash and the end of the line.
      $commentStart = strpos($line, "--");
      if ($commentStart !== false) 
        $line = substr($line, 0, $commentStart);
      

      // Only process non-blank lines.
      if (strlen($line)) 

        // Remove any leading and trailing whitespace and append what's
        // left of the line to the current statement.
        $line = trim($line);
        $statement .= $line;

        // A semi-colon ends the current statement.  Otherwise what was a
        // newline becomes a single space;
        if (substr($statement, -1) == ";") 
          $ingestedSql .= $statement;
          $statement = "\n";
        
        else 
          $statement .= " ";
        
      
    

    return $ingestedSql;
  

?>

【讨论】:

【参考方案5】:

我注意到 PostgreSQL PDO 驱动程序不允许您运行以分号分隔的脚本。为了使用 PDO 在任何数据库上运行 .sql 文件,有必要自己拆分 PHP 代码中的语句。这是一个似乎效果很好的解决方案:

https://github.com/diontruter/migrate/blob/master/src/Diontruter/Migrate/SqlScriptParser.php

引用的类以独立于数据库的方式为我解决了问题,如果有任何问题,请给我留言。以下是在将脚本添加到项目后如何使用它:

$pdo = new PDO($connectionString, $userName, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$parser = new SqlScriptParser();
$sqlStatements = $parser->parse($fileName);
foreach ($sqlStatements as $statement) 
    $distilled = $parser->removeComments($statement);
    if (!empty($distilled)) 
        $statement = $pdo->prepare($sql);
        $affectedRows = $statement->execute();
    

【讨论】:

【参考方案6】:

简而言之,我这样做的方式是:

    读取文件(数据库转储,例如$ mysqldump db &gt; db.sql

    $sql = file_get_contents(db.sql);
    

    使用 mysqli::multi_query 导入

    if ($mysqli->multi_query($sql)) 
        $mysqli->close();
     else 
        throw new Exception ($mysqli->error);
    
    

注意 mysqli_query 支持异步查询。更多:http://php.net/manual/en/mysqli.multi-query.php 和这里https://***.com/a/6652908/2002493

【讨论】:

【参考方案7】:

由于我无法对答案发表评论,请注意使用以下解决方案:

$db = new PDO($dsn, $user, $password);

$sql = file_get_contents('file.sql');

$qr = $db->exec($sql);

PHP PDO https://bugs.php.net/bug.php?id=61613有一个bug

db->exec('SELECT 1; invalidstatement; SELECT 2');

不会出错或返回 false(在 PHP 5.5.14 上测试)。

【讨论】:

我最近了解到,这是因为你需要使用PDOStatement::nextRowset【参考方案8】:

我有一个没有 mysql 工具或 phpmyadmin 的环境,只有我的 php 应用程序连接到不同主机上的 mysql 服务器,但我需要运行 mysqldump 或 myadmin 导出的脚本。为了解决这个问题,我创建了一个脚本multi_query,正如我提到的here

它可以在没有mysql命令行工具的情况下处理mysqldump输出和phpmyadmin导出。我还根据存储在数据库中的时间戳(如 Rails)制定了一些逻辑来处理多个迁移文件。我知道它需要更多的错误处理,但目前可以为我工作。

查看:https://github.com/kepes/php-migration

它是纯php,不需要任何其他工具。如果您不使用它处理用户输入,只使用开发人员制作的脚本或导出工具,您可以安全地使用它。

【讨论】:

【参考方案9】:
mysql_query("LOAD DATA LOCAL INFILE '/path/to/file' INTO TABLE mytable");

【讨论】:

如果 apachemysql 是不同的服务器则不起作用【参考方案10】:

最简单的解决方案是使用 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 脚本的子集,不包括上面的一些极端情况,那么编写一个读取文件并执行文件中的 SQL 语句的 PHP 脚本相对容易。但如果你想支持任何有效的 SQL 脚本,那就要复杂得多了。


另请参阅我对这些相关问题的回答:

Running MySQL *.sql files in PHP is it possible to call a sql script from a stored procedure in another sql script? PHP: multiple SQL queries in one mysql_query statement

【讨论】:

【参考方案11】:

这可能会有所帮助-->

它所做的或多或少是首先获取给函数的字符串(file.sql 的 file_get_contents() 值)并删除所有换行符。然后它用“;”分割数据特点。接下来它进入一个while循环,查看创建的数组的每一行。如果该行包含“`”字符,它将知道这是一个查询并为给定的行数据执行 myquery() 函数。

代码:

function myquery($query) 

mysql_connect(dbhost, dbuser, dbpass);

mysql_select_db(dbname);

$result = mysql_query($query);

if (!mysql_errno() && @mysql_num_rows($result) > 0) 


else 

$result="not";

mysql_close();

return $result;





function mybatchquery ($str) 

$sql = str_replace("\n","",$str)

$sql = explode(";",$str);

$x=0;

while (isset($str[$x])) 

if (preg_match("/(\w|\W)+`(\w|\W)+) 

myquery($str[$x]);



$x++



return TRUE;






function myrows($result) 

$rows = @mysql_num_rows($result);

return $rows;





function myarray($result) 

$array = mysql_fetch_array($result);

return $array;





function myescape($query) 

$escape = mysql_escape_string($query);

return $escape;




$str = file_get_contents("foo.sql");
mybatchquery($str);

【讨论】:

【参考方案12】:

有些人 (Plahcinski) 建议使用此代码:

$file_content = file('myfile.sql');
$query = "";
foreach($file_content as $sql_line)
  if(trim($sql_line) != "" && strpos($sql_line, "--") === false)
    $query .= $sql_line;
    if (substr(rtrim($query), -1) == ';')
      echo $query;
      $result = mysql_query($query)or die(mysql_error());
      $query = "";
    
  
 

但我会用对我有用的更新它:

 //selecting my database
    $database = 'databaseTitleInFile';
    $selectDatabase = mysql_select_db($database, $con);
    if(! $selectDatabase )
    
      die('Could not select the database: ' . mysql_error());
    
    echo "The database " . $database . " selected successfully\n";
//reading the file
    $file_path='..\yourPath\to\File';
    if(!file_exists($file_path))
        echo "File Not Exists";
    
    $file_content = file_get_contents($file_path);
    $array = explode("\n", $file_content)
//making queries
    $query = "";
        foreach($array as $sql_line)
$sql_line=trim($sql_line);
          if($sql_line != "" && substr($sql_line, 0, 2) === "--" && strpos($sql_line, "/*") === false)
            $query .= $sql_line;
            if (substr(rtrim($query), -1) == ';')
              $result = mysql_query($query)or die(mysql_error());
              $query = "";
            
          
         

因为它更全面。 ;-)

【讨论】:

【参考方案13】:

这实际上对我有用:

/* load sql-commands from a sql file */
function loadSQLFromFile($url)

    // ini_set ( 'memory_limit', '512M' );
    // set_time_limit ( 0 );

    global $settings_database_name;
    global $mysqli_object; global $worked; $worked = false;

    $sql_query = "";

    // read line by line
    $lines = file($url);
    $count = count($lines);

    for($i = 0;$i<$count;$i++)
    
        $line = $lines[$i];
        $cmd3 = substr($line, 0, 3);
        $cmd4 = substr($line, 0, 4);
        $cmd6 = substr($line, 0, 6);
        if($cmd3 == "USE")
        
            // cut away USE ``;
            $settings_database_name = substr($line, 5, -3);
        
        else if($cmd4 == "DROP")
        
            $mysqli_object->query($line); // execute this line
        
        else if(($cmd6 == "INSERT") || ($cmd6 == "CREATE"))
        
            // sum all lines up until ; is detected
            $multiline = $line;
            while(!strstr($line, ';'))
            
                $i++;
                $line = $lines[$i];
                $multiline .= $line;
            
            $multiline = str_replace("\n", "", $multiline); // remove newlines/linebreaks
            $mysqli_object->query($multiline); // execute this line
               
    

    return $worked;

?>

【讨论】:

【参考方案14】:

在我的项目中,我使用了下一个解决方案:

<?php

/**
 * Import SQL from file
 *
 * @param string path to sql file
 */
function sqlImport($file)


    $delimiter = ';';
    $file = fopen($file, 'r');
    $isFirstRow = true;
    $isMultiLineComment = false;
    $sql = '';

    while (!feof($file)) 

        $row = fgets($file);

        // remove BOM for utf-8 encoded file
        if ($isFirstRow) 
            $row = preg_replace('/^\xEF\xBB\xBF/', '', $row);
            $isFirstRow = false;
        

        // 1. ignore empty string and comment row
        if (trim($row) == '' || preg_match('/^\s*(#|--\s)/sUi', $row)) 
            continue;
        

        // 2. clear comments
        $row = trim(clearSQL($row, $isMultiLineComment));

        // 3. parse delimiter row
        if (preg_match('/^DELIMITER\s+[^ ]+/sUi', $row)) 
            $delimiter = preg_replace('/^DELIMITER\s+([^ ]+)$/sUi', '$1', $row);
            continue;
        

        // 4. separate sql queries by delimiter
        $offset = 0;
        while (strpos($row, $delimiter, $offset) !== false) 
            $delimiterOffset = strpos($row, $delimiter, $offset);
            if (isQuoted($delimiterOffset, $row)) 
                $offset = $delimiterOffset + strlen($delimiter);
             else 
                $sql = trim($sql . ' ' . trim(substr($row, 0, $delimiterOffset)));
                query($sql);

                $row = substr($row, $delimiterOffset + strlen($delimiter));
                $offset = 0;
                $sql = '';
            
        
        $sql = trim($sql . ' ' . $row);
    
    if (strlen($sql) > 0) 
        query($row);
    

    fclose($file);


/**
 * Remove comments from sql
 *
 * @param string sql
 * @param boolean is multicomment line
 * @return string
 */
function clearSQL($sql, &$isMultiComment)

    if ($isMultiComment) 
        if (preg_match('#\*/#sUi', $sql)) 
            $sql = preg_replace('#^.*\*/\s*#sUi', '', $sql);
            $isMultiComment = false;
         else 
            $sql = '';
        
        if(trim($sql) == '')
            return $sql;
        
    

    $offset = 0;
    while (preg_match('--\s|#|/\*[^!]sUi', $sql, $matched, PREG_OFFSET_CAPTURE, $offset)) 
        list($comment, $foundOn) = $matched[0];
        if (isQuoted($foundOn, $sql)) 
            $offset = $foundOn + strlen($comment);
         else 
            if (substr($comment, 0, 2) == '/*') 
                $closedOn = strpos($sql, '*/', $foundOn);
                if ($closedOn !== false) 
                    $sql = substr($sql, 0, $foundOn) . substr($sql, $closedOn + 2);
                 else 
                    $sql = substr($sql, 0, $foundOn);
                    $isMultiComment = true;
                
             else 
                $sql = substr($sql, 0, $foundOn);
                break;
            
        
    
    return $sql;


/**
 * Check if "offset" position is quoted
 *
 * @param int $offset
 * @param string $text
 * @return boolean
 */
function isQuoted($offset, $text)

    if ($offset > strlen($text))
        $offset = strlen($text);

    $isQuoted = false;
    for ($i = 0; $i < $offset; $i++) 
        if ($text[$i] == "'")
            $isQuoted = !$isQuoted;
        if ($text[$i] == "\\" && $isQuoted)
            $i++;
    
    return $isQuoted;


function query($sql)

    global $mysqli;
    //echo '#<strong>SQL CODE TO RUN:</strong><br>' . htmlspecialchars($sql) . ';<br><br>';
    if (!$query = $mysqli->query($sql)) 
        throw new Exception("Cannot execute request to the database $sql: " . $mysqli->error);
    


set_time_limit(0);

$mysqli = new mysqli('localhost', 'root', '', 'test');
$mysqli->set_charset("utf8");

header('Content-Type: text/html;charset=utf-8');
sqlImport('import.sql');

echo "Peak MB: ", memory_get_peak_usage(true)/1024/1024;

在测试 sql 文件 (41Mb) 内存峰值使用量:3.25Mb

【讨论】:

@Graben 谢谢。我一直在寻找解决方案,除了Sypex Dumper 之外找不到任何可以导入大 SQL 文件的解决方案,但它不能用于您自己的项目,因为它是可移植的即用型解决方案。所以我不得不自己写并在这里发布,但是在我发布之前很久就有人问了问题:)。【参考方案15】:

加载和解析 phpmyadmin 转储或 mysql 转储文件的最简单和最快的方法..

$ mysql -u username -p -h localhost dbname < dumpfile.sql 

【讨论】:

【参考方案16】:

Plahcinski 解决方案的更新解决方案。或者,您可以使用 fopen 和 fread 来获取更大的文件:

$fp = file('database.sql', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$query = '';
foreach ($fp as $line) 
    if ($line != '' && strpos($line, '--') === false) 
        $query .= $line;
        if (substr($query, -1) == ';') 
            mysql_query($query);
            $query = '';
        
    

【讨论】:

这将跳过末尾有 cmets 的行,但在同一行的注释之前是一个真正的 SQL 语句。此外,SQL 脚本支持 /* */ 格式的 cmets。而且语句分隔符并不总是; 当您有一个相对简单的文件并且需要一个快速简单的解析器时,这是一个不错的简单解决方案。我必须通过在每行的开头添加一个空格来修改串联,以处理多行语句没有空格的情况(例如 ON UPDATE CASCADE)\nENGINE = InnoDB; 解析,但 CASCADE)\nENGINE = InnoDB\nCOMMENT = 'stuff'; 没有)【参考方案17】:

希望下面的代码能很好地解决您的问题。

//Empty all tables' contents

$result_t = mysql_query("SHOW TABLES");
while($row = mysql_fetch_assoc($result_t))

mysql_query("TRUNCATE " . $row['Tables_in_' . $mysql_database]);

// Temporary variable, used to store current query
$templine = '';
// Read in entire file
$lines = file($filename);
// Loop through each line
foreach ($lines as $line)

// Skip it if it's a comment
if (substr($line, 0, 2) == '--' || $line == '')
    continue;

// Add this line to the current segment
$templine .= $line;
// If it has a semicolon at the end, it's the end of the query
if (substr(trim($line), -1, 1) == ';')

    // Perform the query
    mysql_query($templine) or print('Error performing query \'<strong>' . $templine . '\': ' . mysql_error() . '<br /><br />');
    // Reset temp variable to empty
    $templine = '';



?>

【讨论】:

【参考方案18】:
$db = new PDO($dsn, $user, $password);

$sql = file_get_contents('file.sql');

$qr = $db->exec($sql);

【讨论】:

我正在使用这个答案。它对我来说在 643kb 脚本上运行良好。到目前为止一切顺利。 脚本是643mb的时候怎么样?或者无论如何,大于max_allowed_packet 完美!到目前为止,对我来说只有 1MB 的文件。 这主要对我有用。它似乎没有导入存储过程或触发器,但可以很好地处理 DROP、TRUNCATE、SELECT、INSERT、UPDATE。 有什么方法可以显示有关任何 SQL 条目的错误? $qr 可能等于 false,但如果 file.sql 中给出的任何命令有问题,则不会给出具体错误,【参考方案19】:

试试这个:

// SQL File
$SQLFile = 'YourSQLFile.sql';

// Server Name
$hostname = 'localhost';

// User Name
$db_user = 'root';

// User Password
$db_password = '';

// DBName
$database_name = 'YourDBName';

// Connect MySQL
$link = mysql_connect($hostname, $db_user, $db_password);

if (!$link) 
die("MySQL Connection error");


// Select MySQL DB
mysql_select_db($database_name, $link) or die("Wrong MySQL Database");

// Function For Run Multiple Query From .SQL File
function MultiQuery($sqlfile, $sqldelimiter = ';') 
set_time_limit(0);

if (is_file($sqlfile) === true) 
$sqlfile = fopen($sqlfile, 'r');

if (is_resource($sqlfile) === true) 
$query = array();
echo "<table cellspacing='3' cellpadding='3' border='0'>";

while (feof($sqlfile) === false) 
$query[] = fgets($sqlfile);

if (preg_match('~' . preg_quote($sqldelimiter, '~') . '\s*$~iS', end($query)) === 1) 
$query = trim(implode('', $query));

if (mysql_query($query) === false) 
echo '<tr><td>ERROR:</td><td> ' . $query . '</td></tr>';
 else 
echo '<tr><td>SUCCESS:</td><td>' . $query . '</td></tr>';


while (ob_get_level() &gt; 0) 
ob_end_flush();


flush();


if (is_string($query) === true) 
$query = array();


echo "</table>";

return fclose($sqlfile);



return false;


/* * * Use Function Like This: ** */

MultiQuery($SQLFile);

【讨论】:

【参考方案20】:

许多主机不允许您通过 PHP 创建自己的数据库,但您似乎已经解决了这个问题。 创建数据库后,您可以简单地操作和填充它:

mysql_connect("localhost"); mysql_query("源文件.sql");

【讨论】:

【参考方案21】:

这是通过 php 恢复 sql 的最佳代码可以使用 100% Goooood! 非常感谢

$file_content = file('myfile.sql');
$query = "";
foreach($file_content as $sql_line)
if(trim($sql_line) != "" && strpos($sql_line, "--") === false)
 $query .= $sql_line;
 if (substr(rtrim($query), -1) == ';')
   echo $query;
   $result = mysql_query($query)or die(mysql_error());
   $query = "";
  
 

【讨论】:

这将跳过末尾有 cmets 的行,但在同一行的注释之前是一个真正的 SQL 语句。此外,SQL 脚本支持 /* */ 格式的 cmets。而且语句分隔符并不总是; 这适用于视图、函数、插入、更改...但并非适用于所有情况,例如当您的表格中有带有字符“;”的 HTML 行时,但这可以更新..谢谢我会尝试更新和重新发布...【参考方案22】:

只是为了向大家重申这个问题:

PHP 的 mysql_query 会自动对每个 SQL 命令进行结束分隔,而且在其手册中对这样做非常模糊。超出一个命令的所有内容都会产生错误。

另一方面,mysql_query 可以使用包含 SQL 样式 cmets、\n、\r..的字符串。

mysql_query 的局限性在于 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 `outputdb:`
 (`intid`, `entry_id`, `definition`) VALUES...

这是一个快速的解决方案: (假设 SQL 格式正确;

$sqlCmds = preg_split("/[\n|\t]*;[\n|\t]*[\n|\r]$/", $sqlDump);

【讨论】:

【参考方案23】:

适用于 Navicat 转储。可能需要转储 Navicat 放入的第一个 /* */ 注释。

$file_content = file('myfile.sql');
$query = "";
foreach($file_content as $sql_line)
  if(trim($sql_line) != "" && strpos($sql_line, "--") === false)
    $query .= $sql_line;
    if (substr(rtrim($query), -1) == ';')
      echo $query;
      $result = mysql_query($query)or die(mysql_error());
      $query = "";
    
  
 

【讨论】:

会出现这样的情况:select id,any from some where any=='--'【参考方案24】:

我一直用这个:

$sql = explode(";",file_get_contents('[your dump file].sql'));// 

foreach($sql as $query)
 mysql_query($query);

【讨论】:

这在大多数情况下都有效,但如果字符串包含 ;字符。 或者如果 .sql 文件大于你的 PHP 的内存限制,或者如果 .sql 文件包含一些客户端内置命令,或者一堆其他情况。【参考方案25】:

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

例如,检查Phing 的PDOSQLExecTask

【讨论】:

【参考方案26】:

我在这里看到的所有解决方案都没有处理在无法访问 LOAD DATA INFILE 的服务器上创建存储过程时需要更改分隔符的问题。我希望发现有人已经解决了这个问题,而不必搜索 phpMyAdmin 代码来解决这个问题。和其他人一样,由于我自己在编写 GPL 代码,因此我也在寻找其他人的 GPL 方式。

【讨论】:

前几天我碰巧阅读了我在这个问题中提到的代码,所以它很新鲜。我们最终只是阅读,直到遇到 ;并执行该语句,并在 cmets 中指出它应该被改进。该项目没有任何进展,因此我们没有想出比这更好的解决方案。【参考方案27】:

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

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

【讨论】:

托管环境限制性更强。 OP 的问题没有提到应用程序需要部署在托管环境中。唔。在 PHP 中运行 SQL 脚本的问题经常出现,这将是一个很棒的小项目。 是的,我要这么说 - 你不能指望人们认为你处于有史以来最严格的环境中。尤其是随着虚拟机的普及,每个人都可以以相对较低的成本拥有自己的服务器。 我还是不明白他为什么不能直接把.sql文件读成字符串然后用PDO或者mysqli执行。我就是这样做的。 PDO 和 mysqli 支持多个查询。诚然,我还没有运行任何巨大的 .sql 文件,但你不能增加或删除 PHP 的最大脚本执行时间吗? phpMyAdmin 的代码库是 sh*t。执行导入的文件 (phpMyAdmin/library/import/sql.php) 大量使用全局变量,并且许多 cmets 存在语法错误。你知道其他更好的例子吗? 查看下面 Luis Granja ***.com/a/7178917/80353的答案【参考方案28】:

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

你可以读入整个文件并使用mysqli_multi_query()一次性运行它

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

【讨论】:

【参考方案29】:

我的建议是查看 PHPMyBackup 的源代码。它是一个自动化的 PHP SQL 加载器。您会发现 mysql_query 一次只加载一个查询,而 PHPMyAdmin 和 PHPMyBackup 等项目已经为您完成了正确解析 SQL 的艰苦工作。请不要重新发明那个***:P

【讨论】:

FWIW,phpMyBackup 和 phpMyAdmin 都在 GPL 下获得许可。如果你“借用”他们的任何代码,你也有义务制作自己的项目 GPL。 是的,我同意。您确实有一个很好的观点,但是本着 GPL 的精神,如果必须实现这样一个他自己的特点。例如,有时必须重新创建***以规避 GPL 限制!不过,在我看来,这通常是值得的,因为并非所有***都是平等的。在这种情况下,一个简单的控制台 10-15 衬里脚本就可以做到这一点。 当然,它使您的代码成为 GPL,但如果您不分发软件,GPL 就无关紧要 - 例如。大多数网络应用程序。 (AGPL 是一个明显的例外,尽管 AFAIK 从未在法庭上进行过测试。)【参考方案30】:

除非您打算导入 巨大 .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 的回答中,如果查询在字符串中有分号,则会中断)..

【讨论】:

不幸的是,mysql_query 一次只会执行一个查询;) $query="SELECT * FROM posts LIMIT 1; SELECT * FROM posts LIMIT 1"; mysql_query($query); 似乎运行良好..?我猜您无法获取每个查询的结果,但是如果您只是加载 .sql 文件,那么您肯定需要检查的只是查询错误吗?

以上是关于从 PHP 中加载 .sql 文件的主要内容,如果未能解决你的问题,请参考以下文章

在 H2 中初始化数据时从文件中加载数据

ORM(Linq-SQL EF)是不是从表中加载整个数据集?

内存数据库H2中的Spring Boot在初始化时不会从文件中加载数据

将 PHP 从 5.3.4 升级到 5.3.22 后,PHP CURL 未在 WAMP 中加载

php ajax依赖下拉列表不从表中加载数据

使用 Bootstrap 4 在主活动选项卡错误中加载选项卡内容 - PHP