Re: [sqlalchemy] Functions on column properties

2014-06-19 Thread Mike Solomon


not looking deeply but the hybrid you have in prop_3 doesn't seem to have 
 any relationship to the base set of rows you're getting from fractions.  
 it returns multiple rows because statement2 isn't using any aggregates.

 How about a straight SQL string?  what SQL do you expect?  these are very 
 easy to link to a hybrid.


It's difficult to issue a straight SQL string for the hybrid property 
itself because what I'm working with are group_by constructs which depend 
on aggregate functions.  So, the hybrid property would need to be aware of 
its context.  For example, in the code below, prop_3 now returns the 
maximum of all fractions and is not responsive to the group_by that comes 
later down the line:

ECHO = False
from sqlalchemy.orm import sessionmaker, aliased
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, select, func
from sqlalchemy.orm import relationship
from sqlalchemy.ext.hybrid import hybrid_property

engine = create_engine('sqlite:///:memory:', echo=ECHO)
Base = declarative_base()
class Fraction(Base):
   __tablename__ = 'fractions'

   id = Column(Integer, primary_key = True)
   prop_1 = Column(Integer)
   prop_2 = Column(Integer)
   prop_3_num = Column(Integer)
   prop_3_den = Column(Integer)

   @hybrid_property
   def prop_3(self) :
 return 0

   @prop_3.expression
   def prop_3(self) :
 alias_1 = aliased(Fraction)
 alias_2 = aliased(Fraction)
 statement1 = select([func.max(1.0 * alias_1.prop_3_num / 
alias_1.prop_3_den).label('fmax')])
 statement2 = select([alias_2.prop_3_num.label('prop_3_num_max'),
  alias_2.prop_3_den.label('prop_3_den_max')]).\
  where((1.0 * alias_2.prop_3_num / 
alias_2.prop_3_den) == statement1)
 return statement2

Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()


for x in range(10) :
  session.add(Fraction(prop_1=x%2, prop_2=x%4, prop_3_num = x+1, 
prop_3_den=x+2))

session.commit()

for x in session.query(Fraction.prop_1, Fraction.prop_2, Fraction.prop_3) :
  print x

print 

for x in session.query(Fraction.prop_1, Fraction.prop_2, 
Fraction.prop_3).group_by(Fraction.prop_1, Fraction.prop_2) :
  print x

***
the result is:

(0, 0, 10, 11)

(1, 1, 10, 11)

(0, 2, 10, 11)

(1, 3, 10, 11)

(0, 0, 10, 11)

(1, 1, 10, 11)

(0, 2, 10, 11)

(1, 3, 10, 11)

(0, 0, 10, 11)

(1, 1, 10, 11)



(0, 0, 10, 11)

(0, 2, 10, 11)

(1, 1, 10, 11)

(1, 3, 10, 11)


whereas I'd like for the local maxima to be chosen, meaning:


(0, 0, 9, 10)

(0, 2, 7, 8)

(1, 1, 10, 11)

(1, 3, 8, 9)

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Functions on column properties

2014-06-19 Thread Mike Bayer

On 6/19/14, 4:09 AM, Mike Solomon wrote:



 It's difficult to issue a straight SQL string for the hybrid property
 itself because


sorry, I meant, please write the query *that you really want* as a SQL
string.   Don't use SQLAlchemy.   It's better to work in that direction.

If you don't know what the SQL you want is, that's a different issue,
I'd start on that part first.


-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Functions on column properties

2014-06-19 Thread Mike Solomon


Le jeudi 19 juin 2014 16:10:19 UTC+3, Michael Bayer a écrit :


 On 6/19/14, 4:09 AM, Mike Solomon wrote: 
  
  
  
  It's difficult to issue a straight SQL string for the hybrid property 
  itself because 


 sorry, I meant, please write the query *that you really want* as a SQL 
 string.   Don't use SQLAlchemy.   It's better to work in that direction. 

 If you don't know what the SQL you want is, that's a different issue, 
 I'd start on that part first. 


Ah, OK.  The SQL below will group fractions based on tag and give the 
num(erator) and den(ominator) of the maximum for each group. In Python, I'd 
like to have a fraction class that has members tag and val where val is a 
hybrid_property combining num and den.   I'd like to be able to do a query 
like session.query(Fraction.tag, 
func.max(Fraction.val)).group_by(Fraction.tag) and get the SQL below:

CREATE TABLE fraction (
  id Int,
  tag Int,
  num Int,
  den Int,
  PRIMARY KEY (id)
);

SELECT DISTINCT fraction_a.tag, fraction_a.high,
  fraction_b.num, fraction_b.den
FROM
  (SELECT fraction.tag, max(1.0 * fraction.num / fraction.den) AS high
FROM fraction
GROUP BY fraction.tag)
  AS fraction_a JOIN
  (SELECT fraction.tag, fraction.num, fraction.den
FROM fraction)
  AS fraction_b
  ON fraction_a.tag = fraction_b.tag
AND fraction_a.high = 1.0 * fraction_b.num / fraction_b.den; 

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Functions on column properties

2014-06-19 Thread Mike Bayer

On 6/19/14, 2:41 PM, Mike Solomon wrote:


 Le jeudi 19 juin 2014 16:10:19 UTC+3, Michael Bayer a écrit :


 On 6/19/14, 4:09 AM, Mike Solomon wrote:
 
 
 
  It's difficult to issue a straight SQL string for the hybrid
 property
  itself because


 sorry, I meant, please write the query *that you really want* as a
 SQL
 string.   Don't use SQLAlchemy.   It's better to work in that
 direction.

 If you don't know what the SQL you want is, that's a different issue,
 I'd start on that part first.


 Ah, OK.  The SQL below will group fractions based on tag and give the
 num(erator) and den(ominator) of the maximum for each group. In
 Python, I'd like to have a fraction class that has members tag and val
 where val is a hybrid_property combining num and den.   I'd like to be
 able to do a query like session.query(Fraction.tag,
 func.max(Fraction.val)).group_by(Fraction.tag) and get the SQL below:


 SELECT DISTINCT fraction_a.tag, fraction_a.high,
   fraction_b.num, fraction_b.den
 FROM
   (SELECT fraction.tag, max(1.0 * fraction.num / fraction.den) AS high
 FROM fraction
 GROUP BY fraction.tag)
   AS fraction_a JOIN
   (SELECT fraction.tag, fraction.num, fraction.den
 FROM fraction)
   AS fraction_b
   ON fraction_a.tag = fraction_b.tag
 AND fraction_a.high = 1.0 * fraction_b.num / fraction_b.den;
So to the extent that 1.0 * num / den is a column-based expression you
like to use in your query, it's a good candidate for a hybrid or
column_property (deferred one in case you don't want to load it
unconditionally).   But as far as the FROM clauses, when we work with
Query(), the FROM clauses are always distinct entities that we have to
combine together as we want, there's never any kind of implicit behavior
with that.

Here's the appropriate place to use column_property() (specifically
deferred() so that it doesn't get loaded by default) in terms of how the
query should come out:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Fraction(Base):
__tablename__ = 'fraction'

id = Column('id', Integer, primary_key=True)
tag = Column(Integer)
num = Column(Integer)
den = Column(Integer)

high = deferred(1.0 * num / den)

fraction_a = select([
Fraction.tag,
func.max(Fraction.high).label(high),
]).group_by(Fraction.tag).alias('fraction_a')

fraction_b = aliased(Fraction, name=fraction_b)
sess = Session()

q = sess.query(fraction_a, fraction_b.num, fraction_b.den).\
distinct().\
join(fraction_b, and_(
fraction_a.c.high == fraction_b.high,
fraction_a.c.tag == fraction_b.tag
)
)
print q

output:

SELECT DISTINCT fraction_a.tag AS fraction_a_tag, fraction_a.high AS
fraction_a_high, fraction_b.num AS fraction_b_num, fraction_b.den AS
fraction_b_den
FROM (SELECT fraction.tag AS tag, max((:param_1 * fraction.num) /
fraction.den) AS high
FROM fraction GROUP BY fraction.tag) AS fraction_a JOIN fraction AS
fraction_b ON fraction_a.high = (:param_2 * fraction_b.num) /
fraction_b.den AND fraction_a.tag = fraction_b.tag







 -- 
 You received this message because you are subscribed to the Google
 Groups sqlalchemy group.
 To unsubscribe from this group and stop receiving emails from it, send
 an email to sqlalchemy+unsubscr...@googlegroups.com
 mailto:sqlalchemy+unsubscr...@googlegroups.com.
 To post to this group, send email to sqlalchemy@googlegroups.com
 mailto:sqlalchemy@googlegroups.com.
 Visit this group at http://groups.google.com/group/sqlalchemy.
 For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Functions on column properties

2014-06-19 Thread Mike Solomon


Le jeudi 19 juin 2014 22:07:14 UTC+3, Michael Bayer a écrit :

So to the extent that 1.0 * num / den is a column-based expression you 
 like to use in your query, it's a good candidate for a hybrid or 
 column_property (deferred one in case you don't want to load it 
 unconditionally).   But as far as the FROM clauses, when we work with 
 Query(), the FROM clauses are always distinct entities that we have to 
 combine together as we want, there's never any kind of implicit behavior 
 with that.



Thank you _very_ much for taking the time to write this informative answer.
The pattern I'm talking about is one I'm using often, so I'll be looking 
for a way to automate it.  If I find something worth contributing, I'll 
definitely send it to the list!

Cheers,
~Mike

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Functions on column properties

2014-06-18 Thread Mike Solomon


Le mercredi 18 juin 2014 22:03:33 UTC+3, Michael Bayer a écrit :

  

  if you can show the SQL you expect that would help.  it seems in your SO 
 question you want a subquery, you'd have to define that:

 class Holder(..):
 some_prop = column_property(select([func.max(1 * col1 / col2)]))



I tried this strategy, and the problem is that when it is used in 
combination with other things it creates a cross product.  In the below 
example, I'd like for all the queries to return 10 results but the last one 
returns 100 because it does not join the table generated in the max 
statement with the Fraction table.  Ideally, I'd like the last query to 
only generate 10 rows where the last two columns in each row are all the 
maximum numerator and denominator.

The sql I'd expect would be like that in my SO question: rows selected from 
a table joined to itself.

ECHO = False
from sqlalchemy.orm import sessionmaker, aliased
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, select, func
from sqlalchemy.orm import relationship
from sqlalchemy.ext.hybrid import hybrid_property

engine = create_engine('sqlite:///:memory:', echo=ECHO)
Base = declarative_base()
class Fraction(Base):
   __tablename__ = 'fractions'

   id = Column(Integer, primary_key = True)
   prop_1 = Column(Integer)
   prop_2 = Column(Integer)
   prop_3_num = Column(Integer)
   prop_3_den = Column(Integer)

   @hybrid_property
   def prop_3(self) :
 return 0

   @prop_3.expression
   def prop_3(self) :
 alias_1 = aliased(Fraction)
 alias_2 = aliased(Fraction)
 statement1 = select([func.max(1.0 * alias_2.prop_3_num / 
alias_2.prop_3_den).label('fmax')])
 statement2 = select([(1.0 * alias_2.prop_3_num / 
alias_2.prop_3_den).label('fdec'), alias_2.prop_3_num.label('max_num'), 
alias_2.prop_3_den.label('max_den')])
 return select([statement2.c.max_num.label('prop_3_max_num'), 
statement2.c.max_den.label('prop_3_max_den')]).\
select_from(statement2).join(statement1, 
onclause=statement1.c.fmax == statement2.c.fdec)

Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()


for x in range(10) :
  session.add(Fraction(prop_1=x%2, prop_2=x%4, prop_3_num = x+1, 
prop_3_den=x+2))

session.commit()

# should only print two rows...don't know why it prints 3
for x in session.query(Fraction.prop_3) :
  print x

for x in session.query(Fraction.prop_1) :
  print x

for x in session.query(Fraction.prop_1, Fraction.prop_2) :
  print x

for x in session.query(Fraction.prop_1, Fraction.prop_2, Fraction.prop_3) :
  print x

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] Functions on column properties

2014-06-18 Thread Mike Bayer

On 6/18/14, 4:50 PM, Mike Solomon wrote:


 Le mercredi 18 juin 2014 22:03:33 UTC+3, Michael Bayer a écrit :



 if you can show the SQL you expect that would help.  it seems in
 your SO question you want a subquery, you'd have to define that:

 class Holder(..):
 some_prop = column_property(select([func.max(1 * col1 / col2)]))



 I tried this strategy, and the problem is that when it is used in
 combination with other things it creates a cross product.  In the
 below example, I'd like for all the queries to return 10 results but
 the last one returns 100 because it does not join the table generated
 in the max statement with the Fraction table.  Ideally, I'd like the
 last query to only generate 10 rows where the last two columns in each
 row are all the maximum numerator and denominator.

 The sql I'd expect would be like that in my SO question: rows selected
 from a table joined to itself.

 ECHO = False
 from sqlalchemy.orm import sessionmaker, aliased
 from sqlalchemy import create_engine
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy import Column, Integer, String, select, func
 from sqlalchemy.orm import relationship
 from sqlalchemy.ext.hybrid import hybrid_property

 engine = create_engine('sqlite:///:memory:', echo=ECHO)
 Base = declarative_base()
 class Fraction(Base):
__tablename__ = 'fractions'

id = Column(Integer, primary_key = True)
prop_1 = Column(Integer)
prop_2 = Column(Integer)
prop_3_num = Column(Integer)
prop_3_den = Column(Integer)

@hybrid_property
def prop_3(self) :
  return 0

@prop_3.expression
def prop_3(self) :
  alias_1 = aliased(Fraction)
  alias_2 = aliased(Fraction)
  statement1 = select([func.max(1.0 * alias_2.prop_3_num /
 alias_2.prop_3_den).label('fmax')])
  statement2 = select([(1.0 * alias_2.prop_3_num /
 alias_2.prop_3_den).label('fdec'),
 alias_2.prop_3_num.label('max_num'), alias_2.prop_3_den.label('max_den')])
  return select([statement2.c.max_num.label('prop_3_max_num'),
 statement2.c.max_den.label('prop_3_max_den')]).\
 select_from(statement2).join(statement1,
 onclause=statement1.c.fmax == statement2.c.fdec)

 Base.metadata.create_all(engine)

 Session = sessionmaker(bind=engine)
 session = Session()


 for x in range(10) :
   session.add(Fraction(prop_1=x%2, prop_2=x%4, prop_3_num = x+1,
 prop_3_den=x+2))

 session.commit()

 # should only print two rows...don't know why it prints 3
 for x in session.query(Fraction.prop_3) :
   print x

 for x in session.query(Fraction.prop_1) :
   print x

 for x in session.query(Fraction.prop_1, Fraction.prop_2) :
   print x

 for x in session.query(Fraction.prop_1, Fraction.prop_2,
 Fraction.prop_3) :
   print x
not looking deeply but the hybrid you have in prop_3 doesn't seem to
have any relationship to the base set of rows you're getting from
fractions.  it returns multiple rows because statement2 isn't using
any aggregates.

How about a straight SQL string?  what SQL do you expect?  these are
very easy to link to a hybrid.






 -- 
 You received this message because you are subscribed to the Google
 Groups sqlalchemy group.
 To unsubscribe from this group and stop receiving emails from it, send
 an email to sqlalchemy+unsubscr...@googlegroups.com
 mailto:sqlalchemy+unsubscr...@googlegroups.com.
 To post to this group, send email to sqlalchemy@googlegroups.com
 mailto:sqlalchemy@googlegroups.com.
 Visit this group at http://groups.google.com/group/sqlalchemy.
 For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.