Yesterday, I was working on SQL query which at first look should be very simple and easy to come with. But I was not able to write the solution withouth the help of my more experienced colleague.
I have 2 entity tables (I omit all fields as they are irrevelant to the problem):
bills
| bill_id |
|---|
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
meters
| meter_id |
|---|
| 1 |
| 2 |
| 3 |
and a mapping table bills_meters
| bill_id | meter_id |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 4 | 1 |
| 4 | 2 |
| 4 | 3 |
| 5 | 3 |
| 6 | 1 |
| 6 | 3 |
As you can see from above a bill can have 1…n meters, and I need to write a query to find all bills which are linked to the given meters, e.g.
find all bills for meters with ids 2,3
which should return only bill with id 2 (and not bill 4 as it also linked to meter with id 3 or bill 6 which have meter 3 but not 2).
My current SQL query is a combination of 2 SELECT statement with grouping. It starts with filtering out bills without the required number of meters:
SELECT
bm.bill_id
FROM
bills_meters AS bm
GROUP BY
bm.bill_id
HAVING
COUNT (bm.bill_id) = 2;then filter result for only bills with required meters
SELECT
bm1.bill_id
FROM
bills_meters
AS
bm1
WHERE
bm1.meter_id IN (2, 3)
AND bm1.bill_id IN
(SELECT
bm.bill_id
FROM
bills_meters AS bm
GROUP BY
bm.bill_id
HAVING
COUNT (bm.bill_id) = 2)
GROUP BY bm1.bill_id this will return bills 2 and 6 as both have linked 2 meters and at least one meter from the given collection (IN (2, 3)).
And finally, get only bills which have only both meters
SELECT
bm1.bill_id
FROM
bills_meters
AS
bm1
WHERE
bm1.meter_id IN (2, 3)
AND bm1.bill_id IN
(SELECT
bm.bill_id
FROM
bills_meters AS bm
GROUP BY
bm.bill_id
HAVING
COUNT (bm.bill_id) = 2)
GROUP BY bm1.bill_id
HAVING COUNT(bm1.bill_id) = 2;The query works, I’ve tested it against a larger set of entries, but somehow I think that there might be a better solution to this problem.