Why is Postgres CTE slower than subquery?
CTE's in PostgreSQL are an optimization fence. That means the query planner doesn't push optimizations across a CTE boundary.


Blog entry about this


I think a lot of this is silly though you can just write it like this.. Here we use CROSS JOIN LATERAL rather than the complex wrapping and NOT EXISTS rather than NOT IN

SELECT word,
  _created_at
FROM nps_reports
CROSS JOIN LATERAL unnest(regexp_split_to_array(
  nps_reports.comment,
  '[^a-zA-Z0-9]+'
)) AS word
WHERE nps_reports.comment &lt;&gt; 'undefined'
  AND nps_reports.comment IS NOT NULL
  AND NOT EXISTS (
    SELECT 1
    FROM stop_words
    WHERE stop_words.stop_word = word
  )
ORDER BY _created_at DESC;


All of that said, whatever you're doing seems to be reinventing FTS. So that's also a bad idea.