¿Es posible mysqldump un subconjunto de una base de datos requerida para reproducir una consulta?


37

Fondo

Me gustaría proporcionar el subconjunto de mi base de datos requerido para reproducir una selectconsulta. Mi objetivo es hacer que mi flujo de trabajo computacional sea reproducible (como en la investigación reproducible ).

Pregunta

¿Hay alguna manera de que pueda incorporar esta declaración de selección en un script que vuelca los datos consultados en una nueva base de datos, de modo que la base de datos se pueda instalar en un nuevo servidor mysql, y la declaración funcionaría con la nueva base de datos? La nueva base de datos no debe contener registros además de los que se han utilizado en la consulta.

Actualización: para aclarar, no estoy interesado en un volcado csv de resultados de consultas. Lo que necesito hacer es volcar el subconjunto de la base de datos para que pueda instalarse en otra máquina, y luego la consulta en sí puede ser reproducible (y modificable con respecto al mismo conjunto de datos).

Ejemplo

Por ejemplo, mi análisis podría consultar un subconjunto de datos que requiere registros de varias tablas (en este ejemplo 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, entonces no hay registros adicionales. ¿Desea solo las columnas especificadas por la consulta?
Richard

@ Richard No lo había considerado, sería bueno saber cómo hacer esto.
David LeBauer

3
Esta es una pregunta muy singular que estoy seguro de que algunos se han preguntado y deben ser respondidos. +1 por hacer pública esta pregunta de tipo.
RolandoMySQLDBA

Lectores futuros: además de la respuesta aceptada, vea la respuesta de randomx , que específicamente volca los datos necesarios para la consulta.
ToolmakerSteve

Respuestas:


52

mysqldump tiene la --donde opción de ejecutar una cláusula WHERE de una tabla dada.

Aunque no es posible mysqldump una consulta de unión, puede exportar filas específicas de cada tabla para que cada fila obtenida de cada tabla participe en la unión más adelante.

Para su consulta dada, necesitaría mysqldump tres veces:

Primero, mysqldump todas las filas de table3 con nombre en ('tarifa', 'fi', 'fo', 'fum'):

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

A continuación, mysqldump todas las filas de table2 que tienen valores de table3_id coincidentes del primer 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

Luego, mysqldump todas las filas table1 que tienen valores table1_id coincidentes del segundo 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: Dado que el segundo y tercer mysqldumps requieren el uso de más de una tabla, se debe usar --lock-all-tables .

Crea tu nueva base de datos:

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

Finalmente, cargue los tres mysqldumps en otra base de datos e intente unirse allí en la nueva base de datos.

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

En el cliente mysql, ejecute su consulta de unión

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

Darle una oportunidad !!!

ADVERTENCIA: ¡Si no se indexa correctamente, el segundo y tercer mysqldumps pueden durar para siempre!

Por si acaso, indexe las siguientes columnas:

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

Asumiré que id es la clave principal de table3.


1
gracias por el ejemplo detallado! Me perdí la --wherecláusula en la documentación; te haré saber cómo funciona esto después de que tenga la oportunidad de probarlo.
David LeBauer

1
+1 Me gusta más que el método --tables para este problema. En general, terminaría usando --tables, pero --where es una muy buena opción.
Richard

Cuando mysqldump una sola tabla, --lock-all-tables no se usa. Debido a que la cláusula where involucra tablas diferentes a la que se está volcando, debe decirle a mysqldump --lock-all-tables. La opción --lock-all-tables está activa para volcar una o más bases de datos, NO PARA UNA SOLA TABLA. Traté de realizar el segundo y tercer mysqldumps pero se quejó de esto. Una vez que emití manualmente --lock-all-tables, el error desapareció y mysqldump fue exitoso. Además, tenga en cuenta que el primer mysqldump en mi respuesta no tiene --lock-all-tables.
RolandoMySQLDBA

@Rolando gracias por tu ayuda. Esto funcionó perfectamente
David LeBauer

@Rolando lo siento, no me di cuenta de que había respondido mi comentario / pregunta antes de eliminarlo. Estaba recibiendo el mismo error. Después de volver a leer el manual, veo --lock-tables solo bloquea las tablas que se están volcando. Estaba confundido porque --lock-all-tables bloquea todas las tablas en todas las bases de datos, lo cual no es necesario cuando solo se usa una sola base de datos.
David LeBauer

7

Consideraría usar un 'outfile' como parte de su SELECT en lugar de mysqldump para resolver este problema. Puede producir cualquier instrucción SELECT que desee y luego agregar "INTO OUTFILE '/path/to/outfile.csv' ..." al final con la configuración adecuada para la salida de estilo CSV. Luego, simplemente puede usar algo como la sintaxis ' CARGAR DATOS DE ARCHIVO ...' para cargar los datos en su nueva ubicación de esquema.

Por ejemplo, usando tu 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'
; 

Tenga en cuenta que necesitará suficiente espacio de almacenamiento disponible en la partición del disco de destino.


Me gusta esto para la carga de datos. Aún necesitará pasar el esquema a la nueva base de datos, pero eso se logra fácilmente con otros trucos.
Richard

También me gusta porque algunas personas pueden no querer las tablas base, solo el resultado combinado como un solo CSV importado. +1 !!!
RolandoMySQLDBA

@randy Gracias por su respuesta, pero no creo que esto resuelva mi problema porque no estoy interesado en un volcado csv de resultados de consultas. Lo que necesito hacer es volcar el subconjunto de la base de datos para que pueda instalarse en otra máquina, y luego la consulta en sí puede ser reproducible (y modificable con respecto al mismo conjunto de datos). El objetivo es un flujo de trabajo computacional que respalde la investigación reproducible .
David LeBauer

Para futuros lectores, re el comentario de David: como Richard mencionó, necesita exportar por separado el esquema de las tablas involucradas. Esos esquemas se pueden cargar fácilmente en una nueva base de datos. Luego, como dijo randomx, se usa Load Data Infilepara cargar ese .csv en esa nueva base de datos. Ahora, la consulta se puede ejecutar.
ToolmakerSteve

Me acabo de dar cuenta de que la limitación de esta técnica es que el resultado de la consulta no está en la misma organización que las tablas originales. Si bien todavía me gusta este enfoque, para recrear la estructura de la tabla original: ejecute consultas separadas, una por tabla, para exportar los datos necesarios para esa tabla.
ToolmakerSteve

6

La utilidad mysqldump tiene una opción --tables que le permite especificar qué tablas volcar. Le permite especificar la lista de tablas.

No conozco ninguna forma más fácil (automatizada).


Gracias por su ayuda, pero solo quiero exportar las filas seleccionadas de cada tabla, no solo las tablas requeridas. Podría tener un script que siga el volcado delete from table1 where id not in (.....);, si esa es la forma más fácil, siempre que el script pueda automatizarse, no es necesario que exista la herramienta específica.
David LeBauer

Te mereces un +1 porque - las tablas serían más simples y eliminar los datos innecesarios sería más trabajo en el nuevo servidor, especialmente si las tablas involucradas tienen más de 1 GB cada una. La mayoría de las personas sentirían un mayor nivel de comodidad al hacerlo de esa manera porque tiene sentido en términos de los pasos. Mi respuesta solo requiere un poco de planificación y un poco más de riesgo.
RolandoMySQLDBA


2

¿Has probado la función de presupuesto en 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'); 

guarde lo anterior, como 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 la línea de comando:

mysqldump mydb table4 |gzip > table4.sql.gz

En su servidor de destino, configure ~ / .my.cnf

[client]
default-character-set=utf8

Importar en el servidor de destino

zcat table4.sql.gz | mysql

1

Escribí un pequeño script para un problema similar, aquí está: 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";
?>

es decir, tienes esta consulta :

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

tienes este basurero :

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");
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.