Using the same schema given
in the beginning of this chapter, the following
example executes the equivalent of
“SELECT * from accounts” followed by a
“SELECT * from ledger_entries where
account_id=”
for each row in
the valueaccounts resultset, substituting that row's
account_id value for
value:
accounts accounts_rs=accounts::create(conn); for (const accounts::base::row &row: *accounts_rs) { ledger_entries account_ledger=row->join_ledger_entries(); for (const example2::ledger_entries::base::row &row: *account_ledger) { // ...
Each
,
each row in class::base::rowclass's resultset, also implements
join_()
methods, from the schema, like the resultset does, but in a slightly
different way.
class
The row's
join_()
returns a new resultset,
class instead of a
class
and initializes it with a
constraint
based on the values of the
row's key fields.
class::base::joins
This has the effect of creating a resultset that represents the rows
in the foreign table that the row joins to.
The resulting resultset is no different than
any other resultset, and accepts additional constraints, by
calling its search() method. For example:
accounts accounts_rs=accounts::create(conn); for (const accounts::base::row &row: *accounts_rs) { ledger_entries account_ledger=row->join_ledger_entries(); account_ledger->search("amount", ">", 0); for (const example2::ledger_entries::base::row &row: *account_ledger) { // ...
This example results in nested resultsets that execute
“SELECT * from ledger_entries where
account_id=”
for each row in
the value AND
amount > 0accounts resultset.
<table name="ledger_entries"> <column name="ledger_entry_id" datatype="int64_t" primarykey='1' /> <column name="account_id" datatype="int64_t"/> <column name="ledger_date" datatype="x::ymd"/> <column name="amount" datatype="double"/> <join class="accounts" type="inner join" only="1"> <column>account_id</column> </join> </table>
A join element has two optional attributes,
only and
maybe, which are mutually exclusive.
These attributes take effect for nested resultset joins only,
for join_()
methods implemented by
classname,
and do not apply to a resultset's
classname::base::rowjoin_()s.
classname
They have the effect of
having the
's
classname::base::rowjoin_()
automatically using
joinnameonly() or
maybe(), and returning another
or
class::base::row,
respectively.
class::base::rowptr
int64_t ledger_id; auto ledger_entries_rs=ledger_entries::create(conn); ledger_entries_rs->search("ledger_entry_id", "=", ledger_id); auto ledger_entry=ledger_entries_rs->only(); auto account=ledger_entry->join_accounts();
In the example schema, the accounts table's
account_id is a foreign key in the
ledger_entries table, so the
ledger_entries's join to
accounts using account_id
always joins one row from accounts.
Without the
only attribute,
join_accounts() returns, by default, a resultset
which always ends up containing one row.
only saves the trouble of always
invoking only() on the resultset, and changes
join_accounts() so that it returns
an accounts::base::row directly.
Similarly, the
maybe attribute changes
join_()
so that it uses
joinnamemaybe() and returns
a .
class::base::rowptr
The resulting
or
class::base::row
gets cached.
Invoking
class::base::rowptrjoin_()
again returns the same row, and does not create another resultset
and execute it.
joinname
Since
only
or
maybe create the nested resultset
and immediately iterate over it, it's not possible to add additional
joins to the nested resultset and
prefetch rows from
them.
Use
only
and
maybe only when it this is never
required.
One suggested naming convention is to always define two joins, from a table with a foreign key to the table with the primary key, like this:
<schema> <table name="ledger_entries"> <!-- ... --> <join class="accounts" joinname="account_id" type="inner join"> <column>account_id</column> </join> <join class="accounts" joinname="account" type="inner join" only="1"> <column>account_id</column> </join> </table> </schema>
join_account() returns a row and
join_account_id() returns a resultset for the
same logical join.