SQLAlchemy ORM – Working with Joins
Now that we have two tables, we will see how to create queries on both tables at the same time. To construct a simple implicit join between Customer and Invoice, we can use Query.filter() to equate their related columns together. Below, we load the Customer and Invoice entities at once using this method −
from sqlalchemy.orm import sessionmaker Session = sessionmaker(bind = engine) session = Session() for c, i in session.query(Customer, Invoice).filter(Customer.id == Invoice.custid).all(): print ("ID: {} Name: {} Invoice No: {} Amount: {}".format(c.id,c.name, i.invno, i.amount))
The SQL expression emitted by SQLAlchemy is as follows −
SELECT customers.id AS customers_id, customers.name AS customers_name, customers.address AS customers_address, customers.email AS customers_email, invoices.id AS invoices_id, invoices.custid AS invoices_custid, invoices.invno AS invoices_invno, invoices.amount AS invoices_amount FROM customers, invoices WHERE customers.id = invoices.custid
And the result of the above lines of code is as follows −
ID: 2 Name: Gopal Krishna Invoice No: 10 Amount: 15000 ID: 2 Name: Gopal Krishna Invoice No: 14 Amount: 3850 ID: 3 Name: Govind Pant Invoice No: 3 Amount: 10000 ID: 3 Name: Govind Pant Invoice No: 4 Amount: 5000 ID: 4 Name: Govind Kala Invoice No: 7 Amount: 12000 ID: 4 Name: Govind Kala Invoice No: 8 Amount: 8500 ID: 5 Name: Abdul Rahman Invoice No: 9 Amount: 15000 ID: 5 Name: Abdul Rahman Invoice No: 11 Amount: 6000
The actual SQL JOIN syntax is easily achieved using the Query.join() method as follows −
session.query(Customer).join(Invoice).filter(Invoice.amount == 8500).all()
The SQL expression for join will be displayed on the console −
SELECT customers.id AS customers_id, customers.name AS customers_name, customers.address AS customers_address, customers.email AS customers_email FROM customers JOIN invoices ON customers.id = invoices.custid WHERE invoices.amount = ?
We can iterate through the result using for loop −
result = session.query(Customer).join(Invoice).filter(Invoice.amount == 8500) for row in result: for inv in row.invoices: print (row.id, row.name, inv.invno, inv.amount)
With 8500 as the bind parameter, following output is displayed −
4 Govind Kala 8 8500
Query.join() knows how to join between these tables because there’s only one foreign key between them. If there were no foreign keys, or more foreign keys, Query.join() works better when one of the following forms are used −
query.join(Invoice, id == Address.custid) | explicit condition |
query.join(Customer.invoices) | specify relationship from left to right |
query.join(Invoice, Customer.invoices) | same, with explicit target |
query.join(”invoices”) | same, using a string |
Similarly outerjoin() function is available to achieve left outer join.
query.outerjoin(Customer.invoices)
The subquery() method produces a SQL expression representing SELECT statement embedded within an alias.
from sqlalchemy.sql import func stmt = session.query( Invoice.custid, func.count(''*'').label(''invoice_count'') ).group_by(Invoice.custid).subquery()
The stmt object will contain a SQL statement as below −
SELECT invoices.custid, count(:count_1) AS invoice_count FROM invoices GROUP BY invoices.custid
Once we have our statement, it behaves like a Table construct. The columns on the statement are accessible through an attribute called c as shown in the below code −
for u, count in session.query(Customer, stmt.c.invoice_count).outerjoin(stmt, Customer.id == stmt.c.custid).order_by(Customer.id): print(u.name, count)
The above for loop displays name-wise count of invoices as follows −
Arjun Pandit None Gopal Krishna 2 Govind Pant 2 Govind Kala 2 Abdul Rahman 2