Both these tables contain considerable amounts of rows, but over time tableA will end up containing orphaned values (i.e. the a_id is not used in tableB) and this problem cannot be rectified by setting, for example, cascade deletes.
To fix this problem I decided to write a simple stored procedure to purge all values in tableA where its a_id is not used in tableB :
DELETE FROM tableA WHERE a_id NOT IN (SELECT a_id FROM tableB)
Now although the following document relates to postgres :
DELETE a from tableA a left outer join tableB b on a.a_id = a_id left out join tableC c on a.a_id = c.a_id left outer join tableE e on a.a_id = e.a_id where b.a_id is null and c.a_id is null and e.a_id is null
Under most circumstances the SQL Server optimizer will take a NOT EXISTS clause and convert it to a standard JOIN syntax as illustrated by ms_sql_dba, so there would be no effective difference. I don't know if it will do the same for NOT IN, but on complex statements the optimizer may not make this conversion and so it is good practice to use ms_sql_dba's JOIN syntax instead of NOT EXISTS or NOT IN.
If it's not practically useful, then it's practically useless.