If you have a large number of users to delete, you might consder using the wp user delete wp-cli command to avoid script timeouts.
Here's an example of a SQL query to select all users without a post.
You can therefore try this untested one-liner:
wp user delete $(wp db query "SELECT * FROM wp_users WHERE ID NOT IN (SELECT DISTINCT post_author FROM wp_posts )" AND ID NOT IN (1,2,3)" ) --reassign=1
or in expanded form:
wp user delete $(wp db query
"SELECT ID
FROM wp_users
WHERE ID NOT IN (
SELECT DISTINCT post_author FROM wp_posts
) AND ID NOT IN (1,2,3)"
) --reassign=1
Note that we added an extra AND ID NOT IN (1,2,3)
restriction to make sure these users are not deleted (e.g. admin users). You will have to adjust it to your needs and also the table prefix wp_
.
Here we reassign all posts to user 1, to avoid the notice:
--reassign parameter not passed. All associated posts will be deleted. Proceed? [y/n]
Hope you can adjust it further to your needs, like relaxing the user delete conditions by adding WHERE post_status = 'publish'
.
Note: Remember to backup befor testing!