sqlalchemy: select order_hist (parent table) having at least one private_trade (child) that met certain criteria

Question:

I have two tables: order_hist (parent table) and private_trade (child table). sqlalchemy: select order_hist (parent table) HAVING at least one private_trade (child) that met certain criteria applied on child table.

op.create_table(
        'order_hist',
        sa.Column('id', sa.Integer(), primary_key=True),
        sa.Column('algo_order_id', sa.Integer, sa.ForeignKey("algo_order.id")),
        ... other fields ...
        sa.Column('status', sa.String(), index=True, nullable=False),
    )
    
op.create_table(
        'private_trade',
        sa.Column('id', sa.Integer(), primary_key=True),
        sa.Column('order_id', sa.String(), sa.ForeignKey("order_hist.order_id")),
        sa.Column('hedged', sa.Boolean(), index=True, nullable=False)
        ... other fields ...
    )

I am trying to select order_hist (parent table) with at least one private_trade (child) where private_trade.hedged=False.

select
    order_hist.exchange,
    order_hist.order_id,
    count(private_trade.id)
from order_hist 
left join private_trade on private_trade.order_id = order_hist.order_id
left join algo_order on algo_order.id = order_hist.algo_order_id
where 
    private_trade.hedged = False
group by order_hist.exchange, order_hist.order_id

Below code actually works but it’s not elegant: I queried twice! I am also thinking this can be done in ".having" and "func.count".

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session
from sqlalchemy.orm import scoped_session
connstr : str = "postgresql://postgres:xxx@localhost/postgres"
engine = create_engine(connstr)
Session = scoped_session(sessionmaker(bind=engine))
session = Session()

algo_order_ids : Tuple = [ ... ]
statuses : Tuple[str] = (ExchangeOrderStatus.OPEN.name,),
has_at_least_one_unhedged_private_trade : bool = True

orders = session.query(
    Order
).filter(Order.algo_order_id.in_(algo_order_ids)).filter(Order.status.in_(statuses)).order_by(Order.created.asc())

if has_at_least_one_unhedged_private_trade:
    orders_with_unhedged_private_trades = session.query(
        Order.order_id
    ).join(
        PrivateTrade, Order.order_id==PrivateTrade.order_id
    ).filter(Order.algo_order_id.in_(algo_order_ids)).filter(Order.status.in_(statuses)).filter(PrivateTrade.hedged==False).group_by(Order.order_id).having(func.count(PrivateTrade.id)>0).all()
    if len(orders_with_unhedged_private_trades)>0:
        orders_with_unhedged_private_trades = [x[0] for x in orders_with_unhedged_private_trades]
        orders = orders.filter(Order.order_id.in_(orders_with_unhedged_private_trades))
        
in_scope_orders = orders.all()

My question is how to refactor above. Express the query in sqlalchemy ‘.having’ and ‘func.count’. Most importantly, not to query database TWICE.

Thanks in advance.

Asked By: user3761555

||

Answers:

I got it!

def order_hist_get_by_algo_order_ids(
    session : Session, 
    algo_order_ids : Tuple,
    statuses : Tuple[str] = (ExchangeOrderStatus.OPEN.name,),
    exclude_hedge_order : bool = False,
    has_at_least_one_unhedged_private_trade : bool = False,
    limit : int = INVALID
):
order_query = session.query(
                        Order
                    ).filter(Order.algo_order_id.in_(algo_order_ids)).filter(Order.status.in_(statuses))

if exclude_hedge_order:
    order_query = order_query.filter(Order.is_hedge_order==False) # INCLUDE only ones where Order.is_hedge_oder = False

if has_at_least_one_unhedged_private_trade:
    order_with_unhedged_trade_query =  session.query(
                                            Order.order_id
                                        ).filter(
                                            Order.algo_order_id.in_(algo_order_ids)
                                        ).filter(
                                            Order.status.in_(statuses)
                                        ).filter(PrivateTrade.hedged==False).group_by(Order.order_id).having(func.count(PrivateTrade.id)>0)
    if order_with_unhedged_trade_query.count()>0:
        order_query = order_query.join(
                            order_with_unhedged_trade_query.subquery(), 
                            order_with_unhedged_trade_query[0] == Order.order_id
                        ).with_entities(
                            Order
                        )
    else:
        order_query = order_query.filter(Order.order_id=="DOES_NOT_EXIST")

if limit != INVALID:
    order_query = order_query.limit(limit)

return order_query.all()
Answered By: user3761555

Sorry this is much better (urm what was I thinking):

def order_hist_get_by_algo_order_ids(
    session : Session, 
    algo_order_ids : Tuple,
    statuses : Tuple[str] = (ExchangeOrderStatus.OPEN.name,),
    exclude_hedge_order : bool = False,
    has_at_least_one_unhedged_private_trade : bool = False,
    limit : int = INVALID
    ):
    order_query = session.query(
                            Order
                        ).filter(Order.algo_order_id.in_(algo_order_ids)).filter(Order.status.in_(statuses))
    
    if exclude_hedge_order:
        order_query = order_query.filter(Order.is_hedge_order==False) # INCLUDE only ones where Order.is_hedge_oder = False

    if has_at_least_one_unhedged_private_trade:
        order_query = order_query.join(
                                            PrivateTrade,
                                            Order.order_id == PrivateTrade.order_id,
                                            isouter=False
                                        ).distinct()
                                        
    if limit != INVALID:
        order_query = order_query.limit(limit)
    
    return order_query.all()
Answered By: user3761555
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.