MySQL implements an extension to standard SQL that lets you refer to an alias in the HAVING
clause:
SELECT
`id`, `firstName`, `lastName`, `nickName`, `email`,
`verificationCode`, `roleId`, `profileImageId`, `statusId`, `createdAt`,
(SELECT COUNT(*) FROM RecipeStores rs WHERE rs.userId = u.id) AS `countStoredRecipes`,
(SELECT COUNT(*) FROM RecipeFavorites rf WHERE rf.userId = u.id) AS `countLikedRecipes`,
(SELECT COUNT(*) FROM Recipes r WHERE r.createdById = u.id) AS `countCreatedRecipes`
FROM `Users` u
HAVING `User`.`countStoredRecipes` >= 2;
I am not a big fan of this technique: this really is bespoke MySQL syntax, that wouldn't run in any other database.
A more standard option is to turn the existing query to a subquery, and filter in the outer scope:
SELECT *
FROM (
SELECT
`id`, `firstName`, `lastName`, `nickName`, `email`,
`verificationCode`, `roleId`, `profileImageId`, `statusId`, `createdAt`,
(SELECT COUNT(*) FROM RecipeStores rs WHERE rs.userId = u.id) AS `countStoredRecipes`,
(SELECT COUNT(*) FROM RecipeFavorites rf WHERE rf.userId = u.id) AS `countLikedRecipes`,
(SELECT COUNT(*) FROM Recipes r WHERE r.createdById = u.id) AS `countCreatedRecipes`
FROM `Users` u
) t
WHERE `countStoredRecipes` >= 2;
In very recent versions of MySQL, you can use lateral joins:
SELECT
`id`, `firstName`, `lastName`, `nickName`, `email`,
`verificationCode`, `roleId`, `profileImageId`, `statusId`, `createdAt`,
rs.*, rf.*, r.*
() AS
FROM `Users` u
INNER JOIN LATERAL (
SELECT COUNT(*) AS `countStoredRecipes`
FROM RecipeStores rs
WHERE rs.userId = u.id
) rs ON rs.`countStoredRecipes`>= 2
LEFT JOIN LATERAL (
SELECT COUNT(*) AS `countLikedRecipes`
FROM RecipeFavorites rf
WHERE rf.userId = u.id
) rf ON true
LEFT JOIN LATERAL (
SELECT COUNT(*) AS `countCreatedRecipes`
FROM Recipes r
WHERE r.createdById = u.id
) r ON true