Twoje zapytanie jest prawie prawidłowe i jest to właściwy sposób (i najbardziej efektywny)
SELECT books.*, count(orders.book_id) as number_of_orders
from books
left join orders
on (books.book_id = orders.book_id)
group by
books.book_id
COUNT(*)
może zawierać wartości NULL w liczniku, ponieważ zlicza wszystkie wiersze, podczas gdy COUNT(orders.book_id)
nie, ponieważ ignoruje wartości NULL w danym polu.