Initial Commit
This commit is contained in:
286
database/perl/vendor/lib/DBIx/Class/Manual/Joining.pod
vendored
Normal file
286
database/perl/vendor/lib/DBIx/Class/Manual/Joining.pod
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
=head1 NAME
|
||||
|
||||
DBIx::Class::Manual::Joining - Manual on joining tables with DBIx::Class
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This document should help you to use L<DBIx::Class> if you are trying
|
||||
to convert your normal SQL queries into DBIx::Class based queries, if
|
||||
you use joins extensively (and also probably if you don't).
|
||||
|
||||
=head1 WHAT ARE JOINS
|
||||
|
||||
If you ended up here and you don't actually know what joins are yet,
|
||||
then you should likely try the L<DBIx::Class::Manual::Intro>
|
||||
instead. Skip this part if you know what joins are..
|
||||
|
||||
But I'll explain anyway. Assuming you have created your database in a
|
||||
more or less sensible way, you will end up with several tables that
|
||||
contain C<related> information. For example, you may have a table
|
||||
containing information about C<CD>s, containing the CD title and its
|
||||
year of publication, and another table containing all the C<Track>s
|
||||
for the CDs, one track per row.
|
||||
|
||||
When you wish to extract information about a particular CD and all
|
||||
its tracks, You can either fetch the CD row, then make another query
|
||||
to fetch the tracks, or you can use a join. Compare:
|
||||
|
||||
SELECT ID, Title, Year FROM CD WHERE Title = 'Funky CD';
|
||||
# .. Extract the ID, which is 10
|
||||
SELECT Name, Artist FROM Tracks WHERE CDID = 10;
|
||||
|
||||
SELECT cd.ID, cd.Title, cd.Year, tracks.Name, tracks.Artist FROM CD JOIN Tracks ON CD.ID = tracks.CDID WHERE cd.Title = 'Funky CD';
|
||||
|
||||
So, joins are a way of extending simple select statements to include
|
||||
fields from other, related, tables. There are various types of joins,
|
||||
depending on which combination of the data you wish to retrieve, see
|
||||
MySQL's doc on JOINs:
|
||||
L<http://dev.mysql.com/doc/refman/5.0/en/join.html>.
|
||||
|
||||
=head1 DEFINING JOINS AND RELATIONSHIPS
|
||||
|
||||
In L<DBIx::Class> each relationship between two tables needs to first
|
||||
be defined in the L<ResultSource|DBIx::Class::Manual::Glossary/ResultSource> for the
|
||||
table. If the relationship needs to be accessed in both directions
|
||||
(i.e. Fetch all tracks of a CD, and fetch the CD data for a Track),
|
||||
then it needs to be defined for both tables.
|
||||
|
||||
For the CDs/Tracks example, that means writing, in C<MySchema::CD>:
|
||||
|
||||
MySchema::CD->has_many('tracks', 'MySchema::Tracks');
|
||||
|
||||
And in C<MySchema::Tracks>:
|
||||
|
||||
MySchema::Tracks->belongs_to('cd', 'MySchema::CD', 'CDID');
|
||||
|
||||
There are several other types of relationships, they are more
|
||||
comprehensively described in L<DBIx::Class::Relationship>.
|
||||
|
||||
=head1 USING JOINS
|
||||
|
||||
Once you have defined all your relationships, using them in actual
|
||||
joins is fairly simple. The type of relationship that you chose
|
||||
e.g. C<has_many>, already indicates what sort of join will be
|
||||
performed. C<has_many> produces a C<LEFT JOIN> for example, which will
|
||||
fetch all the rows on the left side, whether there are matching rows
|
||||
on the right (table being joined to), or not. You can force other
|
||||
types of joins in your relationship, see the
|
||||
L<DBIx::Class::Relationship> docs.
|
||||
|
||||
When performing either a L<search|DBIx::Class::ResultSet/search> or a
|
||||
L<find|DBIx::Class::ResultSet/find> operation, you can specify which
|
||||
C<relations> to also refine your results based on, using the
|
||||
L<join|DBIx::Class::ResultSet/join> attribute, like this:
|
||||
|
||||
$schema->resultset('CD')->search(
|
||||
{ 'Title' => 'Funky CD',
|
||||
'tracks.Name' => { like => 'T%' }
|
||||
},
|
||||
{ join => 'tracks',
|
||||
order_by => ['tracks.id'],
|
||||
}
|
||||
);
|
||||
|
||||
If you don't recognise most of this syntax, you should probably go
|
||||
read L<DBIx::Class::ResultSet/search> and
|
||||
L<DBIx::Class::ResultSet/ATTRIBUTES>, but here's a quick break down:
|
||||
|
||||
The first argument to search is a hashref of the WHERE attributes, in
|
||||
this case a restriction on the Title column in the CD table, and a
|
||||
restriction on the name of the track in the Tracks table, but ONLY for
|
||||
tracks actually related to the chosen CD(s). The second argument is a
|
||||
hashref of attributes to the search, the results will be returned
|
||||
sorted by the C<id> of the related tracks.
|
||||
|
||||
The special 'join' attribute specifies which C<relationships> to
|
||||
include in the query. The distinction between C<relationships> and
|
||||
C<tables> is important here, only the C<relationship> names are valid.
|
||||
|
||||
This slightly nonsense example will produce SQL similar to:
|
||||
|
||||
SELECT cd.ID, cd.Title, cd.Year FROM CD cd JOIN Tracks tracks ON cd.ID = tracks.CDID WHERE cd.Title = 'Funky CD' AND tracks.Name LIKE 'T%' ORDER BY 'tracks.id';
|
||||
|
||||
=head1 FETCHING RELATED DATA
|
||||
|
||||
Another common use for joining to related tables, is to fetch the data
|
||||
from both tables in one query, preventing extra round-trips to the
|
||||
database. See the example above in L</WHAT ARE JOINS>.
|
||||
|
||||
Three techniques are described here. Of the three, only the
|
||||
C<prefetch> technique will deal sanely with fetching related objects
|
||||
over a C<has_many> relation. The others work fine for 1 to 1 type
|
||||
relationships.
|
||||
|
||||
=head2 Whole related objects
|
||||
|
||||
To fetch entire related objects, e.g. CDs and all Track data, use the
|
||||
'prefetch' attribute:
|
||||
|
||||
$schema->resultset('CD')->search(
|
||||
{ 'Title' => 'Funky CD',
|
||||
},
|
||||
{ prefetch => 'tracks',
|
||||
order_by => ['tracks.id'],
|
||||
}
|
||||
);
|
||||
|
||||
This will produce SQL similar to the following:
|
||||
|
||||
SELECT cd.ID, cd.Title, cd.Year, tracks.id, tracks.Name, tracks.Artist FROM CD JOIN Tracks ON CD.ID = tracks.CDID WHERE cd.Title = 'Funky CD' ORDER BY 'tracks.id';
|
||||
|
||||
The syntax of 'prefetch' is the same as 'join' and implies the
|
||||
joining, so there is no need to use both together.
|
||||
|
||||
=head2 Subset of related fields
|
||||
|
||||
To fetch a subset or the related fields, the '+select' and '+as'
|
||||
attributes can be used. For example, if the CD data is required and
|
||||
just the track name from the Tracks table:
|
||||
|
||||
$schema->resultset('CD')->search(
|
||||
{ 'Title' => 'Funky CD',
|
||||
},
|
||||
{ join => 'tracks',
|
||||
'+select' => ['tracks.Name'],
|
||||
'+as' => ['track_name'],
|
||||
order_by => ['tracks.id'],
|
||||
}
|
||||
);
|
||||
|
||||
Which will produce the query:
|
||||
|
||||
SELECT cd.ID, cd.Title, cd.Year, tracks.Name FROM CD JOIN Tracks ON CD.ID = tracks.CDID WHERE cd.Title = 'Funky CD' ORDER BY 'tracks.id';
|
||||
|
||||
Note that the '+as' does not produce an SQL 'AS' keyword in the
|
||||
output, see the L<DBIx::Class::Manual::FAQ> for an explanation.
|
||||
|
||||
This type of column restriction has a downside, the returned $result
|
||||
object will have no 'track_name' accessor:
|
||||
|
||||
while(my $result = $search_rs->next) {
|
||||
print $result->track_name; ## ERROR
|
||||
}
|
||||
|
||||
Instead C<get_column> must be used:
|
||||
|
||||
while(my $result = $search_rs->next) {
|
||||
print $result->get_column('track_name'); ## WORKS
|
||||
}
|
||||
|
||||
=head2 Incomplete related objects
|
||||
|
||||
In rare circumstances, you may also wish to fetch related data as
|
||||
incomplete objects. The usual reason to do is when the related table
|
||||
has a very large field you don't need for the current data
|
||||
output. This is better solved by storing that field in a separate
|
||||
table which you only join to when needed.
|
||||
|
||||
To fetch an incomplete related object, supply the dotted notation to the '+as' attribute:
|
||||
|
||||
$schema->resultset('CD')->search(
|
||||
{ 'Title' => 'Funky CD',
|
||||
},
|
||||
{ join => 'tracks',
|
||||
'+select' => ['tracks.Name'],
|
||||
'+as' => ['tracks.Name'],
|
||||
order_by => ['tracks.id'],
|
||||
}
|
||||
);
|
||||
|
||||
Which will produce same query as above;
|
||||
|
||||
SELECT cd.ID, cd.Title, cd.Year, tracks.Name FROM CD JOIN Tracks ON CD.ID = tracks.CDID WHERE cd.Title = 'Funky CD' ORDER BY 'tracks.id';
|
||||
|
||||
Now you can access the result using the relationship accessor:
|
||||
|
||||
while(my $result = $search_rs->next) {
|
||||
print $result->tracks->name; ## WORKS
|
||||
}
|
||||
|
||||
However, this will produce broken objects. If the tracks id column is
|
||||
not fetched, the object will not be usable for any operation other
|
||||
than reading its data. Use the L</Whole related objects> method as
|
||||
much as possible to avoid confusion in your code later.
|
||||
|
||||
Broken means: Update will not work. Fetching other related objects
|
||||
will not work. Deleting the object will not work.
|
||||
|
||||
=head1 COMPLEX JOINS AND STUFF
|
||||
|
||||
=head2 Across multiple relations
|
||||
|
||||
For simplicity in the example above, the C<Artist> was shown as a
|
||||
simple text field in the C<Tracks> table, in reality, you'll want to
|
||||
have the artists in their own table as well, thus to fetch the
|
||||
complete set of data we'll need to join to the Artist table too.
|
||||
|
||||
In C<MySchema::Tracks>:
|
||||
|
||||
MySchema::Tracks->belongs_to('artist', 'MySchema::Artist', 'ArtistID');
|
||||
|
||||
The search:
|
||||
|
||||
$schema->resultset('CD')->search(
|
||||
{ 'Title' => 'Funky CD' },
|
||||
{ join => { 'tracks' => 'artist' },
|
||||
}
|
||||
);
|
||||
|
||||
Which is:
|
||||
|
||||
SELECT me.ID, me.Title, me.Year FROM CD me JOIN Tracks tracks ON CD.ID = tracks.CDID JOIN Artists artist ON tracks.ArtistID = artist.ID WHERE me.Title = 'Funky CD';
|
||||
|
||||
To perform joins using relations of the tables you are joining to, use
|
||||
a hashref to indicate the join depth. This can theoretically go as
|
||||
deep as you like (warning: contrived examples!):
|
||||
|
||||
join => { room => { table => 'leg' } }
|
||||
|
||||
To join two relations at the same level, use an arrayref instead:
|
||||
|
||||
join => { room => [ 'chair', 'table' ] }
|
||||
|
||||
Or combine the two:
|
||||
|
||||
join => { room => [ 'chair', { table => 'leg' } ] }
|
||||
|
||||
=head2 Table aliases
|
||||
|
||||
As an aside to all the discussion on joins, note that L<DBIx::Class>
|
||||
uses the C<relation names> as table aliases. This is important when
|
||||
you need to add grouping or ordering to your queries:
|
||||
|
||||
$schema->resultset('CD')->search(
|
||||
{ 'Title' => 'Funky CD' },
|
||||
{ join => { 'tracks' => 'artist' },
|
||||
order_by => [ 'tracks.Name', 'artist.Artist' ],
|
||||
}
|
||||
);
|
||||
|
||||
SELECT me.ID, me.Title, me.Year FROM CD me JOIN Tracks tracks ON CD.ID = tracks.CDID JOIN Artists artist ON tracks.ArtistID = artist.ID WHERE me.Title = 'Funky CD' ORDER BY tracks.Name, artist.Artist;
|
||||
|
||||
This is essential if any of your tables have columns with the same names.
|
||||
|
||||
Note that the table of the resultsource the search was performed on, is always aliased to C<me>.
|
||||
|
||||
=head2 Joining to the same table twice
|
||||
|
||||
There is no magic to this, just do it. The table aliases will
|
||||
automatically be numbered:
|
||||
|
||||
join => [ 'room', 'room' ]
|
||||
|
||||
The aliases are: C<room> and C<room_2>.
|
||||
|
||||
=cut
|
||||
|
||||
=head1 FURTHER QUESTIONS?
|
||||
|
||||
Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
|
||||
by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
|
||||
redistribute it and/or modify it under the same terms as the
|
||||
L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
|
||||
Reference in New Issue
Block a user