2009-02-27 15 views
1

Tablolar arasında tanımlı yabancı anahtarların bulunmadığı bir Oracle veritabanında orta büyüklükte tablo (50+) arasındaki ilişkileri tersine mühendislik yapıyorum. Tablolarda sütun adlarını eşleştirebilmek için (biraz) sayabilirim. Örneğin, "SomeDescriptiveName" sütun adı, muhtemelen tablo kümesinde aynıdır.Bir RDBMS harici/yabancı anahtarlarda tablolar arasındaki ilişkileri programlı olarak ayıklamak?

Yapmak istediğim, sütun adlarıyla eşleşen bazı ilişki kümelerini tabloları tek tek el ile geçirmekten daha iyi bir yol bulmaktır. Java DatabaseMetaData yöntemleri ile bir şeyler yapabilirdim ama bu, birisinin daha önce betimlemek zorunda olduğu görevlerden biri gibi görünüyor. Belki sütun isimlerini Perl veya başka bir betik lang ile ayıklayın, sütun isimlerini bir hash anahtarı olarak kullanın ve hash anahtarının işaret ettiği bir diziye tabloları ekleyin?

Bunu daha kolay hale getirebilecek veya iyi bir başlangıç ​​noktası sağlayacak herhangi bir ipucu veya öneriniz var mı? Yabancı anahtarlar önceden tanımlanmış olsaydı, ilişkileri anlamak çok daha kolay olurdu.

Teşekkürler.

cevap

1

Sorunuzdaki cevabı hemen hemen yazdınız.

my %column_tables; 
foreach my $table (@tables) { 
    foreach my $column ($table->columns) { 
     push @{$column_tables[$column]}, $table; 
    } 
} 
print "Likely foreign key relationships:\n"; 
foreach my $column (keys %column_tables) { 
    my @tables = @{$column_tables[$column]}; 
    next 
     if @tables < 2; 
    print $column, ': '; 
    foreach my $table (@tables) { 
     print $table->name, ' '; 
    } 
    print "\n"; 
} 
üç (veya dört) yaklaşımları arasında bir bileşimini kullanabilirsiniz
+0

Tabii ki, mantıksal olarak bağlantılı tüm sütunlar aynı ada sahip olduğu sürece bu işe yarar. Aksi takdirde, ilişkilerin kapsamlı bir analizini yapmak zorunda kalacaksınız. –

1

, şema şaşırtma şekline bağlı olarak geçerli:

  • dinamik yöntemler
    • gözlem:
      • RDBMS'de (veya ODBC katmanında) izlemeyi etkinleştir, ardından
      • sıkı sırayla değiştirilmiş olan tablolar belirlemek ve dizi aralığı içinde birden fazla sütunda oluşan
      • değerleri gösterebilir hangi sütun değer çiftleri daha sonra
      • , uygulama (ideal olarak kayıt yaratma) çeşitli aktivitelerini yerine bir yabancı anahtar ilişkisi
  • statik yöntemler
      (sadece mevcut verilerin analiz edilmesi, gerek çalışan bir uygulama alanına sahip)
    • terminolojisi: bütün sayısal sütun benzersiz değerlerin minimum/maksimum bir nokta (ve muhtemelen ortalama) ve bir maç
    • kod geri almak için çalışır: kolon adları ilişkileri
    • istatistiksel anlaması için deneyin mühendislik: En son çare (komut ile uğraşan sürece) - kalbin zayıf değil :) için
1

Stratejim col bulmak için Oracle sistem kataloğu kullanmak olacaktır sütun numarası ve veri tiplerinde veri türü ile aynıdır ancak tablo adı. Ayrıca, sütunlardan hangisi bir tablonun birincil veya benzersiz anahtarının bir parçasıdır. İşte

bunu yakın olabilecek bir sorgu, ama bunu test etmek kullanışlı Oracle örneği yoktur: Bu ilişkilidir sütunların herhangi davayı almazsınız Tabii

SELECT col1.table_name || '.' || col1.column_name || ' -> ' 
    || col2.table_name || '.' || col2.column_name 
FROM all_tab_columns col1 
    JOIN all_tab_columns col2 
    ON (col1.column_name = col2.column_name 
    AND col1.data_type = col2.data_type) 
    JOIN all_cons_columns cc 
    ON (col2.table_name = cc.table_name 
    AND col2.column_name = cc.column_name) 
    JOIN all_constraints con 
    ON (cc.constraint_name = con.constraint_name 
    AND cc.table_name = con.table_name 
    AND con.constraint_type IN ('P', 'U') 
WHERE col1.table_name != col2.table_name; 

ama farklı isimler var.

0

Bu ilginç bir soru. Çektiğim yaklaşım, küçük bir örnek kümesi için tür ve değerlerle eşleşen sütunlar için kaba bir kuvvet araştırmasıydı. Muhtemelen şemalarınız için iyi sonuçlar sağlamak amacıyla sezgisel düzenlemeleri yapmanız gerekecektir. Bunu, otomatik artırılmış anahtarları kullanmayan bir şema üzerinde çalıştırdım ve iyi çalıştı. Kod MySQL için yazılmıştır, ancak Oracle'a uyum sağlamak çok kolaydır.

use strict; 
use warnings; 
use DBI; 

my $dbh = DBI->connect("dbi:mysql:host=localhost;database=SCHEMA", "USER", "PASS"); 

my @list; 
foreach my $table (show_tables()) { 
    foreach my $column (show_columns($table)) { 
     push @list, { table => $table, column => $column }; 
    } 
} 

foreach my $m (@list) { 
    my @match; 
    foreach my $f (@list) { 
     if (($m->{table} ne $f->{table}) && 
      ($m->{column}{type} eq $f->{column}{type}) && 
      (samples_found($m->{table}, $m->{column}{name}, $f->{column}{samples}))) 
     { 
      # For better confidence, add other heuristics such as 
      # joining the tables and verifying that every value 
      # appears in the master. Also it may be useful to exclude 
      # columns in large tables without an index although that 
      # heuristic may fail for composite keys. 
      # 
      # Heuristics such as columns having the same name are too 
      # brittle for many of the schemas I've worked with. It may 
      # be too much to even require identical types. 

      push @match, "$f->{table}.$f->{column}{name}"; 
     } 
    } 
    if (@match) { 
     print "$m->{table}.$m->{column}{name} $m->{column}{type} <-- @match\n"; 
    } 
} 

$dbh->disconnect(); 

exit; 

sub show_tables { 
    my $result = query("show tables"); 
    return ($result) ? @$result :(); 
} 

sub show_columns { 
    my ($table) = @_; 
    my $result = query("desc $table"); 
    my @columns; 
    if ($result) { 
     @columns = map { 
      { name => $_->[0], 
       type => $_->[1], 
       samples => query("select distinct $_->[0] from $table limit 10") } 
     } @$result; 
    } 
    return @columns; 
} 

sub samples_found { 
    my ($table, $column, $samples) = @_; 
    foreach my $v (@$samples) { 
     my $result = query("select count(1) from $table where $column=?", $v); 
     if (!$result || $result->[0] == 0) { 
      return 0; 
     } 
    } 
    return 1; 
} 

sub query { 
    my ($sql, @binding) = @_; 
    my $result = $dbh->selectall_arrayref($sql, undef, @binding); 
    if ($result && $result->[0] && @{$result->[0]} == 1) { 
     foreach my $row (@$result) { 
      $row = $row->[0]; 
     } 
    } 
    return $result; 
}