我有两个DB,比方说 A_old.sqlite 和 A_new.sqlite 。
两者都有很多表,但新数据库有一个(或多个)表,字段数不相等。
例如:
从shell我可以转储每个数据库,看看差异在哪里:
echo .dump | sqlite3 A_old.sqlite > A_old.sqlite.dump
echo .dump | sqlite3 A_new.sqlite > A_new.sqlite.dump
diff A_old.sqlite A_new.sqlite
问题是:如何在不手动解析 diff 输出的情况下更新OLD数据库中的模式? (在这种情况下,我需要将字段“地址”添加到表 Person )
答案 0 :(得分:1)
你可以在Perl中做这样的事情:
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $olddbh = DBI->connect('dbi:SQLite:old.db');
my $newdbh = DBI->connect('dbi:SQLite:new.db');
my %oldtables = $olddbh->tables();
my %newtables = $newdbh->tables();
my @oldtablenames;
my @newtablenames;
my $tmp;
print "Tables in new database\n";
print "======================\n";
foreach (%newtables){
$tmp=$_;
$tmp =~ s/.*"."//;
$tmp =~ s/".*//;
next if /sqlite_/;
print $tmp,"\n";
push(@newtablenames,$tmp);
}
print "\n";
print "Tables in old database\n";
print "======================\n";
foreach (%oldtables){
$tmp=$_;
$tmp =~ s/.*"."//;
$tmp =~ s/".*//;
next if /sqlite_/;
print $tmp,"\n";
push(@oldtablenames,$tmp);
}
print "\n";
# Check no tables missing from old
foreach (keys %newtables){
printf "Table: %s is missing in old database\n",$_ if ! exists $oldtables{$_};
}
# Check no tables in old but not in new
foreach (keys %oldtables){
printf "Table: %s is superfluous in old database\n",$_ if ! exists $newtables{$_};
}
# Work out tablenames common to new and old
my @common;
foreach my $table (@newtablenames){
foreach my $oldtable (@oldtablenames){
if($oldtable eq $table){
push(@common,$table);
last;
}
}
}
print "\n";
# For all tables, check fields match
foreach my $table (@common){
my $i;
printf "Checking fields in common table: %s\n",$table;
my @newfields;
my $sth = $newdbh->prepare("SELECT * FROM $table LIMIT 1");
$sth->execute();
my $nnfields=$sth->{NUM_OF_FIELDS};
for ($i = 0 ; $i < $nnfields ; $i++ ) {
push(@newfields,$sth->{NAME}->[$i]);
}
my @oldfields;
$sth = $olddbh->prepare("SELECT * FROM $table LIMIT 1");
$sth->execute();
my $nofields=$sth->{NUM_OF_FIELDS};
for ($i = 0 ; $i < $nofields ; $i++ ) {
push(@oldfields,$sth->{NAME}->[$i]);
}
if($nnfields != $nofields){
printf "Number of fields differs: %d vs %d\n",$nnfields,$nofields;
}
}
这将提供如下输出:
Tables in new database
======================
AdditionalTable
Person
Tables in old database
======================
OldTable
Person
Table: "main"."AdditionalTable" is missing in old database
Table: "main"."OldTable" is superfluous in old database
Checking fields in common table: Person
Number of fields differs: 3 vs 2
答案 1 :(得分:1)
这是一个棘手的问题,我怀疑你会找到一个解决它的简单方法。
首先,.dump
命令将发出sql
表的sqlite_master
列。此值是用于创建表的SQL。这里的问题是列顺序可能不同但等效。
vagrant@precise32:~/.ash$ sqlite3 new.db <<< "create table foo(a int, b int);"
vagrant@precise32:~/.ash$ sqlite3 old.db <<< "create table foo(b int, a int);"
vagrant@precise32:~/.ash$ sqlite3 new.db <<< .dump | grep foo
CREATE TABLE foo(a int, b int);
vagrant@precise32:~/.ash$ sqlite3 old.db <<< .dump | grep foo
CREATE TABLE foo(b int, a int);
如果忽略此问题,您仍然需要记住,发出的值只是用于创建表的SQL。无论您是否希望能够以编程方式将列添加到&#34; old&#34;中的表中,您都必须解析它。架构。
一种无法工作的方法是使用新架构创建 new 数据库,然后使用旧版INSERT
中的.dump
语句用于填充新数据库的数据库。
sqlite3 new.db <<< .dump | grep -v "^INSERT" | sqlite3 temp.db
sqlite3 old.db <<< .dump | grep "^INSERT" | sqlite3 temp.db
这会失败,因为INSERT
返回的.dump
语句是位置的。如果您可以以某种方式在表名后面的括号中插入列名,则可能可以工作。要执行此操作,您需要解析列名称并创建以逗号分隔的列表。然后每个插入行都需要在表名后包含该列表。这似乎是awk的一项工作,但在快速采取一些措施之后,我发现它并非琐碎
如果您想手动获取列名列表并将其放入变量中,您可以像这样估算解决方案:
sqlite3 new.db <<< .dump | grep -v "^INSERT" | sqlite3 temp.db
OLD_NAMES_CSV="b,a"
sqlite3 old.db <<< .dump \
| grep "^INSERT" \
| sed -e "s: VALUES: ($OLD_NAMES_CSV) VALUES:" \
| sqlite3 temp.db
在此之后,temp.db
应该包含来自old.db
的数据,但使用new.db
中的模式。
如果您要重新命名列或在新架构中删除它们,这不会起作用,但听起来它可能是您可能的解决方案。
对于比这更难的事情,我建议用python写一些东西。