2

I am facing some issues retrieving data using SQL in one specific scenario:

Suppose I have the following tables:

  • A (id, attr_a, attr_b);
  • B (id, attr_d, attr_e);
  • C (id_a, id_b);

As you can see, table C have FK referencing id from table A, and referencing id from table C.

I need to retrieve table A rows that for each A row relates to every B row.

In a real life scenario, assuming A means users, B means privileges, and C is the many to many entity that relates users with privileges, I would want to get only users that have ALL privileges

joop
  • 4,330
  • 1
  • 15
  • 26
Rogger Fernandes
  • 805
  • 4
  • 14
  • 28

5 Answers5

2
select A.*
from A
join C on id_a = id
group by id
having count(id) = (select count(*) from B)

There is no need to use a subquery because A.id is a primary key (or unique at least) as a column referenced by C.id_a.

klin
  • 112,967
  • 15
  • 204
  • 232
1

The following query should return all A records where, for a given ID, they match to every record in the B table. Note that a subquery is necessary if you want to return every full record in A.

SELECT t1.*
FROM A t1
INNER JOIN
(
    SELECT A.id
    FROM A
    INNER JOIN C
        ON A.id = C.id_a
    GROUP BY A.id
    HAVING COUNT(*) = (SELECT COUNT(*) FROM B)
) t2
    ON t1.id = t2.id
Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
1

Assuming referential integrity enforced by FK constraints, all key columns NOT NULL and a UNIQUE or PK constraint on (id_a, id_b) in C.

If all you need is IDs, only work with table C. Don't waste time joining to A:

SELECT id_a
FROM   C
GROUP  BY 1
HAVING count(*) = (SELECT count(*) FROM B);

If you need columns or whole rows from A, join to it after aggregating and eliminating non-qualifying rows. Should be fastest.

SELECT A.*
FROM  (
   SELECT id_a AS id
   FROM   C
   GROUP  BY 1
   HAVING count(*) = (SELECT count(*) FROM B)
   ) c
JOIN   A USING (id);
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
0

Seems like you really only need table C if what you want is a list of users that have every privilege. One way to this is with CONCAT which looks at the pairing between C.id_a and C.id_b as one string:

SELECT C.id_a
 FROM C
 GROUP BY C.id_a
 HAVING COUNT(DISTINCT CONCAT(C.id_a, C.id_b)) =
 (SELECT COUNT(DISTINCT C.id_b)
  FROM C)

Tested here: http://sqlfiddle.com/#!9/f92a54/3

kjmerf
  • 4,275
  • 3
  • 21
  • 29
0

You don't need to count, you only need to check for (non)existant rows:

SELECT *
FROM A
WHERE NOT EXISTS (
        SELECT * FROM B
        WHERE NOT EXISTS (
                SELECT * FROM C
                WHERE C.id_a = A.id AND C.id_b = B.id
                )
        );
joop
  • 4,330
  • 1
  • 15
  • 26