Sqlalchemy: How to perform outer join with itself? - python

I want to perform this SQL query using Sqlalchemy (with model Evaluation):
select e1.user, sum(e1.points) as s from
(select e1.*
from evaluations e1 left outer join evaluations e2
on (e1.user = e2.user and e1.module = e2.module and e1.time < e2.time)
where e2.user is null and e1.module in (__another subquery__))
group by e1.user order by s limit 5
I don't know how to perform left outer join (especialy the renaming and referencing of renamed comlumns). Could you help me?

# sample sub-query for testing
_another_query = session.query(Evaluation.module).filter(Evaluation.module > 3)
# define aliases
E1 = aliased(Evaluation, name="e1")
E2 = aliased(Evaluation, name="e2")
# inner query
sq = (
session
# .query(E1)
# select columns explicitely to control labels
.query(E1.user.label("user"), E1.points.label("points"))
.outerjoin(E2, and_(
E1.user == E2.user,
E1.module == E2.module,
E1.time < E2.time,
))
.filter(E2.user == None)
.filter(E1.module.in_(_another_query))
)
sq = sq.subquery(name="sq")
# now lets group by
q = (
session
.query(sq.c.user, func.sum(sq.c.points))
.group_by(sq.c.user)
.order_by(func.sum(sq.c.points))
.limit(5)
)

Related

SQLAlchemy appends aliased Table in FROM clause

I'm creating an application with a flask backend and I'm using SQLAlchemy Core to create the SQL-Queries. Now I'm creating a base select like this:
SYSTEM = self.__TABLES.SYSTEM
RESPONSIBLE = self.__TABLES.responsible
INFRA_RESPONSIBLE = RESPONSIBLE.alias('INFRA_RES')
SYSTEM_RESPONSIBLE = RESPONSIBLE.alias('SYS_RES')
STMT = SYSTEM \
.outerjoin(INFRA_RESPONSIBLE, INFRA_RESPONSIBLE.c.id == SYSTEM.c.infrares) \
.outerjoin(SYSTEM_RESPONSIBLE, SYSTEM_RESPONSIBLE.c.id == SYSTEM.c.sysres)
Now, if I'm creating a SQL-Query like this:
STMT = select([SYSTEM]) \
.select_from(STMT) \
.where(INFRA_RESPONSIBLE.c.id == 1)
This is how the generated looks like:
SELECT "it".systems.*
FROM "it".responsible AS "INFRA_RES", "it".systems
LEFT OUTER JOIN "it".responsible as "INFRA_RES" ON "INFRA_RES".id = "it".systems.infrares
LEFT OUTER JOIN "it".responsible as "SYS_RES" ON "SYS_RES".id = "it".systems.sysres
WHERE "INFRA_RES".id = 1
But i want to look it like this:
SELECT "it".systems.*
"it".systems
LEFT OUTER JOIN "it".responsible as "INFRA_RES" ON "INFRA_RES".id = "it".systems.infrares
LEFT OUTER JOIN "it".responsible as "SYS_RES" ON "SYS_RES".id = "it".systems.sysres
WHERE "INFRA_RES".id = 1
The table INFRA_RES is already joined with the base statement, how can I achieve to build a proper SQL-Statement?

Python SQLAlchemy joinedload filter parameters

I'm trying to make a left outer join relation between two tables using joinedload but it doesn't seems to have the same behavior as using outerjoin method.
Here is an example using outerjoin:
stmt = select(ServerFarm)
stmt = stmt.outerjoin(
Server,
(ServerFarm.name == Server.server_farm_id) & (Server.deleted == False)
)
stmt = stmt.filter(ServerFarm.name == name)
print(stmt)
# SELECT ... FROM server_farm
# LEFT JOIN server ON server_farm.name = server.server_farm_id and server.deleted = False
# WHERE server_farm.name = :name
So when I'm using joinedload this is what happens:
stmt = select(ServerFarm)
stmt = stmt.options(
joinedload(
ServerFarm.servers,
(ServerFarm.name == Server.server_farm_id) & (Server.deleted == False)
)
)
stmt = stmt.filter(ServerFarm.name == name)
# SELECT ... FROM server_farm
# LEFT JOIN server ON server_farm.name = server.server_farm_id
# WHERE server_farm.name = :name
On this case, it just ignores the (Server.deleted == False) option. What am I doing wrong? Is there a way to add a "custom" filter on a left outer join using joinedload? How?

SQL statement to sqlalchemy ORM query API

I have the following SQL statement which works as expected, but I want to do the same thing using the Query API of sqlalchemy, I tried the following but it returns empty. Any idea how I can get this SQL statement by composing the Query API operations?
The raw SQL statement is:
SELECT COUNT(mid), mname
FROM(
SELECT missions._id AS mid, missions.name AS mname
FROM missions
INNER JOIN mission_ownership
ON missions._id = mission_ownership.mission_id
INNER JOIN mission_agencies
ON mission_agencies._id = mission_ownership.mission_agency_id
WHERE mission_agencies.name = 'Nasa'
)
GROUP BY mid
HAVING COUNT(mid) > 1
What I currently have using the ORM Query API:
nasa_and_esa_missions = session.query(func.count(Mission._id), Mission).\
join(mission_ownership). \
join(MissionAgency).\
filter(MissionAgency.name == 'Nasa').\
group_by(Mission._id).\
having(func.count(Mission._id) > 1)
If no relationship has been configured between mission_ownership and mission_agency at the ORM level, this can be done by modelling the inner SELECT as a subquery:
subq = (session.query(Mission._id.label('mid'), Mission.name.label('mname'))
.join(mission_ownership)
.join(MissionAgency)
.filter(MissionAgency.name == 'Nasa')
.subquery())
q = (session.query(subq.c.mid, Mission)
.group_by(subq.c.mid)
.having(sa.func.count(subq.c.mid) > 1))
for id_, m in q:
print(id_, m.name)
Which generates this SQL:
SELECT anon_1.mid AS anon_1_mid, missions._id AS missions__id, missions.name AS missions_name
FROM (SELECT missions._id AS mid, missions.name AS mname FROM missions
JOIN mission_ownership ON missions._id = mission_ownership.mission_id
JOIN mission_agencies ON mission_agencies._id = mission_ownership.mission_agency_id
WHERE mission_agencies.name = ?) AS anon_1, missions
GROUP BY anon_1.mid
HAVING count(anon_1.mid) > ?

Python - Create a nested join query using SQLAlchemy

Any ideas for how I could do a nested join using SQLAlchemy? Here is the raw query I am trying to recreate.
SELECT "tblLinkDivisions"."DivisionCoLinkID", 5 AS Score, "tblCompanies"."CompanyID"
FROM "tblS_AppliedStrategy"
INNER JOIN
(
(
"tblLinkIndustry" INNER JOIN "tblLinkDivisions" ON "tblLinkIndustry"."IndustryCoLinkID" = "tblLinkDivisions"."IndustryCoLinkID"
)
INNER JOIN "tblLinkAppliedStrategy"
ON "tblLinkDivisions"."DivisionCoLinkID" = "tblLinkAppliedStrategy"."DivisionCoLinkID"
)
ON "tblS_AppliedStrategy"."AppStrategyCode" = "tblLinkAppliedStrategy"."AppStrategyCode"
INNER JOIN "tblCompanies" ON ("tblCompanies"."CompanyID" = "tblLinkIndustry"."CompanyID" AND "tblCompanies"."CompanyID" != %s)
WHERE "tblS_AppliedStrategy"."AppStrategyCode" IN %s
I have tried a few different solutions with this being the closest outcome:
companies = (
session.query(tblLinkDivisions.DivisionCoLinkID, tblCompanies.CompanyID)
.select_from(tblS_AppliedStrategy)
.join(tblLinkIndustry, tblLinkDivisions.IndustryCoLinkID == tblLinkIndustry.IndustryCoLinkID)
.join(tblLinkAppliedStrategy, tblLinkDivisions.DivisionCoLinkID == tblLinkAppliedStrategy.DivisionCoLinkID)
.join(tblLinkAppliedStrategy, tblS_AppliedStrategy.AppStrategyCode == tblLinkAppliedStrategy.AppStrategyCode)
.join(tblCompanies, and_(tblCompanies.CompanyID == tblLinkIndustry.CompanyID, tblCompanies.CompanyID != company_id))
.filter(tblS_AppliedStrategy.AppStrategyCode.in_(strategy))
.all()
)
Any help or suggestions would be greatly appreciated!

Python variable in long SQL string

What is a safe way to replace the number in the second-to-last line of this SQL query with a variable?
Say my variable is customer_id. Can I use {} in place of 2 and put .format(customer_id) at the end of this string?
unlicensed_query = """
SELECT SUM(x.quantity), SUM(x.quantity * p.list_price)
FROM (
SELECT cu.customer_id, cu.product_id, cu.quantity
FROM csi_usage cu LEFT JOIN csi c
ON cu.customer_id = c.customer_id
AND cu.product_id = c.product_id
WHERE c.product_id IS NULL
AND cu.customer_id = 2) x, product p
WHERE x.product_id = p.id;
"""
As stated by thebjorn, the correct way to do this is to use bound parameters (http://docs.sqlalchemy.org/en/latest/core/tutorial.html#specifying-bound-parameter-behaviors). An example is here:
from sqlalchemy.sql import text
fully_utilized_query = text("""
SELECT SUM(x.quantity)
FROM (
SELECT cu.customer_id, cu.product_id, cu.quantity
FROM csi_usage cu
JOIN csi c
ON cu.customer_id = c.customer_id
AND cu.product_id = c.product_id
AND cu.quantity = c.licence_qty
WHERE cu.customer_id = :customer_id) x;
""")
fully_utilized = self.session.execute(fully_utilized_query, {'customer_id': current_user.customer_id}).scalar()

Categories

Resources