Apakah mungkin untuk mysqldump bagian dari database yang diperlukan untuk mereproduksi kueri?

37

Latar Belakang

Saya ingin memberikan subset dari database saya yang diperlukan untuk mereproduksi selectkueri. Tujuan saya adalah membuat alur kerja komputasi saya dapat direproduksi (seperti dalam penelitian yang dapat direproduksi ).

Pertanyaan

Apakah ada cara saya bisa memasukkan pernyataan pilih ini ke dalam skrip yang membuang data yang diminta ke dalam database baru, sehingga database tersebut dapat diinstal pada server mysql baru, dan pernyataan itu akan bekerja dengan database baru. Basis data baru tidak boleh berisi catatan selain dari yang telah digunakan dalam kueri.

Pembaruan: Untuk klarifikasi, saya tidak tertarik dengan hasil csv dump. Apa yang saya harus dapat lakukan adalah membuang subset database sehingga dapat diinstal pada komputer lain, dan kemudian permintaan itu sendiri dapat direproduksi (dan dapat dimodifikasi sehubungan dengan dataset yang sama).

Contoh

Misalnya, analisis saya mungkin meminta sebagian data yang membutuhkan rekaman dari beberapa tabel (dalam contoh ini 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'); 
David LeBauer
sumber
OK, jadi tidak ada catatan tambahan. Apakah Anda hanya menginginkan kolom yang ditentukan oleh kueri?
Richard
@ Richard aku tidak mempertimbangkan itu - akan menyenangkan mengetahui bagaimana melakukan ini.
David LeBauer
3
Ini adalah pertanyaan yang sangat unik yang saya yakin sebagian orang bertanya-tanya dan perlu dijawab. +1 untuk membawa pertanyaan jenis ini ke publik.
RolandoMySQLDBA
Pembaca masa depan: Selain jawaban yang diterima, lihat jawaban randomx , yang secara spesifik membuang data yang dibutuhkan oleh kueri.
ToolmakerSteve

Jawaban:

52

mysqldump memiliki opsi --where untuk mengeksekusi klausa WHERE untuk tabel yang diberikan.

Meskipun tidak mungkin untuk mysqldump permintaan bergabung, Anda dapat mengekspor baris tertentu dari setiap tabel sehingga setiap baris yang diambil dari setiap tabel akan terlibat dalam bergabung nanti.

Untuk permintaan yang Anda berikan, Anda harus melakukan mysqldump tiga kali:

Pertama, mysqldump semua baris table3 dengan nama dalam ('fee', 'fi', 'fo', 'fum'):

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

Selanjutnya, mysqldump semua baris table2 yang memiliki nilai table3_id yang cocok dari mysqldump pertama:

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

Kemudian, mysqldump semua baris table1 yang memiliki nilai table1_id yang cocok dari mysqldump kedua:

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

Catatan: Karena mysqldumps kedua dan ketiga membutuhkan lebih dari satu tabel, --lock-all-tables harus digunakan .

Buat database baru Anda:

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

Akhirnya, muat ketiga mysqldumps ke database lain dan coba gabung di sana di database baru.

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

Di klien mysql, jalankan kueri bergabung Anda

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

Cobalah !!!

PERINGATAN: Jika tidak diindeks dengan benar, mysqldumps kedua dan ketiga mungkin berlangsung selamanya !!!

Untuk berjaga-jaga, indeks kolom berikut:

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

Saya akan menganggap id adalah kunci utama dari table3.

RolandoMySQLDBA
sumber
1
terima kasih untuk contoh terperinci! Saya melewatkan --whereklausa dalam dokumentasi; akan memberi tahu Anda cara kerjanya setelah saya mendapat kesempatan untuk mencobanya.
David LeBauer
1
+1 Saya suka ini lebih baik daripada metode --tabel untuk masalah ini. Secara umum, saya akan berakhir menggunakan --tabel, tetapi --di mana adalah pilihan yang sangat bagus.
Richard
Ketika Anda mysqldump tabel tunggal, --lock-all-tables tidak digunakan. Karena klausa yang melibatkan tabel selain yang sedang dibuang, Anda harus memberi tahu mysqldump --lock-all-tables. Opsi --lock-all-tables aktif untuk membuang satu atau lebih basis data, BUKAN UNTUK TABEL TUNGGAL. Saya mencoba melakukan mysqldumps ke-2 dan ke-3 tetapi mengeluhkan hal ini. Setelah saya mengeluarkan --lock-all-tables secara manual, kesalahan hilang dan mysqldump berhasil. Juga, tolong perhatikan mysqldump pertama dalam jawaban saya tidak memiliki --lock-all-tables.
RolandoMySQLDBA
@Rolando terima kasih atas bantuan Anda. Ini bekerja dengan sempurna
David LeBauer
@Rolando maaf, saya tidak melihat bahwa Anda telah menjawab komentar / pertanyaan saya sebelum saya menghapusnya. Saya mendapatkan kesalahan yang sama. Setelah membaca ulang manual, saya melihat --lock-tables hanya mengunci tabel yang dibuang. Saya bingung karena --lock-all-tables mengunci semua tabel di semua basis data, yang tidak perlu bila hanya menggunakan basis data tunggal.
David LeBauer
7

Saya akan mempertimbangkan menggunakan 'outfile' sebagai bagian dari SELECT Anda dan bukan mysqldump untuk menyelesaikan masalah ini. Anda dapat menghasilkan pernyataan SELECT apa pun yang Anda inginkan, kemudian menambahkan "INTO OUTFILE '/path/to/outfile.csv' ..." di akhir dengan konfigurasi yang sesuai untuk output gaya CSV. Maka Anda cukup menggunakan sintaks ' LOAD DATA INFILE ...' untuk memuat data ke lokasi skema baru Anda.

Misalnya, menggunakan SQL Anda:

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

Ingatlah bahwa Anda akan membutuhkan ruang penyimpanan yang cukup di partisi disk target.

randomx
sumber
Saya suka ini untuk dataload. Anda masih perlu memindahkan skema ke database baru, tetapi itu mudah dicapai dengan menggunakan beberapa trik lain.
Richard
Saya suka ini juga karena beberapa orang mungkin tidak ingin tabel dasar, hanya hasil gabungan sebagai CSV tunggal yang diimpor. +1 !!!
RolandoMySQLDBA
@randy Terima kasih atas jawaban Anda, tetapi saya tidak berpikir ini menyelesaikan masalah saya karena saya tidak tertarik dengan csv dump hasil pencarian. Apa yang saya harus dapat lakukan adalah membuang subset database sehingga dapat diinstal pada komputer lain, dan kemudian permintaan itu sendiri dapat direproduksi (dan dapat dimodifikasi sehubungan dengan dataset yang sama). Tujuannya adalah alur kerja komputasi yang mendukung penelitian yang dapat direproduksi .
David LeBauer
Untuk pembaca di masa depan komentar David: seperti yang disebutkan Richard, Anda perlu secara terpisah mengekspor skema tabel yang terlibat. Skema tersebut dapat dengan mudah dimuat ke dalam basis data baru. Kemudian, seperti kata randomx, Anda menggunakan Load Data Infileuntuk memuat .csv ke dalam basis data baru itu. Sekarang, kueri dapat dieksekusi.
ToolmakerSteve
Saya baru menyadari bahwa keterbatasan teknik ini, adalah bahwa output query tidak dalam organisasi yang sama dengan tabel aslinya. Sementara saya masih suka pendekatan ini, untuk membuat ulang struktur tabel asli: Jalankan kueri terpisah, satu per tabel, untuk mengekspor data yang diperlukan untuk tabel itu.
ToolmakerSteve
6

Utilitas mysqldump memiliki opsi --tables yang memungkinkan Anda menentukan tabel mana yang akan dibuang. Ini memungkinkan Anda menentukan daftar tabel.

Saya tidak tahu cara yang lebih mudah (otomatis).

Richard
sumber
terima kasih atas bantuan Anda, tetapi saya hanya ingin mengekspor baris yang dipilih dari setiap tabel, bukan hanya tabel yang diperlukan. Saya dapat memiliki skrip yang mengikuti dump dengan delete from table1 where id not in (.....);, jika itu adalah cara termudah, selama skrip dapat diotomatisasi, tidak perlu alat khusus itu ada.
David LeBauer
Anda berhak mendapatkan +1 karena --tabel akan lebih sederhana dan menjatuhkan data yang tidak dibutuhkan akan lebih banyak kerja keras di server baru, terutama jika tabel yang terlibat masing-masing lebih dari 1GB. Kebanyakan orang akan merasakan tingkat kenyamanan yang lebih besar melakukannya dengan cara itu karena itu hanya masuk akal dalam hal langkah-langkahnya. Jawaban saya hanya membutuhkan sedikit perencanaan dan sedikit lebih banyak risiko.
RolandoMySQLDBA
3

Apa yang bermanfaat bagi saya adalah sesuatu seperti:

mysqldump -u db_user -p db_name table_name --no_create_info \
--lock-all-tables --where 'id in (SELECT tn.id FROM table_name AS tn \
JOIN related_table AS rt ON tn.related_table_id = rt.id \
WHERE rt.some_field = 1)' > data.sql

Dari http://krosinski.blogspot.com/2012/12/using-table-join-with-mysqldump.html

Ryan
sumber
2

Sudahkah Anda mencoba fungsi penawaran di mysql?

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

simpan di atas, sebagai query.sql

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

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

Di Baris Perintah:

mysqldump mydb table4 |gzip > table4.sql.gz

Di server tujuan Anda, setup ~ / .my.cnf

[client]
default-character-set=utf8

Impor di server tujuan

zcat table4.sql.gz | mysql
velcrow
sumber
1

saya menulis skrip kecil untuk masalah serupa, ini dia: 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";
?>

yaitu Anda memiliki permintaan ini :

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

Anda mendapatkan dump ini :

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");
digitalis
sumber