Salesforce uses some confusing terminology around this topic. For clarity... - An aggregate query (as far as governor limits and error messages seem to go) is a parent-child subquery (a.k.a. a "left outer join" in more standard SQL) - An aggregate function is something like `COUNT()`, `MAX()`, `AVG()`, etc... Aggregate functions can only be applied on the outermost query, and most (if not all) require you to also use `GROUP BY`. This causes the query to return a `List<AggregateResult>` rather than a `List<SObject>`. Both parent-child subqueries and aggregate functions have their uses. In the particular example that you've given, the advantage of using `COUNT()` is that you don't need to worry about the number of records returned. It'll get you the number you're looking for with minimal fuss. With a parent-child subquery, however, if you have enough child records you won't be able to simply call `parent.children.size()`. At a certain point, Salesforce will try to call `queryMore` (internally) and you'll be greeted with an error message telling you to iterate over the child records. That means that your simple `parent.children.size()` becomes (pseudocode) ```java for(parent :[parent query]){ List<child> childList; for(child :parent.children){ childList.add(child); } childList.size(); } ``` So in this case, the aggregate functions end up being less work (SOQL does more of the work for you). Things like doing your own rollups are a good use case for aggregate functions as well. In other cases, the parent-child subquery approach may make the rest of your code simpler. At the end of the day, it's about doing whatever makes your life easier. If there's a reasonable way to foist some of the work onto SOQL (or another tool/feature) that would normally be done via apex, it's worth looking into.