Some points
As @swasheck mentioned, you can't have a condition like WHERE a = b = c in SQL, it's not valid (unlike other languages). You need to make it
WHERE a = b AND b = c
Using implict joins with WHERE is not good practise any more, 25 years since SQL-92 standards adopted the JOIN syntax (a JOIN b ON <condition>), which has several advantages and should be preferred. One reason is that there are several types of joins available (and all queries except those that use only INNER joins are hard to write using the WHERE syntax):
INNER JOIN or just JOIN
this is the most common type of join, combines rows from the two joined tables when they match the ON condition.
LEFT OUTER JOIN or just LEFT JOIN
very common, too: gets all combinations of INNER JOIN plus all unmatched rows of the left table.
RIGHT OUTER JOIN or just RIGHT JOIN
(not so common) the reverse of LEFT join: gets all combinations of INNER JOIN plus all unmatched rows of the right table.
FULL OUTER JOIN or just FULL JOIN
this is LEFT and RIGHT join, combined.
CROSS JOIN
NATURAL JOIN and variations (not supported by SQL-Server)
There are many references and tutorials on the Web about Joins. You can start with MSDN online documentation.
You also need to study how COUNT() works:
COUNT(*) counts the number of rows (of a group).
COUNT(column/expression) counts the number of rows (of a group) where the column or expression is not null. If the column cannot be NULL, this is the same as COUNT(*)
COUNT(DISTINCT column/expression) counts the number of distinct values of the column or expression (within a group).
And here are a few ways to write your query:
Option 1 - inline subqueries:
SELECT
Person.Name,
( SELECT COUNT(*)
FROM Book
WHERE PersonID = Person.ID
) AS BookCount,
( SELECT COUNT(*)
FROM Car
WHERE PersonID = Person.ID
) AS CarCount
FROM
Person ;
Option 2 - two LEFT Joins, then GROUP BY and use of COUNT(DISTINCT):
This is very similar to your approach, but has the implicit joins with WHERE turned into explicit joins.
The GROUP BY p.ID, p.Name was added, too, so the query can group rows per Person.
We have to use the COUNT(DISTINCT) in this version because the two joins may produce multiple rows per Person. (If a person has 2 Cars and 500 books, 1000 rows will be produced and then collapsed into 1 with the grouping. You can try with COUNT(*) there to see what (erroneous) results are produced.)
SELECT
p.Name,
COUNT(DISTINCT b.BookID) AS BookCount,
COUNT(DISTINCT c.CarID) AS CarCount
FROM
Person AS p
LEFT JOIN
Book AS b
ON b.PersonID = p.ID
LEFT JOIN
Car AS c
ON c.PersonID = p.ID
GROUP BY
p.ID, p.Name ;
Option 3 (my preference) - two LEFT Joins to (derived) GROUP BY subqueries:
SELECT
p.Name,
COALESCE(BookCount, 0) AS BookCount, --- using COALESCE() so the NULLs produced
COALESCE(CarCount, 0) AS CarCount --- by the (LEFT) outer joins for persons
--- that have no car or no book (shame!)
--- are turned into 0
FROM
Person AS p
LEFT JOIN
( SELECT PersonID
, COUNT(*) AS BookCount,
FROM Book
GROUP BY PersonID
) AS b
ON b.PersonID = p.ID
LEFT JOIN
( SELECT PersonID
, COUNT(*) AS CarCount,
FROM Car
GROUP BY PersonID
) AS c
ON c.PersonID = p.ID ;
Option 4 - In SQL Server, there is also the option of using OUTER APPLY to (derived) GROUP BY subqueries. This is similar to LEFT joins but has even more flexibility, which can be very useful in more complex cases. (in other DBMS like PostgreSQL and DB2, the same functionality exists as well, with LATERAL joins).
Notice how the ON conditions from option 3 have been moved to WHERE, inside the outer apply subqueries:
SELECT
p.Name,
COALESCE(BookCount, 0) AS BookCount, --- using COALESCE() so the NULLs produced
COALESCE(CarCount, 0) AS CarCount --- by the (OUTER APLY) joins for persons
--- that have no car or no book (shame!)
--- are turned into 0
FROM
Person AS p
OUTER APPLY
( SELECT PersonID
, COUNT(*) AS BookCount,
FROM Book
WHERE PersonID = p.ID
GROUP BY PersonID
) AS b
OUTER APPLY
( SELECT PersonID
, COUNT(*) AS CarCount,
FROM Car
WHERE PersonID = p.ID
GROUP BY PersonID
) AS c ;
All 4 queries will give same results - all Persons and the Count of their Books and Cars, even if they have no book or no car. If you want to show only Person that have at least one Book or at least one Car (or both), options 2, 3 and 4 can be easily modified: just change the respective LEFT OUTER JOIN (or both of them) to INNER JOIN - and the OUTER APPLY to CROSS APPLY.
If the problem involves calculating other aggregates, like MAX(), SUM(), etc., then the options are essentially the same - but note that option 2 can be used with MIN and MAX but not with SUM or AVG. Option 1 can be used with any aggregate function but needs a separate subquery for each aggregate, so if one needs many aggregates from the same table, options 3 and 4 are preferable.