Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support Postgresql / MySQL DELETE...USING [LEFT OUTER] JOIN #8130

Open
tgross35 opened this issue Jun 13, 2022 · 11 comments
Open

support Postgresql / MySQL DELETE...USING [LEFT OUTER] JOIN #8130

tgross35 opened this issue Jun 13, 2022 · 11 comments
Labels
mysql postgresql PRs (with tests!) welcome a fix or feature which is appropriate to be implemented by volunteers sql use case not really a feature or a bug; can be support for new DB features or user use cases not anticipated
Milestone

Comments

@tgross35
Copy link
Contributor

tgross35 commented Jun 13, 2022

Describe the use case

It doesn't currently seem possible to add a .join() to a delete() object for multi-table deletes. Currently, the following is possible (implicit inner join):

q=delete(TableA).filter(
            TableA.something == 'something',
            TableA.b_id == TableB.id,
            TableB.something == 'abcd',
        ).execution_options(synchronize_session="fetch")
q.stringify_dialect = 'mysql'
str(q)
DELETE FROM table_a USING table_a, table_b
WHERE 'something' = table_a.something
AND table_a.table_b_id = table_b.id
AND table_b.something = 'abcd'

But it would be convenient to be able to specify the join condition using an ORM relationship, and use the other types of joins possible (left).

Databases / Backends / Drivers targeted

This at least works in mysql and mariadb, probably some others (unsure which)

Good examples are here: https://www.mysqltutorial.org/mysql-delete-join/

Example Use

Rough example (untested)

q=delete(TableA).join(TableA.b_relationship)filter(
            TableA.something == 'something',
            TableB.something == 'abcd',
        ).execution_options(synchronize_session="fetch")
DELETE TableA 
FROM TableA
JOIN TableB ON TableA .b_id = TableB.id
WHERE
    TableA.something = 'something'
AND
    TableB.something = 'abcd';

Additional context

It is also possible to join on UPDATE - I am unsure what the current state of support for that is. See here: https://www.mysqltutorial.org/mysql-update-join/

Relevant current doc location: https://docs.sqlalchemy.org/en/14/core/tutorial.html#multiple-table-deletes

@tgross35 tgross35 added requires triage New issue that requires categorization use case not really a feature or a bug; can be support for new DB features or user use cases not anticipated labels Jun 13, 2022
@zzzeek zzzeek changed the title Allow specifying a relationship-based JOIN on delete() structures support MySQL DELETE...JOIN syntax Jun 13, 2022
@zzzeek zzzeek added mysql sql and removed requires triage New issue that requires categorization labels Jun 13, 2022
@zzzeek zzzeek added this to the 2.x.x milestone Jun 13, 2022
@zzzeek
Copy link
Member

zzzeek commented Jun 13, 2022

we support MySQLs join() for UPDATE, for DELETE it does not seem we have specific tests.

this would be a Core use case, not ORM. the way it works is to pass join() directly to delete(). the relationship part you can get using orm.join().

from sqlalchemy.orm import join

stmt = delete(join(ModelA, ModelB, ModelA.bs)) ...

right now it comes out like

DELETE FROM a INNER JOIN b ON a.id = b.a_id USING a INNER JOIN b ON a.id = b.a_id, b

also it would be nice if both the update() and the delete() cases had some docs at https://docs.sqlalchemy.org/en/14/dialects/mysql.html#mysql-mariadb-sql-extensions

@zzzeek
Copy link
Member

zzzeek commented Jun 13, 2022

can that JOIN be in the USING clause ?

@CaselIT
Copy link
Member

CaselIT commented Jun 13, 2022

Pg supports this also, but only on the using https://www.postgresql.org/docs/current/sql-delete.html

@tgross35
Copy link
Contributor Author

tgross35 commented Jun 13, 2022

Looks like the join condition can be after USING, this works for me

DELETE FROM T1 USING T1 LEFT JOIN T2
ON T1.id = T2.t1_id
WHERE T2.value = 'deleteme';

Per the docs, either of these are allowable and I think equivalent. The join just goes in table_references

DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
    tbl_name[.*] [, tbl_name[.*]] ...
    FROM table_references
    [WHERE where_condition]
DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
    FROM tbl_name[.*] [, tbl_name[.*]] ...
    USING table_references
    [WHERE where_condition]

Edit: for completeness, the following works too

DELETE T1 FROM T1 LEFT JOIN T2
ON T1.id = T2.t1_id
WHERE T2.value = 'deleteme';

Kind of annoying how USING makes FROM change its purpose completely

@CaselIT
Copy link
Member

CaselIT commented Jun 13, 2022

Pg supports this also, but only on the using https://www.postgresql.org/docs/current/sql-delete.html

But on pg side it seems that it can be used only to filter the rows. It does not lalow deleting from multiple tables, so probably not the same functionality

@tgross35
Copy link
Contributor Author

from sqlalchemy.orm import join

stmt = delete(join(ModelA, ModelB, ModelA.bs)) ...

Does this distinguish between deleting from only ModelA and deleting from both ModelA and ModelB? Or am I missing context

@zzzeek
Copy link
Member

zzzeek commented Jun 13, 2022

well for DELETE it's whatever the database does I guess? we just print a string

@tgross35
Copy link
Contributor Author

Only from ModelA as-is (though the rendered sql syntax you sent is broken of course), I guess I was moreso wondering if there would be a way to specify a delete from both tables. Assuming that the python syntax is what the goal is, wasn't sure if the goal is to make that work or if that's just an existing workaround until better syntax could be added.

@iTrooz
Copy link

iTrooz commented Mar 12, 2023

stmt = delete(TableA).join(TableB).filter(
    TableA.something == 'something',
    TableB.something == 'abcd'
)

Hey, is the following syntax being worked on ? If not, I would like to pick it up, would you accept a PR ?

@zzzeek
Copy link
Member

zzzeek commented Mar 12, 2023

yes we accept PRs, in this case it has to be pretty complete with good compile and round trip tests etc. see test/sql/test_delete.py for where to go with this.

@zzzeek zzzeek added the PRs (with tests!) welcome a fix or feature which is appropriate to be implemented by volunteers label Mar 12, 2023
@zzzeek
Copy link
Member

zzzeek commented Aug 3, 2023

it would be nice if delete() had a using() construct directly, that accepts a join(). delete().join() itself would get pretty involved with all the ORM use cases

@zzzeek zzzeek changed the title support MySQL DELETE...JOIN syntax support Postgresql / MySQL DELETE...USING [LEFT OUTER] JOIN Aug 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mysql postgresql PRs (with tests!) welcome a fix or feature which is appropriate to be implemented by volunteers sql use case not really a feature or a bug; can be support for new DB features or user use cases not anticipated
Projects
None yet
Development

No branches or pull requests

4 participants