diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-31 20:26:40 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-31 20:26:40 -0400 |
commit | 62d81c2ebdb6b5a4d975fdb84d05398550490142 (patch) | |
tree | 963a1a51d4326a8c8ce001f764da304afd9c1c39 | |
parent | bdfe680708d869d37dd5c1bc2d2b045639caba9d (diff) | |
download | sqlalchemy-62d81c2ebdb6b5a4d975fdb84d05398550490142.tar.gz |
- walk back these literal SQL lectures into something much more succinct.
the ORM one in particular was really long winded and I don't really care if people
use text() anyway, they'll figure it out ;)
-rw-r--r-- | doc/build/core/tutorial.rst | 28 | ||||
-rw-r--r-- | doc/build/orm/tutorial.rst | 95 |
2 files changed, 23 insertions, 100 deletions
diff --git a/doc/build/core/tutorial.rst b/doc/build/core/tutorial.rst index e5af58c88..5f265ef14 100644 --- a/doc/build/core/tutorial.rst +++ b/doc/build/core/tutorial.rst @@ -809,21 +809,19 @@ to be placed within the FROM clause: ('%@aol.com', '%@msn.com') {stop}[(u'Wendy Williams, wendy@aol.com',)] -Going from constructed SQL to text, we lose some capabilities. We lose the -capability for SQLAlchemy to compile our expression to a specific target -database; above, our expression won't work with MySQL since it has no ``||`` -construct. It also becomes more tedious for SQLAlchemy to be made aware of the -datatypes in use; for example, if our bind parameters required UTF-8 encoding -before going in, or conversion from a Python ``datetime`` into a string (as is -required with SQLite), we would have to add extra information to our -:func:`~.expression.text` construct. Similar issues arise on the result set side, where -SQLAlchemy also performs type-specific data conversion in some cases; still -more information can be added to :func:`~.expression.text` to work around this. But what we -really lose from our statement is the ability to manipulate it, transform it, -and analyze it. These features are critical when using the ORM, which makes -heavy usage of relational transformations. To show off what we mean, we'll -first introduce the ALIAS construct and the JOIN construct, just so we have -some juicier bits to play with. +.. sidebar:: Why not use strings everywhere? + + When we use literal strings, the Core can't adapt our SQL to work + on different database backends. Above, our expression won't work + with MySQL since MySQL doesn't have the ``||`` construct. + If we only use :func:`.text` to specify columns, our :func:`.select` + construct will have an empty ``.c`` collection + that we'd normally use to create subqueries. + We also lose typing information about result columns and bound parameters, + which is often needed to correctly translate data values between + Python and the database. Overall, the more :func:`.text` we use, + the less flexibility and ability for manipulation/transformation + the statement will have. Using Aliases ============== diff --git a/doc/build/orm/tutorial.rst b/doc/build/orm/tutorial.rst index fda9115b0..d758c92b7 100644 --- a/doc/build/orm/tutorial.rst +++ b/doc/build/orm/tutorial.rst @@ -982,91 +982,16 @@ completely "raw", using string names to identify desired columns: ('ed',) {stop}[(1, u'ed', 12)] -.. topic:: Pros and Cons of Literal SQL - - :class:`.Query` is constructed like the rest of SQLAlchemy, in that it tries - to always allow "falling back" to a less automated, lower level approach to things. - Accepting strings for all SQL fragments is a big part of that, so that - you can bypass the need to organize SQL constructs if you know specifically - what string output you'd like. - But when using literal strings, the :class:`.Query` no longer knows anything about - that part of the SQL construct being emitted, and has no ability to - **transform** it to adapt to new contexts. - - For example, suppose we selected ``User`` objects and ordered by the ``name`` - column, using a string to indicate ``name``: - - .. sourcecode:: python+sql - - >>> q = session.query(User.id, User.name) - {sql}>>> q.order_by("name").all() #doctest: +NORMALIZE_WHITESPACE - SELECT users.id AS users_id, users.name AS users_name - FROM users ORDER BY name - () - {stop}[(1, u'ed'), (4, u'fred'), (3, u'mary'), (2, u'wendy')] - - Perfectly fine. But suppose, before we got a hold of the :class:`.Query`, - some sophisticated transformations were applied to it, such as below - where we use :meth:`~.Query.from_self`, a particularly advanced - method, to retrieve pairs of user names with - different numbers of characters:: - - >>> from sqlalchemy import func - >>> ua = aliased(User) - >>> q = q.from_self(User.id, User.name, ua.name).\ - ... filter(User.name < ua.name).\ - ... filter(func.length(ua.name) != func.length(User.name)) - - The :class:`.Query` now represents a select from a subquery, where - ``User`` is represented twice both inside and outside of the subquery. - Telling the :class:`.Query` to order by "name" doesn't really give - us much guarantee which "name" it's going to order on. In this - case it assumes "name" is against the outer "aliased" ``User`` construct: - - .. sourcecode:: python+sql - - {sql}>>> q.order_by("name").all() #doctest: +NORMALIZE_WHITESPACE - SELECT anon_1.users_id AS anon_1_users_id, - anon_1.users_name AS anon_1_users_name, - users_1.name AS users_1_name - FROM (SELECT users.id AS users_id, users.name AS users_name - FROM users) AS anon_1, users AS users_1 - WHERE anon_1.users_name < users_1.name - AND length(users_1.name) != length(anon_1.users_name) - ORDER BY name - () - {stop}[(1, u'ed', u'fred'), (1, u'ed', u'mary'), (1, u'ed', u'wendy'), (3, u'mary', u'wendy'), (4, u'fred', u'wendy')] - - Only if we use the SQL element directly, in this case ``User.name`` - or ``ua.name``, do we give :class:`.Query` enough information to know - for sure which "name" we'd like to order on, where we can see we get different results - for each: - - .. sourcecode:: python+sql - - {sql}>>> q.order_by(ua.name).all() #doctest: +NORMALIZE_WHITESPACE - SELECT anon_1.users_id AS anon_1_users_id, - anon_1.users_name AS anon_1_users_name, - users_1.name AS users_1_name - FROM (SELECT users.id AS users_id, users.name AS users_name - FROM users) AS anon_1, users AS users_1 - WHERE anon_1.users_name < users_1.name - AND length(users_1.name) != length(anon_1.users_name) - ORDER BY users_1.name - () - {stop}[(1, u'ed', u'fred'), (1, u'ed', u'mary'), (1, u'ed', u'wendy'), (3, u'mary', u'wendy'), (4, u'fred', u'wendy')] - - {sql}>>> q.order_by(User.name).all() #doctest: +NORMALIZE_WHITESPACE - SELECT anon_1.users_id AS anon_1_users_id, - anon_1.users_name AS anon_1_users_name, - users_1.name AS users_1_name - FROM (SELECT users.id AS users_id, users.name AS users_name - FROM users) AS anon_1, users AS users_1 - WHERE anon_1.users_name < users_1.name - AND length(users_1.name) != length(anon_1.users_name) - ORDER BY anon_1.users_name - () - {stop}[(1, u'ed', u'wendy'), (1, u'ed', u'mary'), (1, u'ed', u'fred'), (4, u'fred', u'wendy'), (3, u'mary', u'wendy')] +.. sidebar:: Why not use strings everywhere? + + When we use literal strings, the Core can't adapt our SQL to work + on different database backends. We also lose typing information about + result columns and bound parameters. Depending on how much of our + SQL is specified as fixed text, the :class:`.Query` object + has less ability to perform transformations and adaptations of the + SQL statement as a whole, which can impact features such as eager loading, + generation of joins and subqueries, and working with compound + mappings such as inheritance mappings. Counting -------- |