เป็นไปได้หรือไม่ที่จะ mysqldump ชุดย่อยของฐานข้อมูลที่จำเป็นในการสร้างแบบสอบถาม?


37

พื้นหลัง

ฉันต้องการให้ส่วนย่อยของฐานข้อมูลของฉันที่จำเป็นในการสร้างselectแบบสอบถาม เป้าหมายของฉันคือการทำให้เวิร์กโฟลว์การคำนวณของฉันทำซ้ำได้ (เช่นในการวิจัยที่ทำซ้ำได้ )

คำถาม

มีวิธีที่ฉันสามารถรวมคำสั่ง select นี้ลงในสคริปต์ที่ทิ้งข้อมูลการสืบค้นลงในฐานข้อมูลใหม่หรือไม่เช่นนั้นฐานข้อมูลสามารถติดตั้งบนเซิร์ฟเวอร์ mysql ใหม่ได้และคำสั่งจะทำงานกับฐานข้อมูลใหม่ ฐานข้อมูลใหม่ไม่ควรมีเร็กคอร์ดนอกเหนือจากที่ใช้ในเคียวรี

อัปเดต: เพื่อความกระจ่างฉันไม่สนใจ csv dump ของผลลัพธ์การสืบค้น สิ่งที่ฉันต้องทำคือการถ่ายโอนข้อมูลชุดย่อยฐานข้อมูลเพื่อให้สามารถติดตั้งบนเครื่องอื่นและจากนั้นแบบสอบถามสามารถทำซ้ำได้ (และแก้ไขได้ด้วยชุดข้อมูลเดียวกัน)

ตัวอย่าง

ตัวอย่างเช่นการวิเคราะห์ของฉันอาจเคียวรีชุดย่อยของข้อมูลที่ต้องการบันทึกจากหลาย ๆ ตาราง (ในตัวอย่างนี้ 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

ผู้อ่านในอนาคต: นอกเหนือจากคำตอบที่ยอมรับแล้วดูคำตอบของ randomxซึ่งจะทิ้งข้อมูลที่ต้องการโดยแบบสอบถาม
ToolmakerSteve

คำตอบ:


51

mysqldumpมีตัวเลือก--whereเพื่อเรียกใช้งาน WHERE clause สำหรับตารางที่กำหนด

แม้ว่าจะเป็นไปไม่ได้ที่จะ 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 ทุกตาราง-จะต้องใช้

สร้างฐานข้อมูลใหม่ของคุณ:

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 แต่ --where เป็นตัวเลือกที่ดีมาก
ริชาร์ด

เมื่อคุณ mysqldump ตารางเดียว --lock-all-tables จะไม่ถูกใช้ กลายเป็นที่ส่วนคำสั่งที่เกี่ยวข้องกับตารางอื่นนอกเหนือจากที่ถูกทิ้งคุณต้องบอก mysqldump - ล็อคทั้งหมดทั้งหมดตาราง ตัวเลือก --lock-all-tables ใช้งานได้สำหรับการทิ้งฐานข้อมูลอย่างน้อยหนึ่งฐานข้อมูลไม่ใช่สำหรับตารางเดี่ยว ฉันพยายามแสดง mysqldumps ที่ 2 และ 3 แต่มันบ่นเกี่ยวกับเรื่องนี้ เมื่อฉันออกด้วยตนเอง - ล็อคทุกตารางข้อผิดพลาดก็หายไปและ mysqldump ก็ประสบความสำเร็จ โปรดสังเกตว่า mysqldump แรกในคำตอบของฉันไม่มี --lock-all-tables
RolandoMySQLDBA

@Rolando ขอบคุณสำหรับความช่วยเหลือของคุณ สิ่งนี้ทำงานได้อย่างสมบูรณ์
David LeBauer

@Rolando ขออภัยฉันไม่ได้สังเกตว่าคุณได้ตอบความคิดเห็น / คำถามของฉันก่อนที่ฉันจะลบมัน ฉันได้รับข้อผิดพลาดเดียวกัน หลังจากอ่านคู่มืออีกครั้งฉันเห็น--lock-tablesล็อคเฉพาะตารางที่ถูกทิ้ง ฉันสับสนเพราะ--lock-all-tablesล็อคตารางทั้งหมดในฐานข้อมูลทั้งหมดซึ่งไม่จำเป็นเมื่อใช้ฐานข้อมูลเดียวเท่านั้น
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

@ แรนดี้ขอบคุณสำหรับคำตอบของคุณ แต่ฉันไม่คิดว่าสิ่งนี้จะช่วยแก้ปัญหาของฉันได้เพราะฉันไม่ได้สนใจ csv dump ของผลการสืบค้น สิ่งที่ฉันต้องทำคือการถ่ายโอนข้อมูลชุดย่อยฐานข้อมูลเพื่อให้สามารถติดตั้งบนเครื่องอื่นและจากนั้นแบบสอบถามสามารถทำซ้ำได้ (และแก้ไขได้ด้วยชุดข้อมูลเดียวกัน) เป้าหมายคือเวิร์กโฟลว์การคำนวณที่สนับสนุนการวิจัยที่ทำซ้ำได้
David LeBauer

สำหรับผู้อ่านอีกครั้งในอนาคตความคิดเห็นของดาวิด: ริชาร์ดกล่าวถึงคุณจะต้องแยกการส่งออกสคีมาของตารางที่เกี่ยวข้อง สกีมาเหล่านั้นสามารถโหลดลงในฐานข้อมูลใหม่ได้อย่างง่ายดาย จากนั้นตามที่ randomx กล่าวคุณใช้Load Data Infileโหลด. csv ลงในฐานข้อมูลใหม่นั้น ตอนนี้แบบสอบถามสามารถดำเนินการได้
ToolmakerSteve

ฉันเพิ่งรู้ว่าข้อ จำกัด ของเทคนิคนี้คือผลลัพธ์ของแบบสอบถามไม่ได้อยู่ในองค์กรเดียวกับตารางดั้งเดิม ในขณะที่ฉันยังคงชอบวิธีนี้ในการสร้างโครงสร้างตารางเดิม: เรียกใช้คิวรีแยกต่างหากหนึ่งรายการต่อตารางเพื่อส่งออกข้อมูลที่จำเป็นสำหรับตารางนั้น
ToolmakerSteve

6

การใช้ mysqldump มีตัวเลือก - ตารางที่ช่วยให้คุณระบุตารางที่จะถ่ายโอนข้อมูล มันช่วยให้คุณระบุรายการของตาราง

ฉันไม่รู้วิธีที่ง่ายกว่า


ขอบคุณสำหรับความช่วยเหลือ แต่ฉันต้องการส่งออกแถวที่เลือกของแต่ละตารางไม่ใช่เฉพาะตารางที่ต้องการ ฉันอาจมีสคริปต์ที่ตามหลังการถ่ายโอนข้อมูลด้วยdelete from table1 where id not in (.....);หากเป็นวิธีที่ง่ายที่สุดตราบใดที่สคริปต์สามารถเป็นแบบอัตโนมัติได้ก็ไม่จำเป็นต้องมีเครื่องมือเฉพาะ
ดาวิดเลอบัวเออร์

คุณสมควรได้รับ +1 เพราะ - ตารางจะง่ายกว่าและการทิ้งข้อมูลที่ไม่จำเป็นจะเป็นการทำงานของม้าในเซิร์ฟเวอร์ใหม่โดยเฉพาะหากตารางที่เกี่ยวข้องมีขนาดเกิน 1GB คนส่วนใหญ่จะรู้สึกสบายใจในระดับที่มากขึ้นเพราะเป็นวิธีที่เหมาะสม คำตอบของฉันใช้การวางแผนเพียงเล็กน้อยและมีความเสี่ยงเพิ่มขึ้นอีกเล็กน้อย
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");
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.