This is an interesting question. The approach I took was a brute force search for columns that matched types and values for a small sample set. You'll probably have to tweak the heuristics to provide good results for your schema. I ran this on a schema that didn't use auto-incremented keys and it worked well. The code is written for MySQL, but it's very easy to adapt to Oracle.
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;
}