Est-il possible de mysqldump un sous-ensemble d'une base de données nécessaire pour reproduire une requête?


37

Contexte

Je voudrais fournir le sous-ensemble de ma base de données nécessaire pour reproduire une selectrequête. Mon objectif est de rendre mon flux de travail informatique reproductible (comme dans une recherche reproductible ).

Question

Est-il possible d'incorporer cette instruction select dans un script qui exporte les données demandées dans une nouvelle base de données, de telle sorte que la base de données puisse être installée sur un nouveau serveur mysql et que l'instruction fonctionne avec la nouvelle base de données? La nouvelle base de données ne doit pas contenir d’enregistrements en plus de ceux utilisés dans la requête.

Mise à jour: pour plus de précision, je ne suis pas intéressé par un vidage CSV des résultats de la requête. Ce que je dois être capable de faire est de vider le sous-ensemble de base de données afin qu'il puisse être installé sur une autre machine, puis la requête elle-même peut être reproductible (et modifiable par rapport au même ensemble de données).

Exemple

Par exemple, mon analyse peut interroger un sous-ensemble de données nécessitant des enregistrements de plusieurs tables (dans cet exemple 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, donc pas d'enregistrements supplémentaires. Souhaitez-vous uniquement les colonnes spécifiées par la requête?
Richard

@ Richard je n'avais pas envisagé cela - ce serait bien de savoir comment faire cela.
David LeBauer

3
C’est une question tout à fait unique que je suis sûr que certains se sont demandé et qu’il fallait y répondre. +1 pour avoir porté ce type de question au public.
RolandoMySQLDBA

Futurs lecteurs: en plus de la réponse acceptée, voir la réponse de randomx , qui vide spécifiquement les données nécessaires à la requête.
ToolmakerSteve

Réponses:


52

mysqldump a l' option --where pour exécuter une clause WHERE pour une table donnée.

Bien qu'il ne soit pas possible de mysqldump une requête de jointure, vous pouvez exporter des lignes spécifiques de chaque table afin que chaque ligne extraite de chaque table soit impliquée ultérieurement dans la jointure.

Pour votre requête donnée, vous auriez besoin de mysqldump trois fois:

D'abord, mysqldump toutes les lignes de la table 3 avec le nom dans ('fee', 'fi', 'fo', 'fum'):

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

Ensuite, mysqldump toutes les lignes de la table2 ayant les valeurs table3_id correspondantes du premier 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

Ensuite, mysqldump toutes les lignes de la table 1 ayant les valeurs table1_id correspondantes dans le second 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

Note: Comme les deuxième et troisième mysqldumps nécessitent l'utilisation de plus d'une table, l'option --lock-all-tables doit être utilisée .

Créez votre nouvelle base de données:

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

Enfin, chargez les trois mysqldumps dans une autre base de données et tentez la jointure dans la nouvelle base de données.

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

Dans le client mysql, lancez votre requête de jointure

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

Essaie !!!

AVERTISSEMENT: S'ils ne sont pas indexés correctement, les deuxième et troisième mysqldumps peuvent prendre une éternité !!!

Juste au cas où, indexez les colonnes suivantes:

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

Je suppose que id est la clé primaire de table3.


1
merci pour l'exemple détaillé! J'ai raté la --whereclause dans la documentation; vous permettra de savoir comment cela fonctionne après avoir eu la chance de l'essayer.
David LeBauer

1
+1 J'aime cela mieux que la méthode --tables pour ce problème. En général, je finirais par utiliser --tables, mais le --où est une très bonne option.
Richard

Lorsque vous mysqldump une seule table, l'option --lock-all-tables n'est pas utilisée. Etant donné que la clause where implique des tables autres que celle en cours de vidage, vous devez dire à mysqldump --lock-all-tables. L'option --lock-all-tables est active pour vider une ou plusieurs bases de données, PAS POUR UNE SEULE TABLE. J'ai essayé d'exécuter les 2ème et 3ème mysqldumps mais il s'en est plaint. Une fois que j'ai publié manuellement --lock-all-tables, l'erreur a disparu et le mysqldump a réussi. Notez également que le premier mysqldump dans ma réponse n'a pas --lock-all-tables.
RolandoMySQLDBA

@Rolando merci pour votre aide. Cela a fonctionné à la perfection
David LeBauer le

@Rolando désolé, je n'ai pas remarqué que vous aviez répondu à mon commentaire / question avant de le supprimer. J'avais la même erreur. Après avoir relu le manuel, je vois que --lock-tables ne verrouille que les tables en cours de vidage. J'étais confus, car --lock-all-tables verrouille toutes les tables de toutes les bases de données, ce qui n'est pas nécessaire si vous utilisez une seule base de données.
David LeBauer

7

Je considérerais d’ utiliser un fichier 'outfile' dans votre SELECT au lieu de mysqldump pour résoudre ce problème. Vous pouvez produire l’instruction SELECT de votre choix, puis ajouter "INTO OUTFILE" /path/to/outfile.csv "..." à la fin avec la configuration appropriée pour la sortie de style CSV. Ensuite, vous pouvez simplement utiliser quelque chose comme la syntaxe ' LOAD DATA INFILE ...' pour charger les données dans votre nouvel emplacement de schéma.

Par exemple, en utilisant votre 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'
; 

N'oubliez pas que vous aurez besoin de suffisamment d'espace de stockage disponible sur la partition de disque cible.


J'aime ça pour le chargement de données. Vous aurez toujours besoin de transférer le schéma dans la nouvelle base de données, mais cela est facilement réalisable en utilisant d'autres astuces.
Richard

J'aime cela aussi car certaines personnes peuvent ne pas vouloir les tables de base, mais simplement le résultat joint en tant que fichier CSV importé. +1 !!!
RolandoMySQLDBA

@ Merci beaucoup pour votre réponse, mais je ne pense pas que cela résout mon problème car je ne suis pas intéressé par un vidage CSV des résultats de la requête. Ce que je dois être capable de faire est de vider le sous-ensemble de base de données afin qu'il puisse être installé sur une autre machine, puis la requête elle-même peut être reproductible (et modifiable par rapport au même ensemble de données). L'objectif est un flux de travail informatique qui prend en charge la recherche reproductible .
David LeBauer

Pour les futurs lecteurs, le commentaire de David: comme l'a mentionné Richard, vous devez exporter séparément le schéma des tables concernées. Ces schémas peuvent facilement être chargés dans une nouvelle base de données. Ensuite, comme le dit randomx, vous utilisez Load Data Infilepour charger ce fichier .csv dans cette nouvelle base de données. Maintenant, la requête peut être exécutée.
ToolmakerSteve

Je viens de me rendre compte que la limitation de cette technique réside dans le fait que la sortie de la requête ne se trouve pas dans la même organisation que les tables d'origine. Même si j'aime toujours cette approche, pour recréer la structure de table d'origine: Exécutez des requêtes distinctes, une par table, pour exporter les données nécessaires à cette table.
ToolmakerSteve

6

Mysqldump util a une option --tables qui vous permet de spécifier les tables à vider. Il vous permet de spécifier la liste des tables.

Je ne connais pas de manière plus simple (automatisée).


merci pour votre aide, mais je veux seulement exporter les lignes sélectionnées de chaque table, pas seulement les tables requises. Je pourrais avoir un script qui suit le vidage avec delete from table1 where id not in (.....);, si c'est le moyen le plus simple, tant que le script peut être automatisé, il n'est pas nécessaire que l'outil spécifique existe.
David LeBauer

Vous méritez un +1 car --tables serait plus simple et supprimer les données inutiles constituerait un travail supplémentaire compliqué sur le nouveau serveur, en particulier si les tables concernées dépassent 1 Go chacune. La plupart des gens se sentiraient plus à l'aise en procédant de la sorte parce que c'est logique en ce qui concerne les étapes. Ma réponse nécessite juste un peu de planification et un peu plus de risque.
RolandoMySQLDBA


2

Avez-vous essayé la fonction de citation dans 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'); 

enregistrer ce qui précède, en tant que query.sql

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

1

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

En ligne de commande:

mysqldump mydb table4 |gzip > table4.sql.gz

Sur votre serveur de destination, configurez ~ / .my.cnf

[client]
default-character-set=utf8

Import sur le serveur de destination

zcat table4.sql.gz | mysql

1

J'ai écrit un petit script pour un problème similaire, le voici: 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";
?>

c'est à dire que vous avez cette requête :

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

vous avez ce dump :

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");
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.