As far as concerns, you want to delete rows in `story_category` that do not exist in `category`.
Here is your original query to identify the rows to delete:
SELECT *
FROM story_category
WHERE category_id NOT IN (
SELECT DISTINCT category.id
FROM category INNER JOIN
story_category ON category_id=category.id
);
Combining `NOT IN` with a subquery that `JOIN`s the original table seems unecessarily convoluted. This can be expressed in a more straight-forward manner with `not exists` and a correlated subquery:
select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id);
Now it is easy to turn this to a `delete` statement:
delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);
This quer would run on any MySQL version, as well as in most other databases that I know.
**[Demo on DB Fiddle](
[To see links please register here]
)**:
-- set-up
create table story_category(category_id int);
create table category (id int);
insert into story_category values (1), (2), (3), (4), (5);
insert into category values (4), (5), (6), (7);
-- your original query to identify offending rows
SELECT *
FROM story_category
WHERE category_id NOT IN (
SELECT DISTINCT category.id
FROM category INNER JOIN
story_category ON category_id=category.id);
<pre>
| category_id |
| ----------: |
| 1 |
| 2 |
| 3 |
</pre>
-- a functionally-equivalent, simpler query for this
select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id)
<pre>
| category_id |
| ----------: |
| 1 |
| 2 |
| 3 |
</pre>
-- the delete query
delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);
-- outcome
select * from story_category;
<pre>
| category_id |
| ----------: |
| 4 |
| 5 |
</pre>