Hey Rick,

I was playing with this patch a bit and noticed that while it seems pretty
good for select()'s, it falls apart in sub-selects.  It doesn't propagate as
a column, thus doesn't get an alias for the outer select to work off of, and
it all falls apart :)

Hopefully there's an easy way to solve this :D  Test case enclosed!  Patch as
you included it in your mail was applied against rev 1453.

Cheers!
-G

On Friday, May 12, 2006, 12:17:18 AM, you wrote:
> Hey Mike:

> Attached please find a patch against trunk r#1441 that implements the SQL
> CASE construct.

> I know you've argued in the past for an implementation in some kind of
> .ext-land, but let me at least present a few arguments for inclusion in the
> core library. I'll do this in the form of responses to some points in your
> latest post on this subject way back on St. Patrick's day (begora):

> On 3/17/06, Michael Bayer <[EMAIL PROTECTED]> wrote:
>>
>> I love the CASE statement, but yah I think its a cleaner in a view
>>

> Well, only if the condition and result expressions used are invariant. If
> that's the case, you may as well use a simple lookup table in a join. In my
> particular use case, the condition and result expressions used in the WHEN
> clauses change on almost every single query.

> (also what DB's support this one ?  let me guess, PG and Oracle and thats
>> it....oh and MSSQL-9.nobodyhasit :)
>>

> ...actually CASE is pretty well supported across the board, PG, Oracle,
> SQLite, MySQL all support it, and MS-SQL has had it since like version
> 6.5back in 1996.

>  ).   You can use literal strings too for this kind of thing.

> Sure but that's effective only if the expressions are rather trivial. Not
> having to build out string representations of SQL is one of the reasons I
> use SA in the first place! :-)


> if you have some brilliant notion of how this would even look as Python
>> expressions, that would be interesting to see.  It would definitely live as
>> a plugged-in extension.  I should make an effort to formally define the
>> "extensions" idea I have so that people can contribute whatever plugins they
>> want.
>>


> Well, I am certainly not claiming brilliance, but I will stand up for
> "stupid simple". The meat of the patch is only about 10 lines long.

> Here's a short doc:

>  case(whenlist, [value=value-expr], [else_=else-expr])

>      whenlist: A list of pairs.
>                Each pair is itself a sequence or iterator yielding a
> sequence
>                of two elements.

>                Each two-element pair represents a "WHEN" block of an SQL
>                CASE expression. The first element of the pair is an
> expression
>                that represents the WHEN condition, and the second is an
>                expression that represents the THEN result.

>                The optional [value] expression, if present, sets up a
>                "simple case" SQL clause. If present, then each condition
>                in the whenlist must evaluate to a constant value to which
> the
>                result of the value-expr is compared.

>                The optional [else_] expression represents the optional
> "ELSE"
>                clause in the SQL CASE construct.


>                Some examples:

>                    case([[MyTable.c.MyColumn < 2, 'First Bucket'], [
MyTable.c.MyColumn >>= 2, 'Second Bucket']])

>                yields:
>                    CASE WHEN MyTable.MyColumn < 2 THEN 'First Bucket'
>                         WHEN MyTable.MyColumn >= 2 THEN 'Second Bucket'
>                    END

>                 -------------------------------------

>                    case([(100, 'one hundred'), (5, 'five'), (82,
> 'quatre-vingts deux')],
>                          value=MyTable.c.MyColumn,
>                          else_='some other number')

>                yields:
>                    CASE MyTable.MyColumn
>                         WHEN 100 THEN 'one hundred'
>                         WHEN 5 THEN 'five'
>                         WHEN 82 THEN 'quatre-vingts deux'
>                         ELSE 'some other number'
>                    END

> OK, this doc isn't really correct, as proper literal string quoting requires
> the literal() function, but you get the idea.

> CASE is a pretty basic construct, and I think it belongs right up there with
> our friends DISTINCT and OUTER JOIN in the main library. Can ya spare a guy
> space for 10 lines of code?

> Thanks,
> Rick

Attachment: select_case.log
Description: Binary data

import sys
from sqlalchemy import *

engine = create_engine('sqlite', {'filename':':memory:'}, echo=True, 
echo_uow=True)
objectstore.LOG = True

info_table = Table('infos', engine,
        Column('pk', Integer, primary_key=True),
        Column('info', String))

info_table.create()

info_table.insert().execute(
        {'pk':1, 'info':'pk_1_data'},
        {'pk':2, 'info':'pk_2_data'},
        {'pk':3, 'info':'pk_3_data'},
        {'pk':4, 'info':'pk_4_data'},
        {'pk':5, 'info':'pk_5_data'})

inner = select([case([[info_table.c.pk < 3, literal('lessthan3', type=String)],
        [info_table.c.pk >= 3, literal('gt3', type=String)]]),
        info_table.c.pk, info_table.c.info], 
from_obj=[info_table]).alias('inner')
inner_result = inner.execute().fetchall()

# Outputs:
# lessthan3 1 pk_1_data
# lessthan3 2 pk_2_data
# gt3 3 pk_3_data
# gt3 4 pk_4_data
# gt3 5 pk_5_data
for i in inner_result:
        print i[0], i[1], i[2]

outer = select([inner])

outer_result = outer.execute().fetchall()

# Fails on KeyError (there aren't enough columns)
for i in outer_result:
        print i[0], i[1], i[2]

Reply via email to