在整个数据库中用新域替换旧域的实例

时间:2018-09-16 10:25:48

标签: php mysql sql mysqli phpmyadmin

我有一个MYSQL数据库,其中包含旧域的多个21500实例,我想用新域更改旧域。

我搜索了如何替换整个数据库,并发现了以下内容:

使用SQL:

create procedure replace_all(find varchar(255), 
        replce varchar(255), 
        indb varcv=char(255))
 DECLARE loopdone INTEGER DEFAULT 0;
 DECLARE currtable varchar(100);
 DECLARE alltables CURSOR FOR SELECT t.tablename, c.column_name 
    FROM information_schema.tables t,
    information_schema.columns c
    WHERE t.table_schema=indb
    AND c.table_schema=indb
    AND t.table_name=c.table_name;

 DECLARE CONTINUE HANDLER FOR NOT FOUND
     SET loopdone = 1;

 OPEN alltables;

 tableloop: LOOP
    FETCH alltables INTO currtable, currcol; 
    IF (loopdone>0) THEN LEAVE LOOP;
    END IF;
         SET stmt=CONCAT('UPDATE ', 
                  indb, '.', currtable, ' SET ',
                  currcol, ' = word_sub(\'', find, 
                  '\','\'', replce, '\') WHERE ',
                  currcol, ' LIKE \'%', find, '%\'');
         PREPARE s1 FROM stmt;
         EXECUTE s1;
         DEALLOCATE PREPARE s1;
     END LOOP;
 END //

然后:

use information_schema;
set @q = 'Boston';
set @t = 'my_db';

select CONCAT('use \'',@q,'\';') as q UNION
select CONCAT('select \'', tbl.`TABLE_NAME`,'\' as TableName, \'', col.`COLUMN_NAME`,'\' as Col, `',col.`COLUMN_NAME`,'` as value  from `' , tbl.`TABLE_NAME`,'` where `' ,
        col.`COLUMN_NAME` , '` like \'%' ,@q,  '%\' UNION') AS q
from `tables` tbl
inner join `columns` col on tbl.`TABLE_NAME` = col.`TABLE_NAME`and col.DATA_TYPE='varchar'
where tbl.TABLE_SCHEMA  =  @t  ;

也:

DROP PROCEDURE IF EXISTS findAll;
CREATE PROCEDURE `findAll`( IN `tableName` VARCHAR( 28 ) , IN `search` TEXT )
BEGIN
       DECLARE finished INT DEFAULT FALSE ;
       DECLARE columnName VARCHAR ( 28 ) ;
       DECLARE stmtFields TEXT ;
       DECLARE columnNames CURSOR FOR
              SELECT DISTINCT `COLUMN_NAME` FROM `information_schema`.`COLUMNS`
              WHERE `TABLE_NAME` = tableName ORDER BY `ORDINAL_POSITION` ;
       DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = TRUE;
       SET stmtFields = '' ;
       OPEN columnNames ;
       readColumns: LOOP
              FETCH columnNames INTO columnName ;
              IF finished THEN
                     LEAVE readColumns ;
              END IF;
              SET stmtFields = CONCAT(
                     stmtFields , IF ( LENGTH( stmtFields ) > 0 , ' OR' , ''  ) ,
                     ' `', tableName ,'`.`' , columnName , '` REGEXP "' , search , '"'
              ) ;
       END LOOP;
       SET @stmtQuery := CONCAT ( 'SELECT * FROM `' , tableName , '` WHERE ' , stmtFields ) ;
       PREPARE stmt FROM @stmtQuery ;
       EXECUTE stmt ;
       CLOSE columnNames ;
END;

使用PHP:

// Connect to your MySQL database.
$hostname = "hostname";
$username = "username";
$password = "password";
$database = "database";

mysql_connect($hostname, $username, $password);

// The find and replace strings.
$find = "old domain";
$replace = "new domain";

$loop = mysql_query("
    SELECT
        concat('UPDATE ',table_schema,'.',table_name, ' SET ',column_name, '=replace(',column_name,', ''{$find}'', ''{$replace}'');') AS s
    FROM
        information_schema.columns
    WHERE
        table_schema = '{$database}'")
or die ('Cant loop through dbfields: ' . mysql_error());

while ($query = mysql_fetch_assoc($loop))
{
        mysql_query($query['s']);
}

但是这些片段中的大多数似乎都不是完整的/有效的。

那么如何用olddomain.com来更新newdomain.com呢?

2 个答案:

答案 0 :(得分:0)

尊敬的,您希望完成此任务的方式使我感到恐惧。毫无疑问,您已经积累了数年的宝贵数据,并且希望对它进行全球替代吗?您可能会严重崩溃。

明智的做法是找到需要更改的表和列,将它们查找一下,然后一一更改。原则上,更新列中的URL非常简单。像这样的事情应该做。

 UPDATE table 
    SET link = REPLACE(link, 'https://former.example.com', 'https://new.example.com')
  WHERE link like CONCAT('%','https://former.example.com','%')

请注意,此地址将替换对特定主机/域名的https引用。这不是一个很混杂的替换策略:

  • 除非是https网址,否则它不会更改数据
  • 它仅更新需要更新的行(由于WHERE子句)
  • 它仅更新您要更新的表和列。

如果您的表是用php生成的,则必须小心。数据库中的某些列可能包含serialized php data -- the output of serialize()。即使数据库设计人员不愿使用存储在数据库列中的序列化数据来折磨数据库,但这是很常见的。 (例如,WordPress在wp_options表中做到了。)

问题是这样的:

 $links = array(
    'top' => 'https://former.example.com/index.php'
  );
 $serial = serialize($links);

将其放入$serial,对字符串(s:36")的长度进行编码。

 a:2:{s:3:"top";s:36:"https://former.example.com/index.php";}

如果仅更改此序列化数据中的URL,则可以更改字符串的实际长度,而无需更改编码长度。这会破坏序列化的数据。 (Rasmus Lerdorf和其他php作者是否选择了一种好的序列化数据格式?这又是一天的对话)。

因此,如果您要尝试使用MySQL REPLACE()或类似方法更新数据库中的序列化文本字符串,则不能更改其长度。

我的观点:在更新之前先了解您要更新的内容。

您可以查询information_schema.COLUMNS来编写查询列表,这些查询将在所有列中查找要更改的文本字符串。

SET @string = 'former.example.com';

SELECT CONCAT(
      'SELECT COUNT(*) FREQUENCY ,', 
      COLUMN_NAME, ' ''VALUE'', ',
        '''',  TABLE_NAME , ''' TABLE_NAME, ', 
       '''',  COLUMN_NAME ,''' COLUMN_NAME ', 
        'FROM ',TABLE_NAME,' ',
      'WHERE ', COLUMN_NAME,' LIKE ''%', @string, '%'' ',
      'GROUP BY ', COLUMN_NAME, ' ORDER BY 1 DESC'
          )   query
  FROM information_schema.COLUMNS
 WHERE TABLE_SCHEMA = DATABASE()
   AND DATA_TYPE = 'varchar'

然后,您可以检查每个表中包含要替换的字符串的每一列。只有这样,您才能确保正确更换它。

答案 1 :(得分:0)

如@dnFer的注释中所述,转储db替换字符串并再次导入。

可以轻松地用几行bash编写脚本,例如:

#!/bin/bash

DB_USER="..."
DB_PASS="..."
DB_BASE="..."

DUMP=$(mysqldump --add-drop-table --user="$DB_USER" --password="$DB_PASS" $DB_BASE)

echo "${DUMP/olddomain.com/newdomain.com}" | mysql --user="$DB_USER" --password="$DB_PASS" $DB_BASE

显然先备份,对吧?