60

I am switching a database from MySQL to Postgres SQL. A select query that worked in MySQL works in Postgres but a similar delete query does not.

I have two tables of data which list where certain back-up files are located. Existing data (ed) and new data (nd). This syntax will pick out existing data which might state where a file is located in the existing data table, matching it against equal filename and path, but no information as to where it is located in the new data:

SELECT ed.id, ed.file_name, ed.cd_name, ed.path, nd.cd_name
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND 
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;

I wish to run a delete query using this syntax:

DELETE ed
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND 
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;

I have tried DELETE ed and DELETE ed.* both of which render syntax error at or near "ed". Similar errors if I try without the alias of ed. If I attempt

DELETE FROM tv_episodes AS ed
LEFT  JOIN data AS nd.....

Postgres sends back syntax error at or near "LEFT".

I'm stumped and can't find much on delete queries using joins specific to psql.

2
  • 1
    possible duplicate of PostgreSQL: how to delete rows using a join Commented Jul 22, 2014 at 14:30
  • 1
    Worth checking out the question linked by @cimmanon (regardless of whether it's a duplicate or not) - the NOT EXISTS syntax works better for me in my situation than the answers here. Commented Jun 27, 2018 at 0:21

4 Answers 4

124

As others have noted, you can't LEFT JOIN directly in a DELETE statement. You can, however, self join on a primary key to the target table with a USING statement, then left join against that self-joined table.

Note the self join on tv_episodes.id in the WHERE clause. This avoids the sub-query route provided above.

DELETE FROM tv_episodes
USING tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
   ed.file_name = nd.file_name AND 
   ed.path = nd.path
WHERE
   tv_episodes.id = ed.id AND
   ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;
Sign up to request clarification or add additional context in comments.

6 Comments

thanks for this. this works well in my case with a couple tables of ~100K rows. The "total cost" given by explain for the subquery methods suggested in the other answers was 337448542.97 (!) and took over 9 minutes. This method had a cost of 40464.59 and took 6.7 seconds.
Do you think you should provide the self join condition if there was just one join in using... say if it was just file_name that we are joining with?
I missed your "note" and accidentally deleted everything from the table on production website :( it's really non-obvious especially for users coming from MySQL or SQL Server
This is weird example. data table should be in USING clause, and no left join at all
Thanks, this fixed my issue. Kind of makes a mockery of having the 'USING' syntax in the first place though, I find the "DELETE table FROM table LEFT JOIN..." syntax of, say, mysql, allot more convenient.
|
15

As bf2020 points out, postgres does not support JOINs when conducting a DELETE query. The proposed solution of a sub-query made me think of the solution. Refine the SELECT query from above and employ it as a sub-query to a DELETE query statement:

DELETE FROM tv_episodes 
WHERE id in (
    SELECT ed.id
    FROM tv_episodes AS ed
    LEFT OUTER JOIN data AS nd ON
    ed.file_name = nd.file_name AND 
    ed.path = nd.path
    WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL
);

Sub-queries can often be inefficient consuming time and CPU resources with some database systems, especially MySQL. From my experience I try to avoid using a sub-query due to that inefficiency plus that such queries are sometimes an easy way out to honing one's skill like learning JOIN syntax.

Since postgre does not permit delete queries using join, the above is the solution that works.

2 Comments

Postgres does not support JOINs when conducting a DELETE query. Nonsense, it does, though the syntax is weird. DELETE FROM ... USING .... See the manual. However, it doesn't support left-joining against the table to be deleted from, that's true.
@dwlamb - if you aren't accepting my answer you should upvote it because it put you on the right track. meta.stackexchange.com/questions/5234/…
5

Use the DELETE... USING syntax:

DELETE FROM tv_episodes USING data WHERE 
tv_episodes.file_name = data.file_name AND 
tv_episodes.path = data.path AND 
tv_episodes.cd_name = 'MediaLibraryDrive' AND 
data.cd_name IS NULL;

1 Comment

In my case, I had to do the self join that Joshua Burgner proposed to avoid deleting everything. Luckily I tried it in a testing database first.
3

Instead of

DELETE ed
FROM tv_episodes AS ed
LEFT OUTER JOIN data AS nd ON
ed.file_name = nd.file_name AND 
ed.path = nd.path
WHERE ed.cd_name = 'MediaLibraryDrive' AND nd.cd_name IS NULL;

please try

DELETE FROM tv_episodes
WHERE cd_name = 'MediaLibraryDrive' AND 
  (tv_episodes.filename, tv_episodes.path IN
    (SELECT ed.filename, 
    ed.path 
    FROM tv_episodes AS ed 
    INNER JOIN data AS nd 
      ON ed.file_name = nd.file_name 
        AND ed.path = nd.path
    WHERE nd.cd_name IS NULL)
  )
  ;

JOIN is not valid in a DELETE query according to the postgresql documentation. You might need to concatenate the left and right parts of the IN expression.

2 Comments

There were some syntax errors in your solution (a comma before FROM and clear declaration of the JOIN on specific fields). I only note these for someone else that comes across this challenge looking for a solution. Kindly see the above revision of my dilemma above. Your advice gave me the solution.
For PostgreSQL a joined delete is done with DELETE FROM ... USING ..., but there's currently no way to express a left outer join against the delete target table AFAIK.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.