쿼리를 재현하는 데 필요한 데이터베이스의 하위 집합을 mysqldump 할 수 있습니까?


37

배경

select쿼리 를 재현하는 데 필요한 데이터베이스의 하위 집합을 제공하고 싶습니다 . 저의 목표는 계산 가능한 워크 플로를 재현 가능한 연구 로 만드는 것 입니다.

질문

이 select 문을 쿼리 된 데이터를 새 데이터베이스로 덤프하는 스크립트에 통합하여 데이터베이스가 새 mysql 서버에 설치 될 수 있고 명령문이 새 데이터베이스와 작동 할 수있는 방법이 있습니까? 새 데이터베이스에는 쿼리에 사용 된 레코드 외에 레코드가 포함되지 않아야합니다.

업데이트 : 명확히하기 위해 쿼리 결과의 CSV 덤프에 관심이 없습니다. 내가 할 수있는 일은 데이터베이스 하위 집합을 덤프하여 다른 컴퓨터에 설치 한 다음 쿼리 자체를 재현 가능하고 동일한 데이터 세트와 관련하여 수정할 수 있도록하는 것입니다.

예를 들어, 분석에서 여러 (이 예에서는 3) 테이블의 레코드가 필요한 데이터의 하위 집합을 쿼리 할 수 ​​있습니다.

select table1.id, table1.level, table2.name, table2.level 
       from table1 join table2 on table1.id = table2.table1_id 
       join table3 on table3.id = table2.table3_id
       where table3.name in ('fee', 'fi', 'fo', 'fum'); 

추가 기록이 없습니다. 쿼리에서 지정한 열만 원하십니까?
Richard

@Richard 나는 그것을 고려하지 않았습니다-이것을하는 방법을 아는 것이 좋을 것입니다.
David LeBauer

3
이것은 매우 독창적 인 질문으로, 일부는 궁금해하고 대답해야했습니다. 이 유형의 질문을 공개하면 +1입니다.
RolandoMySQLDBA

향후 독자 : 허용되는 답변 외에도 randomx 's answer를 참조하십시오 .이 쿼리는 특히 쿼리에 필요한 데이터를 덤프합니다.
ToolmakerSteve

답변:


51

mysqldump 에는 주어진 테이블에 대해 WHERE 절을 실행하는 --where 옵션이 있습니다.

조인 쿼리를 mysqldump 할 수는 없지만 각 테이블에서 가져온 모든 행이 나중에 조인에 참여하도록 각 테이블에서 특정 행을 내보낼 수 있습니다.

주어진 쿼리에 대해 세 번 mysqldump해야합니다.

먼저 mysql은 이름이 ( 'fee', 'fi', 'fo', 'fum') 인 모든 table3 행을 덤프합니다.

mysqldump -u... -p... --where="name in ('fee','fi','fo','fum')" mydb table3 > table3.sql

다음으로 mysqldump는 첫 번째 mysqldump의 table3_id 값과 일치하는 모든 table2 행을 덤프합니다.

mysqldump -u... -p... --lock-all-tables --where="table3_id in (select id from table3 where name in ('fee','fi','fo','fum'))" mydb table2 > table2.sql

그런 다음 mysqldump는 두 번째 mysqldump의 table1_id 값과 일치하는 모든 table1 행을 덤프합니다.

mysqldump -u... -p... --lock-all-tables --where="id in (select table1_id from table2 where table3_id in (select id from table3 where name in ('fee','fi','fo','fum')))" mydb table1 > table1.sql

참고 : 두 번째 및 세 번째 mysqldump에는 둘 이상의 테이블을 사용해야하므로 --lock-all-tables를 사용해야합니다 .

새 데이터베이스를 작성하십시오.

mysqladmin -u... -p... mysqladmin create newdb

마지막으로, 세 개의 mysqldump를 다른 데이터베이스에로드하고 새 데이터베이스에서 조인을 시도하십시오.

mysql -u... -p... -D newdb < table1.sql
mysql -u... -p... -D newdb < table2.sql
mysql -u... -p... -D newdb < table3.sql

MySQL 클라이언트에서 조인 쿼리를 실행하십시오.

mysql> use newdb
mysql> select table1.id, table1.level, table2.name, table2.level 
       from table1 join table2 on table1.id = table2.table1_id 
       join table3 on table3.id = table2.table3_id
       where table3.name in ('fee', 'fi', 'fo', 'fum'); 

시도 해봐 !!!

경고 : 올바르게 색인되지 않으면 두 번째 및 세 번째 mysqldumps가 영원히 걸릴 수 있습니다!

만일을 대비하여 다음 열을 색인화하십시오.

ALTER TABLE table2 ADD INDEX (table1_id);
ALTER TABLE table2 ADD INDEX (table3_id);
ALTER TABLE table3 ADD INDEX (name,id);

id가 table3의 기본 키라고 가정하겠습니다.


1
자세한 예를 주셔서 감사합니다! 나는 --where문서 의 조항을 놓쳤다 . 시험해 볼 기회가 생기면 어떻게 작동하는지 알려 드리겠습니다.
David LeBauer

1
+1이 문제에 대한 --tables 방법보다이 방법이 더 좋습니다. 일반적으로 --tables를 사용하지만 --where는 매우 좋은 옵션입니다.
Richard

단일 테이블을 mysqldump 할 때는 --lock-all-tables가 사용되지 않습니다. where 절이 덤프되는 테이블 이외의 테이블을 포함하기 때문에 mysqldump --lock-all-tables에 알려야합니다. --lock-all-tables 옵션은 단일 테이블이 아닌 하나 이상의 데이터베이스를 덤프 할 때 활성화됩니다. 두 번째 및 세 번째 mysqldumps를 수행하려고 시도했지만 이에 대해 불평했습니다. 수동으로 --lock-all-tables를 발행하면 오류가 사라지고 mysqldump가 성공했습니다. 또한 내 대답의 첫 번째 mysqldump에 --lock-all-tables가 없습니다.
RolandoMySQLDBA

@Rolando 도와 주셔서 감사합니다. 이것은 완벽하게 작동했습니다
David LeBauer

@Rolando 죄송합니다. 삭제하기 전에 내 의견 / 질문에 답변 한 것을 확인하지 못했습니다. 같은 오류가 발생했습니다. 매뉴얼을 다시 읽은 후 --lock- tables는 덤프되는 테이블 만 잠급니다. --lock-all-tables 는 모든 데이터베이스에서 모든 테이블을 잠그기 때문에 혼란 스러웠습니다 . 단일 데이터베이스 만 사용할 때 필요하지 않습니다.
David LeBauer

7

나는 고려할 에 'OUTFILE'를 사용하여 이 문제를 해결하기 위해 SELECT의 일부가 아닌 mysqldump를한다. 원하는 SELECT 문을 생성 한 다음 끝에 CSV 스타일 출력에 대한 적절한 구성으로 "INTO OUTFILE '/path/to/outfile.csv'..."를 추가 할 수 있습니다. 그런 다음 ' LOAD DATA INFILE ...'구문 과 같은 것을 사용 하여 데이터를 새 스키마 위치에로드 할 수 있습니다.

예를 들어, SQL을 사용하여 :

select table1.id, table1.level, table2.name, table2.level 
       from table1 join table2 on table1.id = table2.table1_id 
       join table3 on table3.id = table2.table3_id
       where table3.name in ('fee', 'fi', 'fo', 'fum')
INTO OUTFILE '/tmp/fee-fi-fo-fum.csv'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
; 

대상 디스크 파티션에 사용 가능한 저장 공간이 충분해야합니다.


나는 데이터로드를 위해 이것을 좋아한다. 여전히 새 데이터베이스로 스키마를 가져와야하지만 다른 방법을 사용하면 쉽게 달성 할 수 있습니다.
Richard

일부 사람들은 기본 테이블을 원하지 않을 수도 있기 때문에 단일 CSV를 가져 오기만하면 조인 된 결과 만 얻을 수 있습니다. +1 !!!
RolandoMySQLDBA 1

@randy 귀하의 답변에 감사드립니다,하지만 쿼리 결과의 CSV 덤프에 관심이 없기 때문에 이것이 내 문제를 해결한다고 생각하지 않습니다. 내가 할 수있는 일은 데이터베이스 하위 집합을 덤프하여 다른 컴퓨터에 설치 한 다음 쿼리 자체를 재현 가능하고 동일한 데이터 세트와 관련하여 수정할 수 있도록하는 것입니다. 목표는 재현 가능한 연구 를 지원하는 계산 워크 플로입니다 .
David LeBauer

향후 독자는 David의 의견을 다시 읽습니다. Richard가 언급했듯이 관련된 테이블의 스키마 를 별도로 내 보내야 합니다. 이러한 스키마는 새 데이터베이스에 쉽게로드 할 수 있습니다. 그런 다음 randomx가 말했듯 Load Data Infile이 .csv를 새 데이터베이스에로드하는 데 사용 합니다. 이제 쿼리를 실행할 수 있습니다.
ToolmakerSteve

이 기술의 한계는 쿼리 출력이 원래 테이블과 동일한 조직에 있지 않다는 것입니다. 이 방법을 여전히 좋아하지만 원래 테이블 구조를 다시 만들려면 테이블 당 하나씩 별도의 쿼리를 실행하여 해당 테이블에 필요한 데이터를 내 보냅니다.
ToolmakerSteve

6

mysqldump 유틸리티에는 --tables 옵션 이있어 덤프 할 테이블을 지정할 수 있습니다. 테이블 목록을 지정할 수 있습니다.

나는 더 쉬운 (자동화 된) 방법을 모른다.


도움을 주셔서 감사하지만 필요한 테이블뿐만 아니라 각 테이블의 선택된 행만 내보내고 싶습니다. 스크립트를 자동화 할 수있는delete from table1 where id not in (.....); 한 가장 쉬운 방법이라면 덤프를 따르는 스크립트를 가질 수 있습니다 . 특정 도구가 존재할 필요는 없습니다.
David LeBauer

--tables가 더 간단하고 불필요한 데이터를 삭제하면 특히 관련 테이블이 각각 1GB 이상인 경우 새 서버에서 더 많은 작업이 필요하기 때문에 +1이 필요합니다. 대부분의 사람들은 단계적 측면에서 의미가 있기 때문에 그렇게하는 것이 더 편안합니다. 내 대답에는 약간의 계획과 약간의 위험이 따릅니다.
RolandoMySQLDBA


2

mysql 에서 quote 함수 를 사용해 보셨습니까 ?

SELECT CONCAT("insert into table4(id,level,name,levelt2) VALUES(",   quote(table1.id),   ",",    quote(table1.level),   ",",    quote(table2.name),   ",",    quote(table2.level),    ");") as q
       from table1 join table2 on table1.id = table2.table1_id 
       join table3 on table3.id = table2.table3_id
       where table3.name in ('fee', 'fi', 'fo', 'fum'); 

위의 내용을 query.sql로 저장하십시오.

cat query.sql|mysql --skip-column-names --raw > table4.sql

1

MySQL에서 :

SHOW CREATE TABLE table1; -- use these two create statements
SHOW CREATE TABLE table2; -- to design table4's create statement
CREATE TABLE table4( .... );
INSERT INTO table4(id,level,name,levelt2)
SELECT table1.id, table1.level, table2.name, table2.level 
   from table1 join table2 on table1.id = table2.table1_id 
   join table3 on table3.id = table2.table3_id
   where table3.name in ('fee', 'fi', 'fo', 'fum'); 

명령 행에서 :

mysqldump mydb table4 |gzip > table4.sql.gz

대상 서버에서 ~ / .my.cnf를 설정하십시오.

[client]
default-character-set=utf8

대상 서버에서 가져 오기

zcat table4.sql.gz | mysql

1

비슷한 문제에 대한 작은 스크립트를 작성했습니다. https://github.com/digitalist/mysql_slice

include ('queryDumper.php');


$exampleQuery="select * from information_schema.columns c1 
left join information_schema.columns c2 on 1=1 limit 1";

//define credentials
$exampleMysqli = new mysqli($host, $user, $password, $database);
$exampleResult=$exampleMysqli->query($exampleQuery);

//if  mysqlnd (native driver installed), otherwise use wrapper
$exampleData=fetchAll($exampleResult);
$exampleMeta=$exampleResult->fetch_fields();

/*
 * field content removal options
 * column name => function name in queryDumper.php, namespace QueryDumperHelpers
 * 
 * */

$forbiddenFields=array(
'password'=>'replacePassword', //change password -> md5("password")
'login'=>'replaceLogin', //change login vasya@mail.ru -> vasya@example.com
'comment'=>'sanitizeComment' //lorem ipsum or 
);


//get tables dump
$dump=(\queryDumper\dump($exampleData, $exampleMeta, $forbiddenFields));



$dropDatabase=true; //default false
$dropTable=true; //default false

$dbAndTablesCreationDump=\QueryDumperDatabaseAndTables\dump($exampleMysqli,$exampleMeta, $dropDatabase, $dropTable);

$databases=$dbAndTablesCreationDump['databases'];
$tables=$dbAndTablesCreationDump['tables'];
$eol=";\n\n";
echo implode($eol, $databases)."\n";
echo implode($eol, $tables).";\n";
echo "\n";

//consider using array_unique($dump) before imploding
echo implode("\n\n", $dump);
echo "\n";
?>

즉,이 쿼리가 있습니다 :

SELECT * FROM employees.employees e1 
LEFT JOIN employees.employees e2 ON 1=1 
LIMIT 1; 

이 덤프를 얻었습니다 .

DROP DATABASE `employees`;

CREATE DATABASE `employees`;
CREATE TABLE `employees` ( /* creation code */ ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT IGNORE INTO `employees`.`employees` VALUES ("10001","1953-09-02","Georgi","Facello","M","1986-06-26");

INSERT IGNORE INTO `employees`.`employees` VALUES ("10001","1953-09-02","Georgi","Facello","M","1986-06-26");
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.