All three encode the same intent. LEFT-IS-NULL builds the full join then keeps the partnerless rows — readable, and the joined columns are there if you want them; its risk is someone later filtering a right column in WHERE (the P05 flagship). NOT EXISTS asks per-row 'any partner?' — NULL-safe by construction, the planner's favorite, and it can't fan out. NOT IN reads best and behaves worst: one NULL in the list and the whole result is empty (the P01 flagship).
SELECT sid FROM students WHERE sid NOT IN ( SELECT student_id FROM assign ) ORDER BY sid;
SELECT sid FROM students s WHERE NOT EXISTS ( SELECT 1 FROM assign a WHERE a.student_id = s.sid ) ORDER BY sid;
The NOT IN spelling returned empty — one NULL student_id in the assignment list makes NOT IN never TRUE, where LEFT...IS NULL and NOT EXISTS both correctly list the unassigned students.
Default to NOT EXISTS; LEFT...IS NULL when you also need right-side columns; NOT IN only for provably-non-null lists, with a comment.