2017-08-20 2 views
1

これは私のSQLAlchemyのクエリコードSQLAlchemy/Postgresで `group_by`ごとにN個の結果を制限する方法は?

medium_contact_id_subq = (g.session.query(distinct(func.unnest(FUContact.medium_contact_id_lis))).filter(FUContact._id.in_(contact_id_lis))).subquery() 
q = (g.session.query(FUMessage). 
     filter(FUMessage.fu_medium_contact_id.in_(medium_contact_id_subq)) 
     .order_by(desc(FUMessage.timestamp_utc)) 
     ) 

である私はNの結果とmedium_contact_idでグループ化されたFUMessageを制限したいと思います。回避策として


、これは私の現在の醜いと最適化されていないコードである:群あたりトップN行をフェッチする

medium_contact_id_lis = (g.session.query(distinct(func.unnest(FUContact.medium_contact_id_lis))).filter(FUContact._id.in_(contact_id_lis))).all() 
    q = None 
    for medium_contact_id_tup in medium_contact_id_lis: 
     medium_contact_id = medium_contact_id_tup[0] 
     if q is None: 
      q = (g.session.query(FUMessage) 
       .filter(FUMessage.fu_medium_contact_id == medium_contact_id) 
       .limit(MESSAGE_LIMIT) 
       ) 
     else: 
      subq = (g.session.query(FUMessage) 
       .filter(FUMessage.fu_medium_contact_id == medium_contact_id) 
       .limit(MESSAGE_LIMIT) 
       ) 
      q = q.union(subq) 
    q = q.order_by(desc(FUMessage.timestamp_utc)) 
+0

を限界前には、結果は不確定です。 –

答えて

3

一つの方法は、そのようなrank()又はrow_number()のように窓関数を使用することです必要なグループ化と順序を持つ副選択を作成し、それを囲む選択でフィルタリングします。 N = 1の場合、PostgreSQLでDISTINCT ON ... ORDER BYの組み合わせを使用できます。 SQLAlchemyのにそれを採用

ウィンドウ式を生成する機能素子のover()メソッドを使用して簡単です:労働組合を形成サブクエリが注文していないので

medium_contact_id_subq = g.session.query(
     func.unnest(FUContact.medium_contact_id_lis).distinct()).\ 
    filter(FUContact._id.in_(contact_id_lis)).\ 
    subquery() 

# Perform required filtering in the subquery. Choose a suitable ordering, 
# or you'll get indeterminate results. 
subq = g.session.query(
     FUMessage, 
     func.row_number().over(
      partition_by=FUMessage.fu_medium_contact_id, 
      order_by=FUMessage.timestamp_utc).label('n')).\ 
    filter(FUMessage.fu_medium_contact_id.in_(medium_contact_id_subq)).\ 
    subquery() 

fumessage_alias = aliased(FUMessage, subq) 

# row_number() counts up from 1, so include rows with a row num 
# less than or equal to limit 
q = g.session.query(fumessage_alias).\ 
    filter(subq.c.n <= MESSAGE_LIMIT) 
+0

素晴らしい、これは働いた! – nubela

関連する問題