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.
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()
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()
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.
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()
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()