SQL: How to find all attachments that are not used in any posts/pages/custom_post_types

What you seek isn’t truly possible. You can get sort of close, but you cannot definitively say if an attachment is or isn’t in use.

We can get somewhere close to a solution, but to do this we will need to process each attachment individually.

So first, get a list of all the IDs of attachments. Use a standard WP_Query to do this, you might even get a speed boost from object cache as a result.

Here’s the official date query docs:

https://developer.wordpress.org/reference/classes/wp_query/#date-parameters

For each attachment:

  • Check all post content and post meta for the attachments URL, with the image size removed
  • Check all options and post meta for the attachments ID
  • Check if the attachment has a post parent
  • If any of these are true, the attachment is probably in use

Doing every attachment in a single go will be problematic, it won’t be possible to run the query from a browser due to the PHP execution limit. You’d need to do attachments in batches, or via a CLI command. Preferably in batches of 100. PHP will run out of memory if you try to process all 47k at once.

However, the most effective means of testing if an attachment is in use, is to spider the entire site and save the results locally, then search the folder for the URL. Note that this won’t catch things that only show once forms submit, or RSS specific stuff, things that only show to logged in users, etc