Reputation: 2600
Table 1 - User:
ID
Name
1
Jonh
2
Mark
3
King
Table 2 - Book:
ID
user_id
status
...
1
1
1
...
2
1
1
...
3
1
1
...
4
2
1
...
5
1
0
...
6
1
0
...
Code:
$query = User::find();
$query->joinWith('books');
$query->select(['user.*', 'COUNT(book.id) AS booksCount']);
$query->andWhere(['book.status' => 1]); // Problem Here!
$query->groupBy(['user.id']);
$query->orderBy(['booksCount' => SORT_DESC]);
Problem:
The query
is working properly, but it's not returning the user with id = 3
.
If I remove the line $query->andWhere(['book.status' => 1]);
it works fine and return all users.
What should I change to list all users, even those who do not have a related book with status = 1
?
Upvotes: 1
Views: 931
Reputation: 2600
I found the answer:
$query = User::find();
$query->joinWith(['books' => function ($subquery) {
$subquery->onCondition(['book.status' => 1]);
}]);
$query->select(['user.*', 'COUNT(book.id) AS booksCount']);
$query->groupBy(['user.id']);
$query->orderBy(['booksCount' => SORT_DESC]);
Upvotes: 2
Reputation: 2022
Instead of using COUNT(book.id)
, if the status of the book is either 0 or 1, you can use SUM(book.status)
to get the number of books the user has. Then you can remove your WHERE book.status = 1
clause, and it will return all the users with the number of books they have, even in user 3's case where they have 0 books.
The Problem
The real problem is in your where clause. Because WHERE
is processed before grouping and user 3 doesn't have any rows where book.status = 1
, then the user has no rows which are included in the base query. Therefor the user isn't present during/after the grouping.
If you want a pretty good idea of a catch-all case where you can count rows based on a condition, using COUNT(CASE WHEN book.status IS NULL THEN NULL ELSE NULLIF(0,book.status) END)
will also give you the result you're looking for. Because COUNT()
will not count rows where the expression is NULL
, this would allow the book.status
to be -1, 1, 2, and any other number as long as it isn't 0 (or NULL in user 3's case), and still be included in the count.
Upvotes: -1