The discrepancy is either soft-deleted posts (they aren't removed from the database, so will be included in your query), or posts in forums set to not count towards users' post count.
You can exclude soft-deleted (and moderated) posts from your query with this condition: AND xf_post.message_state = 'visible'
.