Использование внутреннего соединения DISTINCT в SQL - PullRequest
34 голосов
/ 02 октября 2008

У меня есть три таблицы, A, B, C, где A - от многих к одному B, а B - от многих к одному. Мне нужен список всех C в A.

Мои таблицы выглядят примерно так: A [id, valueA, lookupB], B [id, valueB, lookupC], C [id, valueC]. Я написал запрос с двумя вложенными SELECT, но мне интересно, можно ли как-нибудь сделать INNER JOIN с DISTINCT.

SELECT valueC
FROM C
INNER JOIN
(
    SELECT DISTINCT lookupC
    FROM B INNER JOIN
    (
        SELECT DISTINCT lookupB
        FROM A
    ) 
    A2 ON B.id = A2.lookupB
) 
B2 ON C.id = B2.lookupC

EDIT: Таблицы довольно большие, A - 500 тыс. Строк, B - 10 тыс. Строк, C - 100 строк, поэтому, если я сделаю базовое внутреннее объединение и в конце использую DISTINCT, в конце будет много ненужной информации, например:

SELECT DISTINCT valueC
FROM 
C INNER JOIN B on C.id = B.lookupB
INNER JOIN A on B.id = A.lookupB

Это очень, очень медленно (величины во много раз медленнее, чем вложенный SELECT, который я делаю выше.

Ответы [ 4 ]

14 голосов
/ 02 октября 2008

Я провел тест на MS SQL 2005, используя следующие таблицы: строки 400K, строки 26K и строки 450.

Расчетный план запроса показал, что базовое внутреннее объединение будет в 3 раза медленнее, чем вложенные подзапросы, однако при фактическом выполнении запроса базовое внутреннее объединение было в два раза быстрее, чем вложенные запросы. Базовое внутреннее объединение потребовало 297мс на очень минимальном серверном оборудовании.

Какую базу данных вы используете и в какое время вы видите? Я думаю, что если вы видите низкую производительность, то это, вероятно, проблема с индексом.

9 голосов
/ 02 октября 2008

Я считаю, что ваши отношения 1: m уже должны неявно создавать РАЗЛИЧНЫЕ СОЕДИНЕНИЯ.

Но, если ваша цель - просто C в каждом A, может быть проще использовать DISTINCT в самом внешнем запросе.

SELECT DISTINCT a.valueA, c.valueC
FROM C
    INNER JOIN B ON B.lookupC = C.id
    INNER JOIN A ON A.lookupB = B.id
ORDER BY a.valueA, c.valueC
3 голосов
/ 02 октября 2008
SELECT DISTINCT C.valueC 
FROM C 
  LEFT JOIN B ON C.id = B.lookupC
  LEFT JOIN A ON B.id = A.lookupB
WHERE C.id IS NOT NULL

Я не вижу веской причины, по которой вы хотите ограничить результирующие наборы A и B, потому что вы хотите получить список всех C, на которые ссылается A. Я сделал отличное для C.valueC, потому что я догадался, что вы хотели уникальный список C.


РЕДАКТИРОВАТЬ : Я согласен с вашим аргументом. Даже если ваше решение выглядит немного вложенным, оно кажется лучшим и быстрым способом использования ваших знаний о данных и сокращения результирующих наборов.

Нет отдельной конструкции соединения, которую вы могли бы использовать, поэтому просто оставайтесь с тем, что у вас уже есть:

0 голосов
/ 02 октября 2008

Это то, что вы имеете в виду?

SELECT DISTINCT C.valueC
FROM 
C
INNER JOIN B ON C.id = B.lookupC
INNER JOIN A ON B.id = A.lookupB
...