Чи можливо 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'); 

Гаразд, тому ніяких додаткових записів немає. Ви хочете лише стовпців, зазначених у запиті?
Річард

@Richard Я цього не вважав - було б непогано знати, як це зробити.
David LeBauer

3
Це дуже унікальне запитання, на яке я впевнений, що деякі задалися питанням і на них потрібно відповісти. +1 для оприлюднення цього типу.
RolandoMySQLDBA

Майбутні читачі: Окрім прийнятої відповіді, дивіться відповідь випадкових випадків , яка конкретно скидає дані, необхідні для запиту.
ToolmakerSteve

Відповіді:


52

mysqldump має варіант --where виконати пункт WHERE для даної таблиці.

Хоча неможливо виконати mysqldump запиту приєднання, ви можете експортувати конкретні рядки з кожної таблиці, щоб згодом кожен рядок, отриманий з кожної таблиці, був долучений до об'єднання.

Для даного запиту вам потрібно буде тричі mysqldump:

По-перше, mysqldump всі рядки table3 з назвою в ('комісія', 'fi', 'fo', 'fum'):

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

Далі, mysqldump всі рядки table2, які мають відповідні значення table3_id з першого 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

Потім, mysqldump всі рядки table1, які мають відповідні значення table1_id, з другого 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

Примітка: Оскільки для другої та третьої mysqldumps потрібне використання більше однієї таблиці, необхідно використовувати --lock-all-table .

Створіть свою нову базу даних:

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

Нарешті, завантажте три 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'); 

Спробувати !!!

ПОПЕРЕДЖЕННЯ. Якщо неправильно проіндексовано, друга та третя mysqldumps можуть зайняти назавжди !!!

Про всяк випадок індексуйте наступні стовпці:

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пункт у документації; дасть вам знати, як це працює після того, як я отримаю можливість випробувати його.
David LeBauer

1
+1 Мені це подобається краще, ніж метод --tables для цієї проблеми. Взагалі, я б в кінцевому підсумку використовував --tables, але - десь - дуже приємний варіант.
Річард

Коли ви mysqldump одну таблицю, --lock-all-table не використовується. Оскільки в пункті, де задіяні таблиці, окрім тієї, що викидається, ви повинні сказати mysqldump --lock-all-table. Параметр --lock-all-table активований для скидання однієї або декількох баз даних, НЕ ДЛЯ ОДИННОЇ ТАБЛИЦІ. Я намагався виконати 2-й та 3-й mysqldumps, але він скаржився на це. Як тільки я видав вручну --lock-all-table, помилка усунулася і mysqldump вдався. Також, будь ласка, зауважте, що перший mysqldump у моїй відповіді не містить --lock-all-table.
RolandoMySQLDBA

@Rolando дякую за вашу допомогу. Це спрацювало чудово
Девід Лебоуер

@Rolando Вибачте, я не помітив, що ви відповіли на мій коментар / запитання, перш ніж я його видалили. Я отримував таку ж помилку. Після повторного читання посібника я бачу - блоки блокують тільки блоки, що скидаються. Мене збентежило, тому що --lock-all-table фіксує всі таблиці у всіх базах даних, що не потрібно при використанні однієї бази даних.
David LeBauer

7

Я б вирішив використовувати 'outfile' як частину вашого SELECT замість mysqldump для вирішення цієї проблеми. Ви можете створити будь-який оператор SELECT, який ви хочете, а потім додати "INTO OUTFILE" /path/to/outfile.csv '... "в кінці з відповідною конфігурацією для виведення стилю CSV. Тоді ви можете просто використати щось на зразок синтаксису " ЗАВАНТАЖИТИ ДАНІ 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 LeBauer

Для майбутніх читачів повторимо коментар Девіда: як згадував Річард, вам потрібно окремо експортувати схему відповідних таблиць. Ці схеми можна легко завантажити в нову базу даних. Потім, як випадково сказано, ви використовуєте Load Data Infileдля завантаження цього .csv в нову базу даних. Тепер запит можна виконати.
ToolmakerSteve

Я щойно зрозумів, що обмеження цієї методики полягає в тому, що вихід запиту не в тій же організації, що і вихідні таблиці. Хоча мені все ще подобається такий підхід, щоб відтворити оригінальну структуру таблиці: виконайте окремі запити, по одному на таблицю, щоб експортувати дані, необхідні для цієї таблиці.
ToolmakerSteve

6

Утиліта mysqldump має опцію --tables, яка дозволяє вам вказати, які таблиці потрібно скидати. Це дозволяє вказати список таблиць.

Я не знаю жодного простішого (автоматизованого) способу.


дякую за допомогу, але я хочу лише експортувати вибрані рядки кожної таблиці, а не лише необхідні таблиці. Я міг би мати сценарій, який слідкує за дампами delete from table1 where id not in (.....);, якщо це найпростіший спосіб, доки сценарій може бути автоматизований, не обов'язково існувати конкретний інструмент.
David LeBauer

Ви заслуговуєте +1, тому що --tables було б простішим, а викидання непотрібних даних було б просто більшою роботою коня на новому сервері, особливо якщо в цих таблицях розміром більше 1 ГБ. Більшість людей відчували б більший рівень комфорту, роблячи це саме так, оскільки це має сенс з точки зору кроків. Моя відповідь просто бере трохи планування та трохи більше ризику.
RolandoMySQLDBA


2

Ви спробували функцію цитування в 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'); 

збережіть вище, як 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");
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.