当前位置:  开发笔记 > 编程语言 > 正文

从PHP中加载.sql文件

如何解决《从PHP中加载.sql文件》经验,为你挑选了8个好方法。

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

那么,我如何从PHP中加载一个sql文件(如phpMyAdmin的import命令)?



1> 小智..:
$db = new PDO($dsn, $user, $password);

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

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


脚本是643mb时怎么样?或者在任何情况下,大于`max_allowed_pa​​cket`?

2> Yasin..:

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

 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';

//In case mysql is deprecated use mysqli functions. 
mysqli_connect($host,$user,$pass) or die('error connection');
mysqli_select_db($db) or die('error database selection');

$i=1;
foreach($sql_query as $sql){
echo $i++;
echo "
"; mysql_query($sql) or die('error in query'); } ?>


这应该是公认的.工作就像一个魅力,谢谢.
感谢phpBB集团,感谢Abu Sadat,你注意到这段代码也可以重用于其他项目.再次

3> Jeremy Prive..:

我觉得这里回答这个问题的每个人都不知道成为一个允许人们在自己的服务器上安装应用程序的Web应用程序开发人员是什么感觉.特别是共享托管,不允许您使用SQL,如前面提到的"LOAD DATA"查询.大多数共享主机也不允许您使用shell_exec.

现在,要回答OP,最好的办法是在变量中构建一个包含查询的PHP文件,然后运行它们.如果您决定解析.sql文件,您应该查看phpMyAdmin并获取一些从.sql文件中获取数据的想法.查看具有安装程序的其他Web应用程序,您将看到它们,而不是使用.sql文件进行查询,它们只是将它们打包成PHP文件,然后通过mysql_query运行每个字符串或者它们需要做什么.


`phpMyAdmin`的代码库是sh*t.执行导入的文件(`phpMyAdmin/library/import/sql.php`)大量使用全局变量,许多注释都有重大错误.你知道其他更好的例子吗?
我仍然不明白他为什么不能将.sql文件读入字符串并使用PDO或mysqli执行它.我就是这样做的.PDO和mysqli支持多个查询.不可否认,我还没有运行任何巨大的.sql文件,但你不能只增加或删除PHP的最大脚本执行时间吗?
好的一点是,托管环境更具限制性.OP的问题没有提到应用程序需要在托管环境中部署.嗯.在PHP中运行SQL脚本的问题经常出现,这是一个很棒的小项目.
通过Luis Granja http://stackoverflow.com/a/7178917/80353查看以下答案

4> Bill Karwin..:

最简单的解决方案是使用shell_exec()以SQL脚本作为输入来运行mysql客户端.这可能会慢一点,因为它必须fork,但你可以在几分钟内编写代码,然后回到处理有用的东西.编写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脚本,那就复杂得多.


另见我对这些相关问题的回答:

在PHP中运行MySQL*.sql文件

是否可以从另一个sql脚本中的存储过程调用sql脚本?

PHP:一个mysql_query语句中的多个SQL查询



5> phatduckk..:

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

你可以在整个文件中读取并立即运行它 mysqli_multi_query()

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



6> Gromo..:

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

 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 '#SQL CODE TO RUN:
' . htmlspecialchars($sql) . ';

'; 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



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

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

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



8> sanneo..:

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 = '';
        }
    }
}

推荐阅读
mylvfamily
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有