Có thể mysqldump một tập hợp con của cơ sở dữ liệu cần thiết để tạo lại một truy vấn không?


37

Lý lịch

Tôi muốn cung cấp tập hợp con của cơ sở dữ liệu của tôi cần thiết để tạo lại một selecttruy vấn. Mục tiêu của tôi là làm cho dòng công việc tính toán của tôi có thể tái tạo (như trong nghiên cứu tái sản xuất ).

Câu hỏi

Có cách nào để tôi có thể kết hợp câu lệnh chọn này vào một tập lệnh đưa dữ liệu được truy vấn vào cơ sở dữ liệu mới, để cơ sở dữ liệu có thể được cài đặt trên máy chủ mysql mới và câu lệnh sẽ hoạt động với cơ sở dữ liệu mới. Cơ sở dữ liệu mới không được chứa các bản ghi ngoài những bản ghi đã được sử dụng trong truy vấn.

Cập nhật: Để làm rõ, tôi không quan tâm đến kết xuất truy vấn csv. Những gì tôi cần có thể làm là kết xuất tập hợp con cơ sở dữ liệu để có thể cài đặt nó trên một máy khác, và sau đó chính truy vấn có thể được lặp lại (và có thể sửa đổi đối với cùng một tập dữ liệu).

Thí dụ

Ví dụ: phân tích của tôi có thể truy vấn một tập hợp con dữ liệu yêu cầu các bản ghi từ nhiều bảng (trong ví dụ 3) này:

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, vì vậy không có hồ sơ bổ sung. Bạn có muốn chỉ các cột được chỉ định bởi truy vấn?
Richard

@Richard Tôi đã không cân nhắc điều đó - thật tuyệt khi biết cách làm điều này.
David LeBauer

3
Đây là một câu hỏi rất độc đáo mà tôi chắc chắn một số người đã tự hỏi và cần được trả lời. +1 để đưa loại câu hỏi này ra công khai.
RolandoMySQLDBA

Độc giả tương lai: Ngoài câu trả lời được chấp nhận, hãy xem câu trả lời của Randomx , cụ thể là bỏ dữ liệu cần thiết cho truy vấn.
ToolmakerSteve

Câu trả lời:


52

mysqldump có tùy chọn --where để thực thi mệnh đề WHERE cho một bảng đã cho.

Mặc dù không thể mysqldump một truy vấn tham gia, bạn có thể xuất các hàng cụ thể từ mỗi bảng để mỗi hàng được tải từ mỗi bảng sẽ được tham gia vào liên kết sau này.

Đối với truy vấn đã cho của bạn, bạn sẽ cần mysqldump ba lần:

Đầu tiên, mysqldump tất cả các hàng của bảng3 có tên trong ('phí', 'fi', 'fo', 'fum'):

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

Tiếp theo, mysqldump tất cả các hàng của bảng2 có các giá trị của bảng3_id phù hợp với mysqldump đầu tiên:

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

Sau đó, mysqldump tất cả các hàng của bảng1 có giá trị trùng khớp với bảng1_id từ mysqldump thứ hai:

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

Lưu ý: Vì mysqldumps thứ hai và thứ ba yêu cầu sử dụng nhiều hơn một bảng, nên phải sử dụng --lock-all-bảng .

Tạo cơ sở dữ liệu mới của bạn:

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

Cuối cùng, tải ba mysqldumps vào cơ sở dữ liệu khác và thử tham gia vào cơ sở dữ liệu mới.

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

Trong máy khách mysql, chạy truy vấn tham gia của bạ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'); 

Hãy thử một lần !!!

CẢNH BÁO: Nếu không được lập chỉ mục chính xác, mysqldumps thứ hai và thứ ba có thể mất mãi mãi !!!

Chỉ trong trường hợp, lập chỉ mục các cột sau:

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

Tôi sẽ giả sử id là khóa chính của bảng3.


1
cảm ơn ví dụ chi tiết! Tôi đã bỏ lỡ --wheređiều khoản trong tài liệu; sẽ cho bạn biết làm thế nào điều này hoạt động sau khi tôi có cơ hội để thử nó.
David LeBauer

1
+1 Tôi thích điều này hơn phương thức --tables cho vấn đề này. Nói chung, tôi sẽ sử dụng --tables, nhưng --where là một lựa chọn rất hay.
Richard

Khi bạn mysqldump một bảng duy nhất, --lock-all-bảng không được sử dụng. Trở thành mệnh đề where liên quan đến các bảng khác với bảng được kết xuất, bạn phải nói với mysqldump --lock-all-bảng. Tùy chọn --lock-all-bảng đang hoạt động để kết xuất một hoặc nhiều cơ sở dữ liệu, KHÔNG PHẢI CHO BẢNG MỘT SỐ. Tôi đã cố gắng thực hiện mysqldumps thứ 2 và thứ 3 nhưng nó đã phàn nàn về điều này. Khi tôi phát hành thủ công - khóa tất cả các bảng, lỗi sẽ biến mất và mysqldump đã thành công. Ngoài ra, vui lòng lưu ý mysqldump đầu tiên trong câu trả lời của tôi không có --lock-all-bảng.
RolandoMySQLDBA

@Rolando cảm ơn sự giúp đỡ của bạn. Điều này hoạt động hoàn hảo
David LeBauer

@Rolando xin lỗi, tôi không nhận thấy rằng bạn đã trả lời nhận xét / câu hỏi của tôi trước khi tôi xóa nó. Tôi đã nhận được cùng một lỗi. Sau khi đọc lại hướng dẫn, tôi thấy - các bảng khóa chỉ khóa các bảng bị đổ. Tôi đã nhầm lẫn vì --lock-all-bảng khóa tất cả các bảng trên tất cả các cơ sở dữ liệu, điều này không cần thiết khi chỉ sử dụng một cơ sở dữ liệu.
David LeBauer

7

Tôi sẽ xem xét sử dụng 'outfile' như một phần của CHỌN của bạn thay vì mysqldump để giải quyết vấn đề này. Bạn có thể tạo bất kỳ câu lệnh CHỌN nào bạn muốn, sau đó thêm "INTO OUTFILE '/path/to/outfile.csv' ..." vào cuối với cấu hình phù hợp cho đầu ra kiểu CSV. Sau đó, bạn có thể chỉ cần sử dụng cú pháp như ' LOAD DATA INFILE ...' để tải dữ liệu vào vị trí lược đồ mới của bạn.

Ví dụ: sử dụng SQL của bạn:

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

Hãy nhớ rằng bạn sẽ cần đủ dung lượng lưu trữ có sẵn trên phân vùng đĩa đích.


Tôi thích điều này cho tải dữ liệu. Bạn vẫn sẽ cần đưa lược đồ qua cơ sở dữ liệu mới, nhưng điều đó dễ dàng đạt được bằng cách sử dụng một số thủ thuật khác.
Richard

Tôi cũng thích điều này vì một số người có thể không muốn các bảng cơ sở, chỉ là kết quả được tham gia dưới dạng một CSV được nhập. +1 !!!
RolandoMySQLDBA

@randy Cảm ơn bạn đã trả lời, nhưng tôi không nghĩ rằng điều này giải quyết vấn đề của tôi vì tôi không quan tâm đến kết quả truy vấn csv. Những gì tôi cần có thể làm là kết xuất tập hợp con cơ sở dữ liệu để có thể cài đặt nó trên một máy khác, và sau đó chính truy vấn có thể được lặp lại (và có thể sửa đổi đối với cùng một tập dữ liệu). Mục tiêu là một quy trình tính toán hỗ trợ nghiên cứu tái sản xuất .
David LeBauer

Đối với độc giả trong tương lai, nhận xét của David: như Richard đã đề cập, bạn cần xuất riêng lược đồ của các bảng liên quan. Những lược đồ có thể dễ dàng được tải vào một cơ sở dữ liệu mới. Sau đó, như Randomx đã nói, bạn sử dụng Load Data Infileđể tải .csv đó vào cơ sở dữ liệu mới. Bây giờ, truy vấn có thể được thực hiện.
ToolmakerSteve

Tôi chỉ nhận ra rằng hạn chế của kỹ thuật này là đầu ra truy vấn không nằm trong cùng một tổ chức với các bảng gốc. Mặc dù tôi vẫn thích cách tiếp cận này, để tạo lại cấu trúc bảng gốc: Chạy các truy vấn riêng biệt, mỗi truy vấn mỗi bảng, để xuất dữ liệu cần thiết cho bảng đó.
ToolmakerSteve

6

Công cụ mysqldump có tùy chọn --tables cho phép bạn chỉ định bảng nào sẽ kết xuất. Nó cho phép bạn chỉ định danh sách các bảng.

Tôi không biết cách nào dễ dàng hơn (tự động).


cảm ơn sự giúp đỡ của bạn, nhưng tôi chỉ muốn xuất các hàng đã chọn của mỗi bảng, không chỉ các bảng cần thiết. Tôi có thể có một tập lệnh theo sau kết xuất delete from table1 where id not in (.....);, nếu đó là cách dễ nhất, miễn là tập lệnh có thể được tự động hóa, không cần thiết phải có công cụ cụ thể tồn tại.
David LeBauer

Bạn xứng đáng được +1 vì --tables sẽ đơn giản hơn và việc giảm dữ liệu không cần thiết sẽ chỉ là công việc nhiều ngựa hơn trong máy chủ mới, đặc biệt là nếu các bảng có liên quan trên 1GB. Hầu hết mọi người sẽ cảm thấy một mức độ thoải mái hơn khi làm theo cách đó bởi vì nó chỉ có ý nghĩa về các bước. Câu trả lời của tôi chỉ mất một chút kế hoạch và rủi ro hơn một chút.
RolandoMySQLDBA


2

Bạn đã thử chức năng trích dẫn trong mysql chưa?

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

lưu ở trên, như query.sql

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

1

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

Trên dòng lệnh:

mysqldump mydb table4 |gzip > table4.sql.gz

Trên máy chủ đích của bạn, hãy thiết lập ~ / .my.cnf

[client]
default-character-set=utf8

Nhập trên máy chủ đích

zcat table4.sql.gz | mysql

1

tôi đã viết một đoạn script nhỏ cho vấn đề tương tự, đây là: 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";
?>

tức là bạn có truy vấn này :

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

bạn có bãi rác này :

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");
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.