SELECT * FROM destinations;
id | updated_at | created_at | name | image
--------------------------------------+----------------------------+----------------------------+----------------------------------------------+----------------------------------------------------
cf534cd9-56dd-43d5-87bb-713538ec748d | 2023-08-08 23:44:42.822226 | 2023-08-08 23:44:42.822226 | Afghanistan | https://x.cloudfront.net/flags/af.svg
af4e3ac4-e9a6-4c17-8077-664d896f3a7f | 2023-08-08 23:44:42.822226 | 2023-08-08 23:44:42.822226 | Aland Islands | https://x.cloudfront.net/flags/ax.svg
b377b81e-3a0c-4768-84c4-bba67e3bae1b | 2023-08-08 23:44:42.822226 | 2023-08-08 23:44:42.822226 | Albania | https://x.cloudfront.net/flags/al.svg
SELECT * FROM dmc_destinations;
dmc_profile_id | destination_id
--------------------------------------+--------------------------------------
b9ff20c1-1d7a-4a41-a3ac-64345f38814f | b377b81e-3a0c-4768-84c4-bba67e3bae1b
b9ff20c1-1d7a-4a41-a3ac-64345f38814f | ce6a9cea-98e7-41f6-b6a6-cc150fc356e
SELECT "company"."id" AS "company_id"
FROM "companies" "company"
INNER JOIN "dmc_profiles" "dmcProfile" ON "dmcProfile"."company_id" = "company"."id"
INNER JOIN "dmc_destinations" "dmcProfile_destinations" ON "dmcProfile_destinations"."dmc_profile_id" = "dmcProfile"."id"
INNER JOIN "destinations" "destinations" ON "destinations"."id" = "dmcProfile_destinations"."destination_id"
WHERE "company"."type" = 'dmc' AND "destinations"."name" IN ('Algeria', 'Albania')
GROUP BY "company"."id", "dmcProfile"."id", "destinations"."id"
HAVING COUNT(DISTINCT "destinations"."id") = 2;
company_id
------------
(0 rows)
SELECT "company"."id" AS "company_id"
FROM "companies" "company"
INNER JOIN "dmc_profiles" "dmcProfile" ON "dmcProfile"."company_id" = "company"."id"
INNER JOIN "dmc_destinations" "dmcProfile_destinations" ON "dmcProfile_destinations"."dmc_profile_id" = "dmcProfile"."id"
INNER JOIN "destinations" "destinations" ON "destinations"."id" = "dmcProfile_destinations"."destination_id"
WHERE "company"."type" = 'dmc' AND "destinations"."name" IN ('Algeria', 'Albania')
GROUP BY "company"."id", "dmcProfile"."id", "destinations"."id"
HAVING COUNT(DISTINCT "destinations"."id") = 1;
company_id
--------------------------------------
c5223a91-d72d-4ee3-865d-46847a7be90a
c5223a91-d72d-4ee3-865d-46847a7be90a
(2 rows)
I have a company that has profile, and i have destination points that’s related to the profile. So company entity, profile entity with company fk, dmc_destinations as a join table for profile_id and destination_id, destinations as a fixed destination data like name, flagurl, etc.
This is basically filtering, I want to retrieve companies that has both "X" and "Y" countries (not any of them) as a destination point, how do I achieve that? What am I missing here?
Edit: my bad I only give you half the context, this is what typeorm actually generate me:
SELECT "company"."id" AS "company_id",
"company"."updated_at" AS "company_updated_at",
"company"."created_at" AS "company_created_at",
"company"."name" AS "company_name",
"company"."logo_uri" AS "company_logo_uri",
"company"."address" AS "company_address",
"company"."zip_code" AS "company_zip_code",
"company"."type" AS "company_type",
"company"."phone" AS "company_phone",
"company"."website" AS "company_website",
"company"."email" AS "company_email",
"company"."owner_id" AS "company_owner_id",
"company"."host_agency_id" AS "company_host_agency_id",
"company"."city_id" AS "company_city_id",
"company"."country_id" AS "company_country_id",
"company"."state_id" AS "company_state_id",
"company"."collection_id" AS "company_collection_id",
"dmcProfile"."id" AS "dmcProfile_id",
"dmcProfile"."updated_at" AS "dmcProfile_updated_at",
"dmcProfile"."created_at" AS "dmcProfile_created_at",
"dmcProfile"."description" AS "dmcProfile_description",
"dmcProfile"."address" AS "dmcProfile_address",
"dmcProfile"."minimum_spend" AS "dmcProfile_minimum_spend",
"dmcProfile"."years_in_operation" AS "dmcProfile_years_in_operation",
"dmcProfile"."average_response_time" AS "dmcProfile_average_response_time",
"dmcProfile"."facebook" AS "dmcProfile_facebook",
"dmcProfile"."instagram" AS "dmcProfile_instagram",
"dmcProfile"."twitter" AS "dmcProfile_twitter",
"dmcProfile"."youtube" AS "dmcProfile_youtube",
"dmcProfile"."linkedin" AS "dmcProfile_linkedin",
"dmcProfile"."company_id" AS "dmcProfile_company_id",
"destinations"."id" AS "destinations_id",
"destinations"."updated_at" AS "destinations_updated_at",
"destinations"."created_at" AS "destinations_created_at",
"destinations"."name" AS "destinations_name",
"destinations"."image" AS "destinations_image"
FROM "companies" "company"
INNER JOIN "dmc_profiles" "dmcProfile" ON "dmcProfile"."company_id"="company"."id"
INNER JOIN "dmc_destinations" "dmcProfile_destinations" ON "dmcProfile_destinations"."dmc_profile_id"="dmcProfile"."id"
INNER JOIN "destinations" "destinations" ON "destinations"."id"="dmcProfile_destinations"."destination_id"
WHERE "destinations"."name" IN (:...destinations)
GROUP BY "company"."id",
"dmcProfile"."id",
"destinations"."id"
HAVING COUNT(DISTINCT "destinations"."name") = :destinationsCount
from this code:
const qb = this.companyRepository.createQueryBuilder('company');
qb.where('company.type = :type', { type: CompanyTypes.DMC });
qb.innerJoinAndSelect('company.dmcProfile', 'dmcProfile');
if (destinations) {
qb.innerJoinAndSelect('dmcProfile.destinations', 'destinations')
.where('destinations.name IN (:...destinations)', {
destinations,
})
.groupBy('company.id')
.having('COUNT(DISTINCT destinations.name) = :destinationsCount', {
destinationsCount: 2,
});
console.log(qb.getQuery());
}
so I can not really get rid of other group by components.
2
Answers
I’d suggest that if you remove the unnecessary columns from your GROUP BY, then you should see the results.
When you are only outputting the company.id value (and you presumably only want to see the unique company.id values, based on whether they match the criteria of the "HAVING" clause) then there is no need to have any additional fields as part of your GROUP BY (unless you had a very specific reason to do so). By include additional fields in the GROUP BY clause you are ultimately splitting out the rowset that will be evaluated against the "HAVING" clause – which will undoubtedly mean you don’t see the results you expect to see
The following is an approach that can be used to identify rows that satisfy some set of criteria derived across rows from joined relations:
This is not necessarily the best approach, but it has the advantage that criteria are independent. Each condition can be added or removed without requiring changes to other conditions; e.g., adding
AND COUNT(*) FILTER (WHERE destinations.name = 'Angola') > 0
to theHAVING
clause is sufficient to add Angola to the set of required destinations, there was no need to also modify a related comparison to a counter aggregated across the criteria.