From: Mike Bayer Date: Sun, 12 Nov 2006 23:22:13 +0000 (+0000) Subject: edits X-Git-Tag: rel_0_3_1~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1f3ce258ec3d86b00ff3daf4b9071f43a6022a25;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git edits --- diff --git a/doc/build/content/datamapping.txt b/doc/build/content/datamapping.txt index d9a94556ff..ee8fe18dca 100644 --- a/doc/build/content/datamapping.txt +++ b/doc/build/content/datamapping.txt @@ -399,9 +399,54 @@ We've seen how the `relation` specifier affects the saving of an object and its print repr(a) -#### Creating Joins Across Relations {@name=relselectby} +#### Selecting With Joins {@name=queryjoins} + +When using mappers that have relationships to other mappers, the need to specify query criterion across multiple tables arises. SQLAlchemy provides several core techniques which offer this functionality. + +When specifying columns to the `select` method of `Query`, if the columns are attached to a table other than the mapped table, that table is automatically added to the "FROM" clause of the query. This is the same behavior that occurs when creating a non-ORM `select` object. Using this feature, joins can be created when querying: + + {python} + {sql}l = session.query(User).select(and_(users.c.user_id==addresses.c.user_id, addresses.c.street=='123 Green Street')) + SELECT users.user_id AS users_user_id, + users.user_name AS users_user_name, users.password AS users_password + FROM users, addresses + WHERE users.user_id=addresses.user_id + AND addresses.street=:addresses_street + ORDER BY users.oid + {'addresses_street', '123 Green Street'} + +Above, we specified selection criterion that included columns from both the `users` and the `addresses` table. Note that in this case, we had to specify not just the matching condition to the `street` column on `addresses`, but also the join condition between the `users` and `addresses` table. Even though the `User` mapper has a relationship to the `Address` mapper where the join condition is known, **the select method does not assume how you want to construct joins**. If we did not include the join clause, we would get: + + {python} + # this is usually not what you want to do + {sql}l = session.query(User).select(addresses.c.street=='123 Green Street') + SELECT users.user_id AS users_user_id, + users.user_name AS users_user_name, users.password AS users_password + FROM users, addresses + WHERE addresses.street=:addresses_street + ORDER BY users.oid + {'addresses_street', '123 Green Street'} -For mappers that have relationships, the `select_by` method of the `Query` object can create queries that include automatically created joins. Just specify a key in the argument list which is not present in the primary mapper's list of properties or columns, but *is* present in the property list of one of its relationships: +The above join will return all rows of the `users` table, even those that do not correspond to the `addresses` table, in a **cartesian product** with the matching rows from the `addresses` table. + +Another way to specify joins more explicitly is to use the `from_obj` parameter of `select()`. This allows you to explicitly place elements in the FROM clause of the query, which could include lists of tables and/or `Join` constructs: + + {python} + {sql}l = session.query(User).select(addresses.c.street=='123 Green Street', from_obj=[users.join(addresses)]) + SELECT users.user_id AS users_user_id, + users.user_name AS users_user_name, users.password AS users_password + FROM users JOIN addresses ON users.user_id=addresses.user_id + WHERE addresses.street=:addresses_street + ORDER BY users.oid + {'addresses_street', '123 Green Street'} + +In the above example, the `join` function by default creates a natural join between the two tables, so we were able to avoid having to specify the join condition between `users` and `addresses` explicitly. + +#### Creating Joins Using selectby() {@name=relselectby} + +Another way that joins can be created is by using the `select_by` method of `Query`, which has the ability to create joins across relationships automatically. This method is in many circumstances more convenient than, but not as flexible as, the more SQL-level approach using the `select()` method described in the previous section. + +To issue a join using `select_by`, just specify a key in the argument list which is not present in the primary mapper's list of properties or columns, but *is* present in the property list of some relationship down the line of objects. The `Query` object will recursively traverse along the mapped relationships starting with the lead class and descending into child classes, until it finds a property matching the given name. For each new mapper it encounters along the path to the located property, it constructs a join across that relationship: {python} {sql}l = session.query(User).select_by(street='123 Green Street') @@ -421,11 +466,11 @@ The above example is shorthand for: Address.c.street=='123 Green Street') ) -All keyword arguments sent to `select_by` are used to create query criterion. This means that familiar `select` keyword options like `order_by` and `limit` are not directly available. To enable these options with `select_by`, you can try the [plugins_selectresults](rel:plugins_selectresults) extension which offers methods off the result of a `select` or `select_by` such as `order_by()` and array slicing functions that generate new queries. +All keyword arguments sent to `select_by` are used to create query criterion. This means that familiar `select` keyword options like `order_by` and `limit` are not directly available. To enable these options with `select_by`, you can try the [plugins_selectresults](rel:plugins_selectresults) extension which offers transitive methods off the result of a `select` or `select_by` such as `order_by()`, `limit()`, etc. -Also, `select_by` will *not* create joins derived from `Column`-based expressions (i.e. `ClauseElement` objects); the reason is that a `Column`-based expression may include many columns, and `select_by` has no way to know which columns in the expression correspond to properties and which don't (it also prefers not to dig into column expressions which may be very complex). The next section describes some ways to combine `Column` expressions with `select_by`'s auto-joining capabilities. +Note that the `select_by` method, while it primarily uses keyword arguments, also can accomodate `ClauseElement` objects positionally; recall that a `ClauseElement` is genearated when producing a comparison off of a `Column` expression, such as `users.c.name=='ed'`. When using `ClauseElements` with `select_by`, these clauses are passed directly to the generated SQL and are **not** used to further locate join criterion. If criterion is being constructed with these kinds of expressions, consider using the `select()` method which is better designed to accomodate these expressions. -#### More Granular Join Control Using join\_to, join\_via {@name=jointo} +#### Generating Join Criterion Using join\_to, join\_via {@name=jointo} Feature Status: [Alpha API][alpha_api] @@ -463,6 +508,7 @@ Expressions produced by `join_to` and `join_via` can be used with `select` to cr q.join_via(['orders', 'items']) ) +Note that the `from_obj` parameter of `select()`, described previously, allows finer grained control of joins, allowing any combination of inner and outer joins. #### Eager Loading {@name=eagerload}