Bir sorguyu yeniden oluşturmak için gerekli olan veritabanının alt kümesini mysqldump yapmak mümkün müdür?


37

Arka fon

Bir selectsorguyu yeniden oluşturmak için gereken veritabanımın alt kümesini sağlamak istiyorum . Amacım, hesaplamalı iş akışımı tekrar üretilebilir kılmak ( tekrarlanabilir araştırmalarda olduğu gibi ).

Soru

Bu select deyimini, sorgulanan verileri yeni bir veritabanına aktaran bir komut dosyasına dahil edebilmemin bir yolu var mı? Yeni veritabanı, sorguda kullanılanlara ek olarak kayıt içermemelidir.

Güncelleme: Açıklama için, sorgu sonuçlarının csv dökümü ile ilgilenmiyorum. Yapabilmem gereken, veritabanı alt kümesini başka bir makineye kurulabilmesi için çöpe atmak ve ardından sorgunun kendisi tekrarlanabilir (ve aynı veri setine göre değiştirilebilir).

Örnek

Örneğin, analizim birden çok (bu örnekte 3) tablolardan kayıt gerektiren bir veri alt kümesini sorgulayabilir:

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

Tamam, ek kayıt yok. Yalnızca sorgu tarafından belirtilen sütunları mı istiyorsunuz?
Richard,

@Richard Bunu düşünmemiştim - bunun nasıl yapıldığını bilmek güzel olurdu.
David LeBauer

3
Bu, bazılarının merak ettiği ve cevaplanması gereken bir konu olduğuna emin olduğum çok özel bir soru. Bu tür soruyu herkese açık hale getirmek için +1.
RolandoMySQLDBA

Gelecekteki okuyucular: Kabul edilen cevaba ek olarak, özellikle sorgunun ihtiyaç duyduğu verileri dökülen randomx'ın cevabına bakınız .
ToolmakerSteve

Yanıtlar:


51

mysqldump , verilen tablo için NEREDE yan tümcesini çalıştırmak için --where seçeneğine sahiptir .

Bir birleştirme sorgusunu mysqldump yapmak mümkün olmamakla birlikte, her tablodan belirli satırları dışa aktarabilirsiniz, böylece her tablodan alınan her satır daha sonra birleşimde yer alacak.

Verilen sorgunuz için üç kez mysqldump uygulamanız gerekir:

Öncelikle, mysqld, tüm tablo3 satırlarının adlarını girin ('fee', 'fi', 'fo', 'fum'):

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

Daha sonra, mysqldump, ilk mysqldump'tan eşleşen table3_id değerlerine sahip tüm table2 satırları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

Ardından, mysqldump, ikinci mysqldump öğesinden eşleşen table1_id değerlerine sahip tüm tablo1 satırlarını girin:

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

Not: İkinci ve üçüncü mysqldumps birden fazla tablonun kullanılmasını gerektirdiğinden, --lock-all-table kullanılmalıdır .

Yeni veritabanınızı oluşturun:

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

Son olarak, üç mysqldumps'ı başka bir veritabanına yükleyin ve orada yeni veritabanında birleşmeyi deneyin.

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

MySQL istemcisinde birleştirme sorgunuzu çalıştırı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'); 

Bir şans ver !!!

UYARI: Doğru dizine alınmazsa, ikinci ve üçüncü mysqldumps sonsuza kadar sürebilir !!!

Durumda, aşağıdaki sütunları dizine ekle:

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

ID'nin table3'ün ana anahtarı olduğunu varsayalım.


1
Detaylı örnek için teşekkürler! --whereBelgedeki maddeyi özledim ; Bunu denemek için bir şans bulduktan sonra nasıl çalıştığını size bildiririz.
David LeBauer

1
+1 Bunu, bu problem için --tables yönteminden daha çok seviyorum. Genelde, --tables 'ı kullanırdım, fakat --where çok hoş bir seçenektir.
Richard,

Tek bir tabloyu mysqldump, --lock-all-tables kullanılmaz. Yan tümce tümce tüm tabloları terk etmelisiniz, çünkü yan tümce tüm tabloları mysqldump söylemek gerekir. -Lock-all-tables seçeneği, TEK BİR TABLO İÇİN DEĞİL, bir veya daha fazla veritabanını atmak için etkindir. 2. ve 3. mysqldumps yapmaya çalıştım ama bu şikayet etti. Manuel olarak -lock-all-tables komutunu verdikten sonra hata ortadan kalktı ve mysqldump başarılı oldu. Ayrıca, cevabımdaki ilk mysqldump öğesinin olmadığını unutmayın - - all-all-table.
RolandoMySQLDBA 2:11

@Rolando yardımlarınız için teşekkürler. Bu mükemmel çalıştı
David LeBauer 2:11

@Rolando üzgünüm, silmeden önce yorumumu / sorumu cevapladığınızı fark etmedim. Aynı hatayı alıyorum. Kılavuzu tekrar okuduktan sonra görüyorum - kilit tabloları yalnızca atılan tabloları kilitliyor. Kafam karıştı çünkü -lock-all-tables tüm veritabanlarını tüm veritabanlarına kilitler, bu da sadece tek bir veritabanı kullanırken gerekli değildir.
David LeBauer

7

Bu sorunu çözmek için mysqldump yerine SELECT'inizin bir parçası olarak bir 'outfile' kullanmayı düşünürdüm . İstediğiniz SELECT ifadesini üretebilir, daha sonra CSV stil çıkışı için uygun konfigürasyon ile sonunda "INTO OUTFILE '/path/to/outfile.csv' ..." komutunu ekleyebilirsiniz. Sonra verileri yeni şema konumunuza yüklemek için ' LOAD DATA INFILE ...' sözdizimi gibi bir şey kullanabilirsiniz .

Örneğin, SQL'inizi kullanarak:

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

Hedef disk bölümünde yeterli boş alana ihtiyacınız olduğunu unutmayın.


Dataload için bunu beğendim. Şemayı yeni veritabanına aktarmanız gerekecek, ancak bu kolayca başka bazı püf noktaları kullanılarak da mümkün.
Richard,

Bunu da seviyorum çünkü bazı insanlar temel tabloları istemeyebilirler, yalnızca içe aktarılan tek bir CSV olarak birleştirilen sonuç. +1 !!!
RolandoMySQLDBA

@randy Cevabınız için teşekkür ederim, ancak bu sorunun benim sorunumu çözdüğünü sanmıyorum çünkü bir csv sorgu sonuçları dökümü ile ilgilenmiyorum. Yapabileceğim tek şey, veritabanı alt kümesini başka bir makineye kurulabilmesi için çöpe atmak ve ardından sorgunun kendisi yeniden üretilebilir (ve aynı veri setine göre değiştirilebilir). Amaç, tekrarlanabilir araştırmayı destekleyen hesaplamalı bir iş akışıdır .
David LeBauer

Gelecekteki okuyucular için David'in yorumu: Richard'ın dediği gibi , ilgili tabloların şemasını ayrı ayrı dışa aktarmanız gerekir . Bu şemalar kolayca yeni bir veritabanına yüklenebilir. Ardından, randomx'ın dediği gibi, Load Data Infilebu .csv dosyasını bu yeni veritabanına yüklemek için kullanılır . Şimdi, sorgu çalıştırılabilir.
ToolmakerSteve

Bu tekniğin sınırlamasının, sorgu çıktısının orijinal tablolarla aynı organizasyonda olmadığını anladım. Bu yaklaşımı hala sevmeme rağmen, orijinal tablo yapısını yeniden oluşturmak için: Her tablo için bir tane olmak üzere her tablo için ayrı ayrı sorguları çalıştırın.
ToolmakerSteve

6

Mysqldump util, hangi tabloların atılacağını belirlemenizi sağlayan bir --tables seçeneğine sahiptir. Tabloların listesini belirtmenize izin verir.

Daha kolay (otomatik) bir yol bilmiyorum.


yardımınız için teşekkür ederim, ama sadece gerekli tabloları değil, sadece her tablonun seçilen satırlarını dışa aktarmak istiyorum. Birlikte dökümü izleyen bir komut dosyası olabilir delete from table1 where id not in (.....);yani, kolay yoludur sürece komut olarak eğer edilebilir otomatik, belirli bir araç var olduğunu gerekli değildir.
David LeBauer

Bir + 1'i hak ediyorsun çünkü --tables daha basit olacak ve gereksiz verileri düşürmek yeni sunucuda daha fazla at işi olacak, özellikle de masaların her biri 1GB'den fazla ise. Çoğu insan bu şekilde yapma konusunda daha fazla rahatlık hissedecektir, çünkü sadece adımlar açısından anlamlıdır. Cevabım sadece biraz planlama ve biraz daha risk alıyor.
RolandoMySQLDBA

3

Benim için faydalı olan şuydu:

mysqldump -u db_user -p db_name table_name --no_create_info \
--lock-all-tables --where 'id in (SELECT tn.id FROM table_name AS tn \
JOIN related_table AS rt ON tn.related_table_id = rt.id \
WHERE rt.some_field = 1)' > data.sql

Http://krosinski.blogspot.com/2012/12/using-table-join-with-mysqldump.html adresinden


2

MySQL'de alıntı fonksiyonunu denediniz mi?

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

Yukarıdaki, query.sql kaydedin.

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

1

MySQL'de:

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

Komut Satırında:

mysqldump mydb table4 |gzip > table4.sql.gz

Hedef sunucunuzda ~ / .my.cnf kurulumunu yapın

[client]
default-character-set=utf8

Hedef sunucuda içe aktar

zcat table4.sql.gz | mysql

1

Benzer bir problem için küçük bir senaryo yazdım, işte burada: 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";
?>

yani bu sorgunuz var :

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

Bu çöplüğü aldın :

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");
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.