use MSSQL::TableReferences;
my $tr = MSSQL::TableReferences->new($dbh); # $dbh obtained elsewhere
# Get information about references FROM this table to other tables
$refs = $tr->tablereferences('this_table');
# $refs is now a hashref that describes how this_table
# references other_table.
$refs = {
'table' => 'this_table',
'alias' => 'tt',
'related' => [
{
'name' => 'other_table',
'alias' => 'ot',
'joins' => [
'tt.person_id = ot.person_id',
'tt.week_id = ot.week_id'
]
}
],
};
# Now get information about references TO this table FROM other tables
$refs = $tr->tablereferencedby('this_table')
# $refs is now a hashref that describes how two
# other tables reference this_table.
$refs = {
'table' => 'this_table',
'alias' => 'tt',
'related' => [
{
'name' => 'other_table',
'alias' => 'ot',
'joins' => [
'ot.person_id = tt.person_id'
]
},
{
'name' => 'yet_another_table',
'alias' => 'yat',
'joins' => [
'yat.person_id = tt.person_id',
'yat.week_id = tt.week_id'
]
}
]
};
####
# get the first relationship
my $firstrel = shift @{$refs->{related}};
# compose some sql
my $sql = qq{
SELECT * FROM $refs->{table} AS $refs->{alias}
JOIN $firstrel->{name} AS $firstrel->{alias}
ON }
.
join (' AND ', @{$firstrel->{joins}});
print $sql;
__END__
output:
SELECT * FROM this_table AS tt
JOIN other_table AS ot
ON tt.person_id = ot.person_id
AND tt.week_id = ot.week_id
####
my_table => mt
my_other_table => mot
fred => f
dbo.fred => f # note that table prefixes are ignored
####
my_table => mt
mother_table => mt1
####
package MSSQL::TableReferences;
use strict;
use warnings;
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
$self->{DBH} = shift or die 'dbh?';
bless ($self, $class);
return $self;
}
sub tablereferencedby {
tablereferences(@_,'Get references TO this table');
}
sub tablereferences {
my ($self,$table,$get_refs_TO_this_table) = @_;
my ($fromalias, $toalias, $fromkeyid, $tokeyid) =
$get_refs_TO_this_table
? ('?', '¿', 'fkeyid', 'rkeyid')
: ('¿', '?', 'rkeyid', 'fkeyid');
my $sql="select object_name(r.$fromkeyid)\n";
$sql .= ",'$fromalias.' + col_name(r.fkeyid,r.fkey$_) + ' = $toalias.' + col_name(r.rkeyid,r.rkey$_)\n"
for (1..16);
$sql .=qq{
from sysreferences r
where $tokeyid=object_id('$table')
order by constid,object_name($fromkeyid)
};
my @relationships;
push @relationships, {
name => $_->[0],
alias => _FirstLetters($_->[0]),
joins => [grep {defined} @{$_}[1..16]]
} for @{$self->{DBH}->selectall_arrayref($sql)};
my $table_alias = _FirstLetters($table);
for my $relationships (@relationships) {
$relationships->{alias} .= '1' if ($relationships->{alias} eq $table_alias);
for (@{$relationships->{joins}}) {
s/\?\./$relationships->{alias}\./;
s/\¿\./$table_alias\./;
}
}
@relationships ?
{
table => $table,
alias => $table_alias,
related => \@relationships
}
: undef;
}
sub _FirstLetters {
# return the first letter and the letters succeeding underscores.
# useful as alias names in sql.
# examples
# my_event_history => meh
# dbo.my_table => mt
my $text=shift;
return '' unless $text;
$text=~s/^\w+\.//ig; # remove prefixes
my $fl='';
while ($text=~ /(?:^(\w)|_(\w))/g) {$fl.=$1 || $2;}
return $fl;
}
1;
__END__