クエリを再現するために必要なデータベースのサブセットを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'); 

OK、追加のレコードはありません。クエリで指定された列のみが必要ですか?
リチャード

@リチャード私はそれを考慮していませんでした-これを行う方法を知ってうれしいです。
デビッドルバウアー

3
これは非常にユニークな質問であり、一部の人は疑問に思っており、回答する必要があると確信しています。このタイプの質問を公開するための+1。
-RolandoMySQLDBA

将来の読者:受け入れられた答えに加えて、特にクエリに必要なデータをダンプするrandomxの答えを参照してください。
ToolmakerSteve

回答:


52

mysqldumpには、特定のテーブルに対してWHERE句を実行する--whereオプションがあります。

結合クエリをmysqldumpすることはできませんが、各テーブルからフェッチされたすべての行が後で結合に関与するように、各テーブルから特定の行をエクスポートできます。

指定したクエリに対して、mysqldumpを3回実行する必要があります。

最初に、名前が( 'fee'、 'fi'、 'fo'、 'fum')のすべてのtable3行をmysqldumpします。

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

次に、最初のmysqldumpのtable3_id値が一致するすべてのtable2行をmysqldumpします。

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

次に、2番目のmysqldumpから一致するtable1_id値を持つすべてのtable1行をmysqldumpします。

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

注:2番目と3番目のmysqldumpでは複数のテーブルを使用する必要があるため、--lock-all-tablesを使用する必要があります

新しいデータベースを作成します。

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

最後に、3つのmysqldumpsを別のデータベースにロードし、新しいデータベースでそこで結合を試みます。

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

試してみる !!!

警告:正しくインデックス付けされていない場合、2番目と3番目のmysqldumpが永遠にかかることがあります!!!

念のため、次の列にインデックスを付けます。

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ドキュメントの条項を見逃しました。私はそれを試してみる機会を得た後、これがどのように機能するかをお知らせします。
デビッドルバウアー

1
+1この問題の--tablesメソッドよりもこれが好きです。一般的に、--tablesを使用することになりますが、--whereは非常に良いオプションです。
リチャード

単一のテーブルをmysqldumpする場合、-lock-all-tablesは使用されません。where句にはダンプされるテーブル以外のテーブルが含まれるため、mysqldump --lock-all-tablesに通知する必要があります。--lock-all-tablesオプションは、1つのテーブルではなく、1つ以上のデータベースをダンプするためにアクティブです。2番目と3番目のmysqldumpを実行しようとしましたが、これについて不満がありました。--lock-all-tablesを手動で発行すると、エラーはなくなり、mysqldumpは成功しました。また、私の答えの最初のmysqldumpに--lock-all-tablesがないことに注意してください。
RolandoMySQLDBA

@Rolando、ご協力ありがとうございます。これは完璧に機能しました
デビッドルバウアー

@Rolando申し訳ありませんが、削除する前にあなたが私のコメント/質問に答えていたことに気付きませんでした。同じエラーが発生していました。マニュアルを読み直した後、--lock- tablesはダンプされるテーブルのみをロックします。--lock-all-tablesがすべてのデータベースのすべてのテーブルをロックするため、混乱しました。これは、単一のデータベースのみを使用する場合は必要ありません。
デビッドルバウアー

7

この問題を解決するには、mysqldumpの代わりにSELECTの一部として「outfile」を使用することを検討ます。必要なSELECTステートメントを作成し、最後に「INTO OUTFILE '/path/to/outfile.csv' ...」を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'
; 

ターゲットディスクパーティションに十分な空き容量が必要になることに注意してください。


私はこれをデータロードで気に入っています。スキーマを新しいデータベースに引き継ぐ必要がありますが、他のトリックを使用して簡単に実現できます。
リチャード

一部の人々はベーステーブルを望んでいないかもしれないので、私もこれが好きです、ただインポートされた単一のCSVとして結合された結果。+1 !!!
-RolandoMySQLDBA

@randyご回答いただきありがとうございますが、クエリ結果のcsvダンプに興味がないので、これが私の問題を解決するとは思いません。私ができるようにする必要があるのは、データベースのサブセットをダンプして、別のマシンにインストールできるようにすることです。そうすれば、クエリ自体を再現可能(および同じデータセットに関して変更可能)にできます。目標は、再現可能な研究をサポートする計算ワークフローです。
デビッドルバウアー

今後の読者のために、Davidのコメントを再度ご覧ください。Richardが述べたように、関連するテーブルのスキーマを個別にエクスポートする必要があります。これらのスキーマは、新しいデータベースに簡単にロードできます。次に、randomxが述べたように、Load Data Infileその.csvを新しいデータベースにロードするために使用します。これで、クエリを実行できます。
ToolmakerSteve

この手法の制限は、クエリ出力が元のテーブルと同じ組織にないことであることに気付きました。私はまだこのアプローチが好きですが、元のテーブル構造を再作成するには:テーブルごとに1つのクエリを個別に実行して、そのテーブルに必要なデータをエクスポートします。
ToolmakerSteve

6

mysqldump utilには、ダンプするテーブルを指定できる--tablesオプションがあります。テーブルのリストを指定できます。

簡単な(自動化された)方法がわかりません。


ご協力いただきありがとうございますが、必要なテーブルだけでなく、各テーブルの選択した行のみをエクスポートします。スクリプトを自動化できるdelete from table1 where id not in (.....);限り、特定のツールが存在する必要はありませんが、それが最も簡単な方法である場合、ダンプの後に続くスクリプトを持つことができます
デビッドルバウアー

--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.