SQLAlchemy Joining with subquery issue - python

I am trying to translate SQL into SQLAlchemy. The SQL version of the query I want is as follows:
SELECT * from calendarEventAttendee
JOIN calendarEventAttendanceActual ON calendarEventAttendanceActual.id = calendarEventAttendee.attendanceActualId
LEFT JOIN
(SELECT bill.id, bill.personId, billToEvent.eventId FROM bill JOIN billToEvent ON bill.id = billToEvent.billId) b
ON b.eventId = calendarEventAttendee.eventId AND b.personId = calendarEventAttendee.personId
WHERE b.id is NULL
My SQLAlchemy query is as follows:
query = db.session.query(CalendarEventAttendee).join(CalendarEventAttendanceActual)
sub_query = db.session.query(Bill, BillToEvent).join(BillToEvent, BillToEvent.billId == Bill.id).subquery()
query = query.outerjoin(sub_query, and_(sub_query.Bill.personId == CalendarEventAttendee.personId, Bill.eventId == CalendarEventAttendee.eventId))
results = query.all()
I am getting an error AttributeError: 'Alias' object has no attribute 'Bill'
If I adjust the SQLAlchemy query to the following:
sub_query = db.session.query(Bill, BillToEvent).join(BillToEvent, BillToEvent.billId == Bill.id).subquery()
query = query.outerjoin(sub_query, and_(sub_query.Bill.personId == CalendarEventAttendee.personId, sub_query.BillToEvent.eventId == CalendarEventAttendee.eventId))
results = query.all()
I get an error AttributeError: Bill
Any help would be appreciated, thanks!

Once you call subquery(), there is no access to objects, but only to columns via .c.{column_name} accessor.
Do the following for sub_query instead: load only the columns you need in order to avoid any name collisions:
sub_query = db.session.query(
Bill.id, Bill.personId, BillToEvent.eventId
).join(BillToEvent, BillToEvent.billId == Bill.id).subquery()
Then in your query use column names with .c.column_name:
query = query.outerjoin(
sub_query, and_(
sub_query.c.personId == CalendarEventAttendee.personId,
sub_query.c.eventId == CalendarEventAttendee.eventId)
)
results = query.all()

Related

How to resolve SQLAlchemy Union Throwing Error

I'm using SQL Alchemy(Python, SQLServer) Union on two queries. It throws me the below error. Please help me in resolving it.
query1 = db.query(Employee.LastName).filter(Employee.Age == 30).all()
query2 = db.query(Employee.LastName).filter(Employee.Salary > 25000).all()
query3 = union(query1, query2).all()
**"SELECT construct for inclusion in UNION or other set construct expected, got [('Joseph',),('Alan',),('Joseph',)]."**
Also tried the below query and it throws the below error
query1 = db.query(Employee.LastName).filter(Employee.Age == 30).all()
query2 = db.query(Employee.LastName).filter(Employee.Salary > 25000).all()
query3 = query1.union(query2).all()
**"'list' object has no attribute 'union'"**
Remove the .all() from the first two queries, it turns the queries into lists, but you want to pass Query instances to union.
query1 = db.query(Employee.LastName).filter(Employee.Age == 30) # <- Query
query2 = db.query(Employee.LastName).filter(Employee.Salary > 25000) # <- Query
result = query1.union(query2).all() # <- List

SQLAlchemy subquery access outer tables

I'm having trouble converting this SQL into a valid SQLAlchemy query:
select *
from A
join B on B.Id = (
select top 1 Id
from B
where B.name = A.name
order by B.date
)
I've tried using the subquery but it fails:
query = session.query(A, B)
sub_query = session.query(B)
sub_query = sub_query.filter(B.name == A.name)
sub_query = sub_query.order_by(B.date.desc()).limit(1)
sub_query = sub_query.subquery()
query = query.join(B, B.id == sub_query.c.Id)
By accessing the A in the subquery, SqLAlchemy will add it to the subquery from clause and doesn't use the A from the outer query.
I've seen many SQLAlchemy subquery examples but none of them uses the outer fields.
By using correlate(A) in the subquery we tell the SQLAlchemy that reuses A from the outer query.
For making the join work we should access the Id of the subquery, so we should return only Id and use scalar_subquery() to convert the subquery to a scalar subquery:
query = session.query(A, B)
sub_query = session.query(B.Id)
sub_query = sub_query.filter(B.name == A.name)
sub_query = sub_query.order_by(B.date.desc()).limit(1)
sub_query = sub_query.correlate(A)
query = query.join(B, B.id == sub_query.scalar_subquery())

SQLAlchemy Select from Join of two Subqueries

Need help translating this SQL query into SQLAlchemy:
select
COALESCE(DATE_1,DATE_2) as DATE_COMPLETE,
QUESTIONS_CNT,
ANSWERS_CNT
from (
(select DATE as DATE_1,
count(distinct QUESTIONS) as QUESTIONS_CNT
from GUEST_USERS
where LOCATION like '%TEXAS%'
and DATE = '2021-08-08'
group by DATE
) temp1
full join
(select DATE as DATE_2,
count(distinct ANSWERS) as ANSWERS_CNT
from USERS
where LOCATION like '%TEXAS%'
and DATE = '2021-08-08'
group by DATE
) temp2
on temp1.DATE_1=temp2.DATE_2
)
Mainly struggling with the join of the two subqueries. I've tried this (just for the join part of the SQL):
query1 = db.session.query(
GUEST_USERS.DATE_WEEK_START.label("DATE_1"),
func.count(GUEST_USERS.QUESTIONS).label("QUESTIONS_CNT")
).filter(
GUEST_USERS.LOCATION.like("%TEXAS%"),
GUEST_USERS.DATE == "2021-08-08"
).group_by(GUEST_USERS.DATE)
query2 = db_session_stg.query(
USERS.DATE.label("DATE_2"),
func.count(USERS.ANSWERS).label("ANSWERS_CNT")
).filter(
USERS.LOCATION.like("%TEXAS%"),
USERS.DATE == "2021-08-08"
).group_by(USERS.DATE)
sq2 = query2.subquery()
query1_results = query1.join(
sq2,
sq2.c.DATE_2 == GUEST_USERS.DATE)
).all()
In this output I receive only the DATE_1 column and the QUESTIONS_CNT columns. Any idea why the selected output from the subquery is not being returned in the result?
Not sure if this is the best solution but this is how I got it to work. Using 3 subqueries essentially.
query1 = db.session.query(
GUEST_USERS.DATE_WEEK_START.label("DATE_1"),
func.count(GUEST_USERS.QUESTIONS).label("QUESTIONS_CNT")
).filter(
GUEST_USERS.LOCATION.like("%TEXAS%"),
GUEST_USERS.DATE == "2021-08-08"
).group_by(GUEST_USERS.DATE)
query2 = db_session_stg.query(
USERS.DATE.label("DATE_2"),
func.count(USERS.ANSWERS).label("ANSWERS_CNT")
).filter(
USERS.LOCATION.like("%TEXAS%"),
USERS.DATE == "2021-08-08"
).group_by(USERS.DATE)
sq1 = query1.subquery()
sq2 = query2.subquery()
query3 = db.session.query(sq1, sq2).join(
sq2,
sq2.c.DATE_2 == sq1.c.DATE_1)
sq3 = query3.subquery()
query4 = db.session.query(
func.coalesce(
sq3.c.DATE_1, sq3.c.DATE_2),
sq3.c.QUESTIONS_CNT,
sq3.c.ANSWERS_CNT
)
results = query4.all()

Trying to Convert SQL query to SQLAlchemy query

I'm trying to translate raw SQL into an sqlalchemy query, but so far I'm getting different results. The results I get from my attempt at sqlalchemy is missing Property objects (I get back a tuple with (ListingCalendarDays, None, PricingData)), so I think something is missing in the translation.
Original query:
result = session.execute("""SELECT p.id as property_id,
p.home_code,
c.listing_id,
c.calendar_date,
c.available,
ab.price
FROM listing_calendar_days c
LEFT JOIN properties p
ON (p.id::integer = c.listing_id and p.id is not null and p.id <> '')
LEFT JOIN pricing_data ab
ON c.listing_id = ab.listing_id
AND c.calendar_date = ab.price_date
WHERE c.calendar_date >= '%s'
AND c.calendar_date <= '%s' ;""" % ( now.isoformat(), end.isoformat()))
My Attempt:
query = (session.query( ListingCalendarDays, Properties, PricingData )
.outerjoin(Properties,
and_(Properties.id == str(ListingCalendarDays.listing_id),
Properties.id != None))
.outerjoin(PricingData,
and_(ListingCalendarDays.listing_id == PricingData.listing_id,
ListingCalendarDays.calendar_date == PricingData.price_date))
.filter(ListingCalendarDays.calendar_date.between( now, end )))
result = query.all()
You can print out the query that SQLAlchemy generates by doing
print(query)
In this particular case, your join condition for Properties is wrong. str(ListingCalendarDays.listing_id) produces the literal string "ListingCalendarDays.listing_id", not the column listing_id as casted to varchar. For a cast, you need the cast() construct:
cast(ListingCalendarDays.listing_id, String)
In addition, you're also missing the p.id <> '' condition.
Putting it all together:
and_(Properties.id == cast(ListingCalendarDays.listing_id, String),
Properties.id != None,
Properties.id != "")

Performing union with three queries - SQLAlchemy

In my project setup querying is being done based on the SQLAlchemy.
As per my previous requirements I have done the union with two queries.
Now I need to do Union with three queries.
Code is as follows:
query1 = query1.filter(model.name == "in-addr.arpa.")
query2 = query2.filter(model.tenant_id.in_(tenant_ids))
query = query1.union(query2)
Now Here I need to add one more query as follows:
query3 = query3.filter(model.tenant_id == context.tenant_id)
So I need to perform Union with all the three queries.
The solution is following:
query1 = query1.filter(model.name == "in-addr.arpa.")
query2 = query2.filter(model.tenant_id.in_(tenant_ids))
query3 = query3.filter(model.tenant_id == context.tenant_id)
query = query1.union(query2,query3)
This is how I did this in SQLAlchemy 1.3
from sqlalchemy import union
query1 = query1.filter(model.name == "in-addr.arpa.")
query2 = query2.filter(model.tenant_id.in_(tenant_ids))
query3 = query3.filter(model.tenant_id == context.tenant_id)
all_queries = [query1, query2, query3]
golden_set = union(*all_queries)
The change here is that the union method accepts a list of SQLAlchemy selectables.
In SQLAlchemy 1.4 you will need to use the function union and pass the queries as positional arguments instead of a list.
from sqlalchemy import union
query1 = query1.filter(model.name == "in-addr.arpa.")
query2 = query2.filter(model.tenant_id.in_(tenant_ids))
query3 = query3.filter(model.tenant_id == context.tenant_id)
query = union(query1, query2, query3)

Categories