È possibile mysqldump un sottoinsieme di un database richiesto per riprodurre una query?


37

sfondo

Vorrei fornire il sottoinsieme del mio database necessario per riprodurre una selectquery. Il mio obiettivo è rendere riproducibile il mio flusso di lavoro computazionale (come nella ricerca riproducibile ).

Domanda

C'è un modo in cui posso incorporare questa istruzione select in uno script che scarica i dati richiesti in un nuovo database, in modo tale che il database possa essere installato su un nuovo server mysql e che l'istruzione funzioni con il nuovo database. Il nuovo database non deve contenere record oltre a quelli utilizzati nella query.

Aggiornamento: per chiarimenti, non sono interessato a un dump CSV dei risultati della query. Quello che devo essere in grado di fare è scaricare il sottoinsieme del database in modo che possa essere installato su un altro computer e quindi la query stessa può essere riproducibile (e modificabile rispetto allo stesso set di dati).

Esempio

Ad esempio, la mia analisi potrebbe interrogare un sottoinsieme di dati che richiede record da più (in questo esempio 3) tabelle:

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, quindi nessun record aggiuntivo. Desideri solo le colonne specificate dalla query?
Richard,

@Richard Non l'avevo considerato - sarebbe bello sapere come fare.
David LeBauer,

3
Questa è una domanda davvero unica che sono sicuro che alcuni si sono chiesti e alla quale bisognava rispondere. +1 per aver reso pubblica questa domanda di tipo.
RolandoMySQLDBA,

Lettori futuri: oltre alla risposta accettata, vedere la risposta di randomx , che scarica in modo specifico i dati necessari alla query.
ToolmakerSteve

Risposte:


52

mysqldump ha l' opzione --where per eseguire una clausola WHERE per una determinata tabella.

Sebbene non sia possibile eseguire il mysqldump di una query di join, è possibile esportare righe specifiche da ciascuna tabella in modo che ogni riga recuperata da ciascuna tabella venga coinvolta nel join in un secondo momento.

Per la tua specifica query, dovrai eseguire tre volte mysqldump:

Innanzitutto, mysqldump tutte le righe della tabella 3 con nome in ('fee', 'fi', 'fo', 'fum'):

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

Quindi, mysqldump tutte le righe table2 che hanno valori table3_id corrispondenti dal primo 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

Quindi, mysqldump tutte le righe table1 che hanno valori table1_id corrispondenti dal secondo 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

Nota: poiché il secondo e il terzo mysqldumps richiedono l'uso di più di una tabella, è necessario utilizzare --lock-all-tables .

Crea il tuo nuovo database:

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

Infine, carica i tre mysqldumps in un altro database e prova a unirti nel nuovo database.

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

Nel client mysql, esegui la query di join

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

Provaci !!!

ATTENZIONE: se non indicizzato correttamente, il secondo e il terzo mysqldumps potrebbero richiedere per sempre !!!

Per ogni evenienza, indicizza le seguenti colonne:

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

Presumo che id sia la chiave primaria di table3.


1
grazie per l'esempio dettagliato! Ho perso la --whereclausola nella documentazione; ti farò sapere come funziona dopo che avrò la possibilità di provarlo.
David LeBauer,

1
+1 Mi piace meglio del metodo --tables per questo problema. In generale, finirei per usare --tables, ma --where è un'opzione molto bella.
Richard,

Quando mysqldump una singola tabella, --lock-all-tables non viene utilizzato. Dato che la clausola where riguardava tabelle diverse da quella oggetto di dumping, devi dire a mysqldump --lock-all-tables. L'opzione --lock-all-tables è attiva per il dumping di uno o più database, NON PER UNA SINGOLA TABELLA. Ho provato a eseguire il 2o e 3o mysqldumps ma mi sono lamentato di questo. Dopo aver emesso manualmente --lock-all-tables, l'errore è scomparso e il mysqldump ha avuto successo. Inoltre, tieni presente che il primo mysqldump nella mia risposta non ha --lock-all-tables.
RolandoMySQLDBA

@Rolando grazie per il tuo aiuto.
Funzionava

@Rolando mi dispiace, non ho notato che avevi risposto al mio commento / domanda prima di cancellarlo. Stavo ottenendo lo stesso errore. Dopo aver riletto il manuale, vedo --lock-tables blocca solo il dumping delle tabelle. Ero confuso perché --lock-all-tables blocca tutte le tabelle in tutti i database, il che non è necessario quando si utilizza solo un singolo database.
David LeBauer,

7

Vorrei prendere in considerazione l' utilizzo di un "file outfile" come parte di SELECT anziché mysqldump per risolvere questo problema. È possibile produrre qualsiasi istruzione SELECT desiderata, quindi aggiungere "INTO OUTFILE '/path/to/outfile.csv' ..." alla fine con la configurazione appropriata per l'output in stile CSV. Quindi puoi semplicemente usare qualcosa come la sintassi ' LOAD DATA INFILE ...' per caricare i dati nella nuova posizione dello schema.

Ad esempio, usando il tuo 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'
; 

Tieni presente che avrai bisogno di spazio di archiviazione sufficiente sulla partizione del disco di destinazione.


Mi piace questo per il dataload. Sarà comunque necessario riportare lo schema nel nuovo database, ma ciò è facilmente realizzabile con alcuni altri trucchi.
Richard,

Mi piace anche questo perché alcune persone potrebbero non volere le tabelle di base, ma solo il risultato unito come un singolo CSV importato. +1 !!!
RolandoMySQLDBA

@randy Grazie per la tua risposta, ma non credo che questo risolva il mio problema perché non mi interessa un dump CSV dei risultati della query. Quello che devo essere in grado di fare è scaricare il sottoinsieme del database in modo che possa essere installato su un altro computer e quindi la query stessa può essere riproducibile (e modificabile rispetto allo stesso set di dati). L'obiettivo è un flusso di lavoro computazionale che supporti la ricerca riproducibile .
David LeBauer,

Per i futuri lettori il commento di David: come ha detto Richard, è necessario esportare separatamente lo schema delle tabelle coinvolte. Tali schemi possono essere facilmente caricati in un nuovo database. Quindi, come diceva randomx, si usa Load Data Infileper caricare quel .csv in quel nuovo database. Ora, la query può essere eseguita.
ToolmakerSteve

Ho appena capito che il limite di questa tecnica è che l'output della query non è nella stessa organizzazione delle tabelle originali. Mentre mi piace ancora questo approccio, per ricreare la struttura originale della tabella: esegui query separate, una per tabella, per esportare i dati necessari per quella tabella.
ToolmakerSteve

6

L' utilità mysqldump ha un'opzione --tables che ti consente di specificare quali tabelle scaricare. Ti consente di specificare l'elenco delle tabelle.

Non conosco alcun modo più semplice (automatizzato).


grazie per il vostro aiuto, ma voglio solo esportare le righe selezionate di ogni tabella, non solo le tabelle richieste. Potrei avere uno script che segue il dump con delete from table1 where id not in (.....);, se è il modo più semplice, purché lo script possa essere automatizzato, non è necessario che esista lo strumento specifico.
David LeBauer,

Ti meriti un +1 perché --tables sarebbe più semplice e far cadere i dati non necessari sarebbe solo più lavoro a cavallo nel nuovo server, specialmente se le tabelle coinvolte sono superiori a 1 GB ciascuna. La maggior parte delle persone proverebbe un maggior livello di conforto in questo modo perché ha senso solo in termini di passaggi. La mia risposta richiede solo un po 'di pianificazione e un po' più di rischio.
RolandoMySQLDBA,


2

Hai provato la funzione di preventivo in 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'); 

salvare quanto sopra, come query.sql

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

1

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

Sulla riga di comando:

mysqldump mydb table4 |gzip > table4.sql.gz

Sul tuo server di destinazione, imposta ~ / .my.cnf

[client]
default-character-set=utf8

Importa sul server di destinazione

zcat table4.sql.gz | mysql

1

ho scritto una piccola sceneggiatura per un problema simile, eccola qui: 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";
?>

cioè hai questa domanda :

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

hai questa discarica :

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");
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.