简体   繁体   中英

SQLAlchemy Left join WHERE clause being converted to zeros and ones

Howdie do,

I have the following SQL, that I'm converting to SQLAlchemy:

select t1.`order_id`, t1.`status_type`
from `tracking_update` AS t1 LEFT JOIN `tracking_update` AS t2
ON (t1.`order_id` = t2.`order_id` AND t1.`last_updated` < t2.`last_updated`)
where t1.`order_id` = '21757'and t2.`last_updated` IS NULL

The SQL is just returning the latest tracking update for order id 21757. I'm accomplishing this by doing a left join back to the same table. In order to do this, I'm aliasing the table first:

        tUAlias1 = aliased(TrackingUpdate)
        tUalias2 = aliased(TrackingUpdate)

So far, this is what I have for my conversion to SQLAlchemy:

tracking_updates = db.session.query(tUAlias1.order_id, tUAlias1.status_type).\
            outerjoin(tUalias2, (tUAlias1.order_id == tUalias2.order_id) & (tUAlias1.last_updated < tUalias2.last_updated)).\
            filter(and_(tUAlias1.order_id == '21757', tUalias2.last_updated is None))

And this is the result of the SQLAlchemy code that is executed on the server via log:

SELECT tracking_update_1.order_id AS tracking_update_1_order_id, tracking_update_1.status_type AS tracking_update_1_status_type 
FROM tracking_update AS tracking_update_1 LEFT OUTER JOIN tracking_update AS tracking_update_2 ON tracking_update_1.order_id = tracking_update_2.order_id AND tracking_update_1.last_updated < tracking_update_2.last_updated 
WHERE 0 = 1

As you can see, the filter(WHERE clause) is now 0 = 1.

Now, if I remove the and_ statement and try two filters like so:

tracking_updates = db.session.query(tUAlias1.order_id, tUAlias1.status_type).\
            outerjoin(tUalias2, (tUAlias1.order_id == tUalias2.order_id) & (tUAlias1.last_updated < tUalias2.last_updated)).\
            filter(tUAlias1.order_id == '21757').filter(tUalias2.last_updated is None)

I receive the same result. I know the SQL itself is fine as I can run it with no issue via MySQL workbench.

When SQL run directly, I will receive the following

order ID | Status
21757      D

Also, if I remove the tUalias2.last_updated is None, I actually receive some results, but they are not correct. This is the SQL Log for that:

Python code

tracking_updates = db.session.query(tUAlias1.order_id, tUAlias1.status_type).\
            outerjoin(tUalias2, (tUAlias1.order_id == tUalias2.order_id) & (tUAlias1.last_updated < tUalias2.last_updated)).\
            filter(tUAlias1.order_id == '21757')

SQLAlchemy run:

SELECT tracking_update_1.order_id AS tracking_update_1_order_id, tracking_update_1.status_type AS tracking_update_1_status_type 
FROM tracking_update AS tracking_update_1 LEFT OUTER JOIN tracking_update AS tracking_update_2 ON tracking_update_1.order_id = tracking_update_2.order_id AND tracking_update_1.last_updated < tracking_update_2.last_updated 
WHERE tracking_update_1.order_id = '21757'

Any ideas?

Howdie do,

I figured it out

The Python 'is' operator doesn't play nice with SQLAlchemy

I found this out thanks to the following S/O question:

Selecting Null values SQLAlchemy

I've since updated my query to the following:

tracking_updates = db.session.query(tUAlias1.order_id, tUAlias1.status_type).\
            outerjoin(tUalias2, (tUAlias1.order_id == tUalias2.order_id) & (tUAlias1.last_updated < tUalias2.last_updated)).\
            filter(tUAlias1.order_id == '21757').filter(tUalias2.last_updated == None)

The problem is not in how SqlAlchemy processes null values, the problem is that you use an operator which is not supported for instrumented' columns and thus the expression tUalias2.last_updated is None evaluates to a value (False), which is then translated to either and 0=1 . You should write . You should write tUalias2.last_updated.is_(None) instead of tUalias2.last_updated is None` to make your code work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM