How to select specific columns of multi-column join in sqlalchemy? - python

We are testing the possibility to implement SQLAlchemy to handle our database work. In some instances I need to join a database to a clone of itself (with potentially different data, of course).
An example of the SQL I need to replicate is as follows:
SELECT lt.name, lt.date, lt.type
FROM dbA.dbo.TableName as lt
LEFT JOIN dbB.dbo.TableName as rt
ON lt.name = rt.name
AND lt.date = rt.date
WHERE rt.type is NULL
So far I have tried using the join object but I can't get it to not spit the entire join out. I have also tried various .join() methods based on the tutorial here: http://docs.sqlalchemy.org/en/rel_1_0/orm/tutorial.html and I keep getting an AttributeError: "mapper" or not what I'm looking for.
The issues I'm running into is that I need to not only join on multiple fields, but I can't have any foreign key relationships built into the objects or tables.

Thanks to Kay's like I think I figured out the solution.
It looks like it can be solved by:
session.query(dbA_TableName).outerjoin(
dbB_TableName,
and_(dbA_TableName.name == dbB_TableName.name",
dbA_TableName.date == dbB_TableName.date")
).filter("dbB_TableName.type is NULL")`

Related

Concatenate multiple join sqlalchemy python with different foreign keys

I have the following issue, i need to convert the following query into python's sqlalchemy orm:
SELECT parts.model_num,
ptemp_objects.ptemp_id, ptemp_objects.type, ptemp_objects.area, ptemp_objects.text, ptemp_objects.x, ptemp_objects.y, ptemp_objects.width, ptemp_objects.height, ptemp_objects.font, ptemp_objects.font_size, ptemp_objects.alignment, ptemp_objects.bold, ptemp_objects.italic, ptemp_objects.display_order,
ptype_areas.x, ptype_areas.y, ptype_areas.name, ptype_areas.width, ptype_areas.height,
paper_types.name, paper_types.width, paper_types.height, paper_types.left_margin, paper_types.right_margin, paper_types.top_margin, paper_types.bottom_margin,
print_images.path
FROM parts
JOIN prints
ON prints.part_id = parts.id
JOIN ptemp_objects
ON prints.ptemp_id = ptemp_objects.ptemp_id
JOIN ptype_areas
ON ptemp_objects.area = ptype_areas.id
JOIN paper_types
ON ptype_areas.ptype_id = paper_types.id
LEFT JOIN print_images
ON ptemp_objects.type = print_images.id
where prints.part_id = 879 and parts.model_num="BD854-20-YN-125-BN";
I have been trying with this:
session.query(Table1, Table2, Table3).select_from(Table1).join(Table2).join(Table3).all()
but i dont know how to build this in python's sqlalchemy nor how to declare it with so many foreign keys.
I am a beginner using this orm, i've been reading sqlalchemy's documentation but i have not been able to understand it well nor i have not found any solution to build this query. It would be great if you could help me to build this and a bit of explain also would be good.
Thanks!
I am using:
Windows 10 Professional.
Python 3.8.8.
Visual Studio Code.
SQLAlchemy 1.4.22
I could figure out the way to perform many joins into python's sqlalchemy,
basically i performed this code in python:
query = session.query(Parts.model_num, Parts.description, PtempObjects.text,PtypeAreas, PrintImages).select_from(Parts)\
.join(Prints,Prints.part_id==Parts.id)\
.join(PtempObjects,Prints.ptemp_id==PtempObjects.ptemp_id)\
.join(PtypeAreas,PtypeAreas.id==PtempObjects.area)\
.join(PrintImages,PrintImages.id==PtempObjects.type, isouter=True)\
.filter(Prints.part_id==879,Parts.model_num=="BD854-20-YN-125-BN")

Unable to access aliased fields in SQLAlchemy query results?

Confused working with query object results. I am not using foreign keys in this example.
lookuplocation = aliased(ValuePair)
lookupoccupation = aliased(ValuePair)
persons = db.session.query(Person.lastname, lookuplocation.displaytext, lookupoccupation.displaytext).\
outerjoin(lookuplocation, Person.location == lookuplocation.valuepairid).\
outerjoin(lookupoccupation, Person.occupation1 == lookupoccupation.valuepairid).all()
Results are correct as far as data is concerned. However, when I try to access an individual row of data I have an issue:
persons[0].lastname works as I expected and returns data.
However, there is a person.displaytext in the result but since I aliased the displaytext entity, I get just one result. I understand why I get the result but I need to know what aliased field names I would use to get the two displaytext columns.
The actual SQL statement generated by the above join is as follows:
SELECT person.lastname AS person_lastname, valuepair_1.displaytext AS valuepair_1_displaytext, valuepair_2.displaytext AS valuepair_2_displaytext
FROM person LEFT OUTER JOIN valuepair AS valuepair_1 ON person.location = valuepair_1.valuepairid LEFT OUTER JOIN valuepair AS valuepair_2 ON person.occupation1 = valuepair_2.valuepairid
But none of these "as" field names are available to me in the results.
I'm new to SqlAlchemy so most likely this is a "newbie" issue.
Thanks.
Sorry - RTFM issue - should have been:
lookuplocation.displaytext.label("myfield1"),
lookupoccupation.displaytext.label("myfield2")
After results are returned reference field with person.myfield
Simple.

How to update multiple records using peewee

I'm using Peewee with Postgres database. I want to know how to update multiple records in a tabel at once?
We can perform this update in SQL using these commands, and I'm looking for a Peewee equivalent approach.
Yes, you can use the insert_many() function:
Insert multiple rows at once. The rows parameter must be an iterable
that yields dictionaries. As with insert(), fields that are not
specified in the dictionary will use their default value, if one
exists.
Example:
usernames = ['charlie', 'huey', 'peewee', 'mickey']
row_dicts = ({'username': username} for username in usernames)
# Insert 4 new rows.
User.insert_many(row_dicts).execute()
More details at: http://docs.peewee-orm.com/en/latest/peewee/api.html#Model.insert_many
ORMs usually dose not support bulk update and you have to use custom SQL, you can see samples in this link (db.excute_sql)

Automagically including a join when needed in SQLAlchemy

I'm building a class about a set of tables. One of those dables, TableDates, includes a field TableDates.date which I use to filter for periods. That's easy. Assuming query already have the needed select construct:
query = query.filter(TableDates.date < date)
And it works when query already included a proper join with TableDates.
But in some cases, y have a query that does not include a proper join with it. In those cases, I should use:
query = query.join(TableDates).filter(TableDates.date < date)
And here is the problem. I would like to include this filter code in an object method that would work both with queries that already joined TableDates, or not. Of course, I can track in my own methods when I join with TableDates, to decide if I have to include join(TableDates) in the query or not. But my question is: Is there a way of letting SQLAlchemy do the job? I've browsing the documentation, and I didn't find a clue, but maybe I missed it.
More in particular, I was thinking of either:
Having a way of checking a query to know if TableDates is already joined in it.
Having a way of writting the join(TableDates) in a way that if it is already joined in query, it does nothing (of course, if I just try to add that join when TableDates is already joined, I get an exception stating that I cannot joint it with itself).

sqlalchemy FULL OUTER JOIN

How to implement FULL OUTER JOIN in sqlalchemy on orm level.
Here my code:
q1 = (db.session.query(
tb1.user_id.label('u_id'),
func.count(tb1.id).label('tb1_c')
)
.group_by(tb1.user_id)
)
q2 = (db.session.query(
tb2.user_id.label('u_id'),
func.count(tb2.id).label('tb2_c')
)
.group_by(tb2.user_id)
)
above two queries and I want to apply FULL OUTER JOIN on them.
Since 1.1. sqlalchemy now fully supports FULL OUTER JOINS. See here: https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.join.params.full
So for your code you would want to do:
q1 = (db.session.query(
tb1.user_id.label('u_id'),
func.count(tb1.id).label('tb1_c')
)
.group_by(tb1.user_id)
).cte('q1')
q2 = (db.session.query(
tb2.user_id.label('u_id'),
func.count(tb2.id).label('tb2_c')
)
.group_by(tb2.user_id)
).cte('q2')
result = db.session.query(
func.coalesce(q1.u_id, q2.u_id).label('u_id'),
q1.tb1_c,
q2.tb2_c
).join(
q2,
q1.u_id == q2.u_id,
full=True
)
Note that as with any FULL OUTER JOIN, tb1_c and tb2_c may be null so you might want to apply a coalesce on them.
First of all, sqlalchemy does not support FULL JOIN out of the box, and for some good reasons. So any solution proposed will consist of two parts:
a work-around for missing functionality
sqlalchemy syntax to build a query for that work-around
Now, for the reasons to avoid the FULL JOIN, please read some old blog Better Alternatives to a FULL OUTER JOIN.
From this very blog I will take the idea of how to avoid FULL JOIN by adding 0 values to the missing columns and aggregating (SUM) on UNION ALL intead. SA code might look something like below:
q1 = (session.query(
tb1.user_id.label('u_id'),
func.count(tb1.id).label('tb1_c'),
literal(0).label('tb2_c'), # #NOTE: added 0
).group_by(tb1.user_id))
q2 = (session.query(
tb2.user_id.label('u_id'),
literal(0).label('tb1_c'), # #NOTE: added 0
func.count(tb2.id).label('tb2_c')
).group_by(tb2.user_id))
qt = union_all(q1, q2).alias("united")
qr = select([qt.c.u_id, func.sum(qt.c.tb1_c), func.sum(qt.c.tb2_c)]).group_by(qt.c.u_id)
Having composed the query above, I actually might consider other options:
simply execute those two queries separately and aggregate the results already in Python itself (for not so large results sets)
given that it looks like some kind of reporting functionality rather than business model workflow, create a SQL query and execute it directly via engine. (only if it really is much better performing though)

Categories

Resources