PHP 备份表与还原表 其中遇到的重难点主要为 1. 每1000条数据取出组装成VALUES(),()结构的数据来加快写入,否则还原写入数据库太慢,目前4000条数据可以秒进。 2. MYSQL版本不同,部分datetime字段如为空字符串会报错无法写入,需要保留NULL。 > Incorrect datetime value: '0000-00-00 00:00:00' for column 上代码: ```sql //保存的SQL文件 protected $sqlfile; public function initialize() { $this->sqlfile = 'srm_payable' . date('His'); } /** * 任务函数 */ public function job() { set_time_limit(0); $model = new Model(); //备份表 $sqlfile = $this->backup($model->getTable(), 0); //还原表到另一个库 $sql_array = $this->recover($sqlfile); // 循环遍历执行$sqls里的sql语句 foreach ($sql_array as $sql) { if (!Db::connect(self::$connect_config)->execute($sql)) { echo "error sql:" . $sql; } } return ajaxReturn(0, 'success');; } /** * 备份表结构 * @param string $table 表名 * @param integer $start 起始行数 * @return boolean false - 备份失败 */ public function backup($table, $start) { $db = Db::connect(); $sqlfile = $this->sqlfile; // 备份表结构 if (0 == $start) { $result = $db->query("SHOW CREATE TABLE `{$table}`"); $sql = "\n"; $sql .= "-- -----------------------------\n"; $sql .= "-- Table structure for `{$table}`\n"; $sql .= "-- -----------------------------\n"; $sql .= "DROP TABLE IF EXISTS `{$table}`;\n"; $sql .= trim($result[0]['Create Table']) . ";\n\n"; if (false === $this->writeSql($sql, $sqlfile)) { return false; } } //数据总数 $result = $db->query("SELECT COUNT(*) AS count FROM `{$table}`"); $count = $result['0']['count']; //备份表数据 if ($count) { //写入数据注释 if (0 == $start) { $sql = "-- -----------------------------\n"; $sql .= "-- Records of `{$table}`\n"; $sql .= "-- -----------------------------\n"; $this->writeSql($sql, $sqlfile); } //备份数据记录,将1000条记录拼接成VALUES,提升写入速度 $result = $db->query("SELECT * FROM `{$table}` LIMIT {$start}, 1000"); $sql_temp = ''; foreach ($result as $row) { $temp_str = ''; foreach ($row as $tk => $temp) { //解决MYSQL5.7的情况下空字符串无法插入时间的错误,同时如果字段值带引号需要转义 $temp = is_null($temp) ? "NULL, " : "'" . addslashes($temp) . "', "; $temp_str .= $temp; } $temp_str = substr($temp_str, 0, strlen($temp_str) - 2); $sql_temp .= "(" . str_replace(array("\r", "\n"), array('\\r', '\\n'), $temp_str) . "),"; } $sql_temp = substr($sql_temp, 0, strlen($sql_temp) - 1); $sql = "INSERT INTO `{$table}` VALUES " . $sql_temp . ";\n"; if (false === $this->writeSql($sql, $sqlfile)) { return false; } //还有更多数据 if ($count > $start + 1000) { return $this->backup($table, $start + 1000); } } return $sqlfile; } /** * 将sql文件保存下来 */ public function writeSql($data, $file = 'sql') { $url = Env::get('runtime_path') . '/sql/' . date('Ymd') . '/' . $file . '.sql'; $dir_name = dirname($url); //目录不存在就创建 if (!file_exists($dir_name)) { //iconv防止中文名乱码 $res = mkdir(iconv("UTF-8", "GBK", $dir_name), 0777, true); } $fp = fopen($url, "a"); //打开文件资源通道 不存在则自动创建 fwrite($fp, $data . "\r\n"); //写入文件 fclose($fp); //关闭资源通道 return $url; } /** * 返回可执行的sql数组 */ public function recover($sqlfile) { $sqlstr = ""; // 声明sql语句字符串 // 处理数组$lines,去掉注释行,放入$sqlstr中 $sqlfile = Env::get('runtime_path') . '/sql/' . date('Ymd') . '/' . $sqlfile . '.sql'; $lines = file($sqlfile); // 读取文件到数组$lines foreach ($lines as $line) { $line = trim($line); if ($line != "") { // 去除备注行 if ((substr($line, 0, 1) != "#" && substr($line, 0, 2) != "--")) { $sqlstr .= $line; } } } $sqlstr = preg_replace("/\/\*.*\*\//is", "", $sqlstr); //去掉 /**/注释 // 处理$sqlstr,用分号分成单句sql语句 $sqlstr = rtrim($sqlstr, ";"); $sql_array = explode(";", $sqlstr); return $sql_array; } ```