Raw SQL to SQLAlchemy - python

Part of my raw sql statement looks like this:
select /*some selects*/
if(/*condition*/, table1.price , if(/*condition*/, t2.price, t3.price)) as price
/*some joins*/
left join table2 t2 on table1.type=t2.id
left join table3 t3 on table1.type=t3.id
This statement works as expected.
SQLAlchemy ORM:
query = db_session.query(Table1,\
func.IF(Table1.field5 == 5, Table1.price,\
func.IF(Table1.new_model == 1, Table2.price, Table3.price))
#+some selects
#+some joins
query = query.join(Table2, Table1.type == Table2.id)\
.join(table3, Table1.type == Table3.id)
And it doesn`t work the same way. It returns the result that only connected to the Table2. And not using this joins in query returns needed rows, but without needed fields from this Table2 and Table3, of course.
What is my mistake?

you need to use outerjoin for LEFT JOIN

LEFT JOIN and JOIN are different operations.
For LEFT JOIN use outerjoin. For JOIN (aka INNER JOIN) use join.

Related

How can I reverse the join order to get a right join with sqlalchemy using a subquery?

I'm trying to create the following (very much simplified) SQL statement with Flask-SQLAlchemy ORM
SELECT TableA.attr
FROM (SELECT DISTINCT TableB.a_id FROM TableB) AS TableB
LEFT JOIN TableA
ON TableA.id = TableB.a_id
To achieve that I used the following SQLAlchemy statements
sq = db.session.query(distinct(TableB.a_id).label('a_id')).subquery()
results = db.session.query(TableA.attr).join(sq, sq.c.a_id==TableA.id, isouter=True).all()
This works however rather than joining my subquery TableB (left) with TableA (right) it does the reverse and joins TableA with TableB.
SELECT TableA.attr
FROM TableA
LEFT JOIN (SELECT DISTINCT TableB.a_id FROM TableB) AS TableB
ON TableB.a_id = TableA.id
Since I understand that SQLAlchemy doesn't have a right join, I'll have to somehow reverse the order while still getting TableA.attr as the result and I can't figure out how to do that with a subquery.
The answer to the question was indeed the select_from() method. I.e. the actual solution to my problem were the following statements:
sq = db.session.query(distinct(TableB.a_id).label('a_id')).subquery()
results = db.session.query(TableA.attr) \
.select_from(sq) \
.join(TableA, TableA.id==sq.c.a_id, isouter=True).all()
In general terms: The select_from() method gets the left part of the join. The join() gets the right part as its first argument.

sqlalchemy limit the results returned from first join and have subsequent joins only join on the limited results

Without running a sub query. Is there a way to limit the results of a particular join in a query with sqlalchemy such that any subsequent joins in the query only join off of those results? For instance i want the first 5 results of the first join, and then join the second table on the results of the first.
I have tried sticking the limit in the middle hoping ORM would understand, but this does not work
result = t1.query.filter_by(
field1="something"
).join(
t2, t1.id == t2.other_id
).limit(5).join(
t3, t2.id == t3.other_id
).add_columns(
t1.title
).all()
This might be a general SQL question as i am new to SQL and dont know if its possible to do this without a subquery.
yes, this can be done with a subquery:
subquery = t1.query.filter_by(field1="something").subquery()
query = t1.outerjoin(
sub_query,
and_(sub_query.c.some_column == t1.some_column)
)

sqlachemy core: how to do inner join and multiple joins

Note this is for the core, not orm.
Hope some one can help me with these 2 questions :
1) There seems to be outerjoin and plain join but how does one do inner join?
2) What is the syntax to do multiple joins. I was able to do one join, but not sure the syntax for multiple joins.
My 1st join which works looks like this :
select([...]).select_from(outerjoin(a, b))
but it generates some errors for this syntax to do two joins :
select([...]).select_from(outerjoin(a, b).select_from(outerjoin(ma, tr))
Thanks in advance.
join does INNER JOIN by default. outerjoin calls join with argument isouter=True.
If our desired sql query is
SELECT a.col1, b.col2, c.col3
FROM a
LEFT JOIN b ON a.col1 = b.col1
LEFT JOIN c ON c.col1 = b.col2
Then the sqlalchemy-core statement should be:
select(
[a.c.col1, b.c.col2, c.c.col3]
).select_from(
a.outerjoin(
b, a.c.col1 == b.c.col1
).outerjoin(
c, b.c.col2 == c.c.col1
)
)
The on clause is not necessary if the relationship has been defined and is not ambiguous.
The outerjoin functions can nested rather than chained (as you have done for the simple join), i.e.
outerjoin(outerjoin(a, b), c)
but I find that form less readable.

Need to retain all columns but getting Duplicate Column Name #1060

I have this large query I am trying to perform. I perform a series of joins, and then from that resulting relation I want to perform another join and filter out certain tuples.
SELECT *
FROM
(
SELECT *
FROM
market_instrument
inner join exchange_instrument
on market_instrument.id = exchange_instrument.instrument_id
inner join Table1 on market_instrument.id = Table1.instrument_id
left join Table2 on market_instrument.id = Table2.instrument_id
left join `options`on market_instrument.id = `options`.instrument_id
left join Table3 on market_instrument.id = Table3.instrument_id
) as R
inner join Table4 on R.instrument_id = Table4.instrument_id
where Table4.fill_timestamp between CURDATE() - INTERVAL 30 DAY AND NOW();
R is the "series of joins" I'm referring to. I want to inner join R with Table4 and then filter out the resulting relation for the last 30 days (where the date attribute is Table4.fill_timestamp). I'm using SQLAlchemy so I thought about somehow saving R to some result relation variable and performing a separate query on that, but I don't know how SQLAlchemy handles that, so I wanted to try doing the entire query in SQL first.
I keep getting the Duplicate Column Name "instrument_id" error. instrument_id is the primary key for all tables except market_instrument, where it's the same but it's called id instead. What can I do to get around this issue?
The problem is that R has all the columns from several tables, and more than one of those tables has a column named "instrument_id". You have not assigned aliases to any of those column names, so SQL does not know which instrument_id column you mean when you say "R.instrument_id".
If market_instrument is the only table with an id column then you could join on R.id instead of R.instrument_id.
Alternatively, another group of solutions involves assigning different names to some or all of the columns in R. For example,
SELECT
market_instrument.*,
exchange_instrument.*,
Table1.instrument_id AS the_one_true_id,
Table1.another_column,
Table1.yet_another_column,
...
Table2.*,
options.*,
Table3.*
FROM
market_instrument
inner join exchange_instrument
on market_instrument.id = exchange_instrument.instrument_id
inner join Table1 on market_instrument.id = Table1.instrument_id
left join Table2 on market_instrument.id = Table2.instrument_id
left join `options`on market_instrument.id = `options`.instrument_id
left join Table3 on market_instrument.id = Table3.instrument_id
With the above, you could then join on R.the_one_true_id. Alternatively, you could leave your current join as it is, and rename all the instrument_id columns but one. It might (or might not) be convenient to do that in the context of replacing R with a full-fledged VIEW in your schema.
Alternatively, your select list could enumerate all the columns of all the tables in the join. That might be tedious, but if you really do need all of them, then you will need to do that to disambiguate the other duplicate names, which include, at least, the various other instrument_id columns. Presented with such a task, however, perhaps you would discover that you don't really need every one of them.
As yet another alternative, you could add more columns instead of renaming existing ones. For example,
SELECT
*
exchange_instrument.instrumentId AS ei_instrument_id,
Table1.instrument_id AS t1_instrument_id,
Table2.instrument_id AS t2_instrument_id,
options.instrument_id AS op_instrument_id,
Table3.instrument_id AS t3_instrument_id
FROM
...
Then you can access, say, R.t1_instrument_id, whose name is presumably unique.

Nested Joins in SQLAlchemy

How do I do nested joins in SQLAlchemy? The statement I'm trying to run is
SELECT a.col1, a.col2, c.col3
FROM a
LEFT OUTER JOIN (b INNER JOIN c ON c.col4 = b.col4) ON b.col5 = a.col5
I need to show all records in A, but join them only with those records in B that can JOIN with C.
The code I have so far is
session.query(a.col1, a.col2, c.col3).outerjoin(b, b.col5 == a.col5).all()
This gets me most of what I need, with A records showing null values where it's missing B records; however, too many Bs are getting in, and I need to limit them. However, if I just add another join, i.e.,
session.query(a.col1, a.col2, c.col3).outerjoin(b, b.col5 == a.col5).join(c, b.col4 == c.col4).all()
It drops out all of the A records with null values in B.
I should point out that I can't join A to C directly because the only connection between the two is through B.
This is done easiest using subquery:
subq = (session.query(b.col5).join(c, c.col4 == b.col4)).subquery("subq")
qry = session.query(a).outerjoin(subq, a.col5 == subq.c.col5)
print(qry)
If you showed more of the model definition and especially the nature of the relationships between the tables, there might be a more elegant solution.

Categories

Resources