If you are finding that your application is taking a long time to provide the result set, perhaps we need more information about your actual desired logic. Using a JOIN then also performing a subquery on each possible row feels expensive.
Maybe you don't actually need every row from A extended by B so long as there is at least one record from A with a matching ad_no and no zeroed time.
Anyhow, I am under the impression that WHERE IN (subquery) is a bad idea. For now, I'll suggest a JOIN subquery.
$sub = $this->db
->distinct()
->select('ad_no')
->where_not_in('staff', ['00:00', '0:00'])
->get_compiled_select('a');
return $this->db
->join('b', 'ad_no')
->join("($sub) x", 'ad_no')
->order_by('b.ctype, b.cname, b.ad_no')
->get('a')
->result();
Renders as:
SELECT *
FROM a
JOIN b USING (ad_no)
JOIN (
SELECT DISTINCT ad_no
FROM a
WHERE staff NOT IN ('00:00', '0:00')
) x USING (ad_no)
ORDER BY b.ctype, b.cname, b.ad_no
where $staff!='00:00' and $staff!='0:00'and why are they with $?