2014-06-07 3 views
0

sqlalchemy에서 나는 선수 테이블을 나타내는 테이블과 선수 점수에 해당하는 테이블을 두 테이블간에 일대 다 매핑합니다. 운동 선수는 임의의 점수를 가질 수 있습니다. 나는 선수들의 점수에 기초하여 선수들을 걸러 내려고 노력하고있다. 다음은 두 테이블의 코드입니다조인 된 sqlalchemy 문의 필터에 대한 하위 쿼리

ECHO = False 
from sqlalchemy.orm import sessionmaker 
from sqlalchemy import create_engine 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy import Column, Integer 
from sqlalchemy import ForeignKey 
from sqlalchemy.orm import relationship, backref 


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

    id = Column(Integer, primary_key=True) 

Base.metadata.create_all(engine) 
Session = sessionmaker(bind=engine) 
session = Session() 

athlete0 = Athlete(id = 0) 
athlete1 = Athlete(id = 1) 
athlete2 = Athlete(id = 2) 

session.add_all([ 
    athlete0, 
    athlete1, 
    athlete2]) 

session.commit() 

class Score(Base): 
    __tablename__ = 'scores' 
    pos = Column(Integer, primary_key=True) 
    score = Column(Integer) 
    athlete_id = Column(Integer, ForeignKey('athletes.id')) 
    athlete = relationship("Athlete", backref=backref('scores', order_by=pos)) 

Base.metadata.create_all(engine) 

athlete0.scores = [Score(score = 4), Score(score = 3), Score(score = 5)] 
athlete1.scores = [Score(score = 2), Score(score = 1)] 
athlete2.scores = [Score(score = 3), Score(score = 8), Score(score = 10), Score(score = 7)] 
session.commit() 

는 그리고 여기에 내가하고 싶은 일의 유형 : 코드의이 조각은 당신을 도울 수

foo = session.query(Athlete).join(Score).\ 
     filter(PRODUCT_OF_SCORES_FOR_A_GIVEN_ATHLETE > 5) 
+0

그럼 원하는 것은 score1 * score2 * score3> 5 같은 선수를 얻는 것입니까? –

+0

그래, 점수가 각 운동 선수마다 같지 않으므로 알고리즘은 점수가 무엇이든간에 곱해야합니다. – mikesol

답변

1

희망. Athlete 클래스에서 hybrid_property를 추가하기 만하면됩니다.

from sqlalchemy.ext.hybrid import hybrid_property 
from sqlalchemy.sql.expression import select, func 

class Athlete(Base): 
    __tablename__ = 'athletes' 

    id = Column(Integer, primary_key=True) 

    @hybrid_property 
    def product_of_score(self): 
     return sum(r.score for r in self.scores) 

    @product_of_score.expression 
    def product_of_score(self): 
     return select([func.sum(Score.score)]).\ 
      where(Score.athlete_id==self.id).\ 
      label('product_of_score') 

는 쿼리는 다음과 같습니다

>>> rc = session.query(Athlete).filter(Athlete.product_of_score > 5).all() 
>>> for r in rc: 
    print(r.id) 

0 
2 
+0

@codeape, 감사합니다. –

+0

나는 OP가 점수를 합산 한 것이 아니라 제품을 원한다고 생각 했습니까? 여기서 귀하의 코드는 합계를 계산합니다. –

+0

제품은 자연 로그 값을 취한 다음 합계를 계산 한 다음 e^sum을 수행하여 찾을 수 있습니다. 따라서 전략은 확실히 작동합니다. 내가 뭘하고 싶은지 대리모 대신 비교에서 원래 속성을 사용하고 싶습니다. 'rc = session.query (Athlete) .filter (Athlete) .filter (Athlete) .filter'와 같은 것을 산출 할 수 있다는 것을 의미합니다. Athlete.product_of_score> 5) .all()'. 보다 일반적으로, 한 속성을 다른 속성에 대한 프록시 (비교보다 큼,보다 작음, 같음 등)로 만드는 방법이 있습니까? – mikesol

0

내 자신의 질문에 대한 답변을 찾을. 나는 sqlalchemy core의 관점에서 아래를 대표하고 있습니다. 요즘 사용하고있는 것입니다.

같이

파이썬 코드가 보인다 :

from sqlalchemy import Table, Column, String, Integer, MetaData, \ 
    select, func, ForeignKey, text 
import sys 
from functools import reduce 

from sqlalchemy import create_engine 
engine = create_engine('sqlite:///:memory:', echo=False) 

metadata = MetaData() 

linked_list = Table('linked_list', metadata, 
    Column('id', Integer, primary_key = True), 
    Column('at', Integer, nullable=False), 
    Column('val', Integer, nullable=False), 
    Column('next', Integer, ForeignKey('linked_list.at')) 
) 

refs = Table('refs', metadata, 
    Column('id', Integer, primary_key = True), 
    Column('ref', Integer, ForeignKey('linked_list.at')), 
) 

placeholder = Table('placeholder', metadata, 
    Column('id', Integer, primary_key = True), 
    Column('ref', Integer, ForeignKey('linked_list.at')), 
    Column('val', Integer, nullable=False), 
) 

metadata.create_all(engine) 
conn = engine.connect() 

refs_al = refs.alias() 

linked_list_m = select([ 
        linked_list.c.at, 
        linked_list.c.val, 
        linked_list.c.next]).\ 
        where(linked_list.c.at==refs_al.c.ref).\ 
        cte(recursive=True) 

llm_alias = linked_list_m.alias() 
ll_alias = linked_list.alias() 

linked_list_m = linked_list_m.union_all(
    select([ 
     llm_alias.c.at, 
     ll_alias.c.val * llm_alias.c.val, 
     ll_alias.c.next 
    ]). 
     where(ll_alias.c.at==llm_alias.c.next) 
) 


llm_alias_2 = linked_list_m.alias() 

sub_statement = select([ 
      llm_alias_2.c.at, 
      llm_alias_2.c.val]).\ 
     order_by(llm_alias_2.c.val.desc()).\ 
     limit(1) 

def gen_statement(v) : 
    return select([refs_al.c.ref, func.max(llm_alias_2.c.val)]).\ 
    select_from(
    refs_al.\ 
     join(llm_alias_2, onclause=refs_al.c.ref == llm_alias_2.c.at)).\ 
    group_by(refs_al.c.ref).where(llm_alias_2.c.val > v) 

LISTS = [[2,4,4,11],[3,4,5,6]] 

idx = 0 
for LIST in LISTS : 
    start = idx 
    for x in range(len(LIST)) : 
    ELT = LIST[x] 
    conn.execute(linked_list.insert().\ 
     values(at=idx, val = ELT, next=idx+1 if x != len(LIST) - 1 else None)) 
    idx += 1 
    conn.execute(refs.insert().values(ref=start)) 

def gen_insert(v) : 
    return placeholder.insert().from_select(['ref', 'val'], gen_statement(v)) 

print "LISTS:" 
for LIST in LISTS : 
    print " ", LIST 

def PRODUCT(L) : return reduce(lambda x,y : x*y, L, 1) 
print "PRODUCTS OF LISTS:" 
for LIST in LISTS : 
    print " ", PRODUCT(LIST) 

for x in (345,355,365) : 
    statement_ = gen_insert(x) 
    print "########" 
    print "Lists that are greater than:", x 
    conn.execute(statement_) 
    allresults = conn.execute(select([placeholder.c.val])).fetchall() 
    if len(allresults) == 0 : 
    print " /no results found/" 
    else : 
    for res in allresults : 
     print res 
    conn.execute(placeholder.delete()) 

print "########" 

결과는 다음과 같습니다

LISTS: 
    [2, 4, 4, 11] 
    [3, 4, 5, 6] 
PRODUCTS OF LISTS: 
    352 
    360 
######## 
Lists that are greater than: 345 
(352,) 
(360,) 
######## 
Lists that are greater than: 355 
(360,) 
######## 
Lists that are greater than: 365 
    /no results found/ 
######## 

그리고 SQL가 발생

비결은 제품을 계산하기 위해 RECURSIVE와를 사용하는 것입니다 파이썬 함수 gen_statement에 의해 만들어진 자리 표시 자 테이블에 삽입됩니다 (더 읽기 쉽도록 들여 쓰기가 변경됨) :

.0 0 맺는 > 365 요청에 대해 오류가 발생합니다 SQLAlchemy의 난 그냥, select 문에 의해 반환 된 행을 반복하는 경우 때문에 호기심
WITH RECURSIVE anon_2(at, val, next) AS 
    (SELECT linked_list.at AS at, linked_list.val AS val, linked_list.next AS next 
    FROM linked_list, refs AS refs_1 
    WHERE linked_list.at = refs_1.ref 
    UNION ALL 
    SELECT anon_3.at AS at, linked_list_1.val * anon_3.val AS anon_4, linked_list_1.next AS next 
    FROM anon_2 AS anon_3, linked_list AS linked_list_1 
    WHERE linked_list_1.at = anon_3.next) 
SELECT refs_1.ref, max(anon_1.val) AS max_1 
FROM refs AS refs_1 JOIN anon_2 AS anon_1 ON refs_1.ref = anon_1.at 
WHERE anon_1.val > :val_1 GROUP BY refs_1.ref 

, 이유는 내가 읽는 다음 placeholder 표를 작성하고있어입니다 행. 이론 상으로는 0 행만 산출됩니다. 그러나 문 결과가 테이블 placeholder에 삽입 될 때 예상대로 0 행이 삽입됩니다.