Как я могу выбрать имена всех групп, к которым принадлежит человек, используя JOIN вместо IN? - PullRequest
2 голосов
/ 19 февраля 2010

Рассмотрим следующий упрощенный пример:

CREATE TABLE groups ( gid INTEGER PRIMARY KEY, name VARCHAR(100) );

CREATE TABLE people ( pid INTEGER PRIMARY KEY );

CREATE TABLE people_groups (
    gid INTEGER NOT NULL
        CONSTRAINT fk_people_groups_group
        REFERENCES groups(gid),
    pid INTEGER NOT NULL
        CONSTRAINT fk_people_groups_person
        REFERENCES people(pid),
    CONSTRAINT pk_people_groups PRIMARY KEY (gid, pid)
);

INSERT INTO people (pid) VALUES (1);
INSERT INTO groups (gid, name) VALUES (1, 'One');
INSERT INTO groups (gid, name) VALUES (2, 'Two');
INSERT INTO people_groups (gid, pid) VALUES (1,1);
INSERT INTO people_groups (gid, pid) VALUES (2,1);

SELECT gid, name FROM groups WHERE gid IN (
    SELECT gid FROM people_groups WHERE pid = 1
);

Это выводит:

1|One
2|Two

Что является правильным JOIN для этого последнего SELECT?

Ответы [ 6 ]

3 голосов
/ 19 февраля 2010

Это эквивалент:

SELECT g.gid, g.name 
FROM groups g 
  INNER JOIN people_groups pg
  ON g.gid = pg.gid
WHERE pg.pid = 1
3 голосов
/ 19 февраля 2010
SELECT g.gid, g.name 
  FROM groups g INNER JOIN people_groups pg
       ON g.gid = pg.gid
 WHERE pg.pid = 1

Примечание. Это эквивалентно вашему утверждению IN, поскольку вы специально фильтруете только одного человека. Если вы фильтруете по нескольким людям, все будет иначе. Например, предположим, что какой-то человек с pid=2 также находится в группе 1, и вы делаете следующее ВЫБОР:

SELECT g.gid, g.name 
  FROM groups g INNER JOIN people_groups pg
       ON g.gid = pg.gid
 WHERE pg.pid IN (1, 2)

Это вернет группу 1 дважды (в отличие от вашего решения IN, которое вернет каждую группу только один раз). Чтобы решить эту проблему, вам нужно добавить ключевое слово DISTINCT после SELECT или add GROUP BY g.gid, g.name в самом конце SQL. Следует помнить об этом, если вы используете этот ответ в качестве общего правила для преобразования IN в JOIN.

2 голосов
/ 19 февраля 2010
SELECT gid, name FROM groups 
NATURAL JOIN people_groups 
WHERE pid = 1;
1 голос
/ 19 февраля 2010

Ответы, данные ранее, верны, но я хотел бы отметить, что ваш запрос выиграет от выполнения PRIMARY KEY на (pid, gid) (в этом порядке).

Этот запрос:

SELECT  g.gid
FROM    people_groups pg
JOIN    groups g
ON      g.gid = pg.gid
WHERE   pd.pid = 1

сможет затем сделать people_groups лидирующим в JOIN, что, скорее всего, сделает его намного быстрее, поскольку pid кажется очень избирательным в таблице ссылок.

В качестве альтернативы вы можете создать вторичный индекс:

CREATE UNIQUE INDEX ux_peoplegroup_p_g ON people_group (pid, gid)

или просто

CREATE INDEX ix_peoplegroup_p ON people_group (pid)

, если people_groups равно InnoDB.

1 голос
/ 19 февраля 2010

Как насчет:

SELECT g.gid, g.name
  FROM groups AS g JOIN people_groups AS p ON g.gid = p.gid
 WHERE p.pid = 1;
0 голосов
/ 19 февраля 2010
SELECT g.gid, g.name FROM groups g, people_groups pg 
WHERE g.gid = pg.gid and pg.pid = 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...