Нужна помощь с SQL-запросом, который имеет внутреннее и внешнее соединение - PullRequest
2 голосов
/ 28 января 2011

Мне действительно нужна помощь, чтобы получить правильный запрос.Я не могу поделиться реальными именами таблиц и столбцов, но постараюсь сделать все возможное, чтобы решить проблему просто.

Предположим, что следующие таблицы.Таблицы и ключи НЕ МОГУТ быть изменены.Период.Мне все равно, если вы считаете, что это плохой дизайн, этот вопрос не является вопросом дизайна, он связан с синтаксисом SQL.

  • Таблица A - Первичный ключ с именем id1
  • ТаблицаB - Содержит два внешних ключа, TableA.id1 и Foo.id2 (игнорируйте Foo, это не имеет значения)
  • Таблица C - Содержит два внешних ключа, TableA.id1 и Foo.id2, дополнительные интересныестолбцы.

Ограничения:

  1. SQL получает набор идентификаторов, переданных в качестве аргумента.
  2. Он должен возвращать список строк таблицы C.
  3. Он должен возвращать только строки таблицы C, в которых существует строка таблицы B с совпадающими таблицами TableA.id1 и Foo.id2 - есть строки ARE вТаблица C, которая не соответствует Таблице B
  4. Строка ДОЛЖНА быть возвращена для каждого переданного идентификатора id1, даже если строки таблицы C. не существует.

Сначала я попытался использовать Left Outer.Соединение из Таблицы A в Таблицу B, а затем Внутреннее Соединение с Таблицей C. Это нарушает 4-е правило, приведенное выше, так как Внутреннее Объединение исключает эти строки.

Далее я попробовал два соединения Left Outer.Это ближе, но имеет побочный эффект от включения строк, которые соответствуют соединению таблицы A с таблицей B, но не имеют соответствующей записи таблицы C, а это не то, что мне нужно.

Итак, вотто, что я придумала.

SELECT
  a.id1,
  c.*
FROM
  TableB b
INNER JOIN 
  TableC c USING (id1,id2)
RIGHT OUTER JOIN
  TableA a USING (id1)
WHERE
  a.id1 in (x,y,z)

Я немного настороженно отношусь к правому внешнему соединению, поскольку прочитанная документация говорит, что его можно заменить левым внешним, но он не появляетсятак для этого случая.Это также кажется немного редким, что заставляет других разработчиков нервничать, поэтому я осторожен.

Итак, три вопроса в одном.

  1. Это правильно?
  2. Правильно ли я использовал правильное внешнее объединение?
  3. Есть ли более чистый способ добиться того же?

РЕДАКТИРОВАТЬ: DB - это MySQL

Ответы [ 3 ]

2 голосов
/ 28 января 2011

Вы можете переписать его как левое внешнее соединение, используя скобки.В псевдо-SQL измените это:

SELECT ...
FROM b
INNER JOIN c ON ...
RIGHT OUTER JOIN a ON ...

на это:

SELECT ...
FROM a
LEFT OUTER JOIN (
    b INNER JOIN c ON ...
) ON ...
1 голос
/ 28 января 2011

Вы можете использовать предложение EXISTS, которое иногда работает лучше

SELECT
  a.id1,
  c.*
FROM TableA a
LEFT JOIN TableC c
 ON c.id1 = a.id1 AND EXISTS (
    select *
    from TableB b
    where b.id1=c.id1 and b.id2=c.id2)
WHERE
  a.id1 in (x,y,z)

Как вы уже написали, это работает, потому что ANSI JOINs всегда обрабатываются сверху вниз . Поскольку перед присоединением к A вам нужно проверить B на C, это единственный способ написать его без введения подзапроса [(B x C) RIGHT JOIN A]. Однако , план с неправильным запросом может выполнить все записи в B и C (B x C) перед прямым соединением с A.

Метод EXISTS эффективно использует фильтр на A, затем LEFT присоединяется к C и для каждого найденного C проверяет, что он также существует в B (или сбрасывает).

Q в

  1. Да, ваш запрос правильный
  2. Да
  3. EXISTS должен работать лучше
0 голосов
/ 28 января 2011

Да, вам нужно начать с Таблицы A, а затем добавить таблицы B и C, используя соединения.Единственная причина, по которой вам даже требуется TableA, - убедиться, что у вас есть строка для каждого параметра.

Select a.id1,c.*
From
  TableA a
  Left Join TableB b on a.id1=b.id1
  Left Join TableC c on b.id1=c.id1 and b.id2=c.id2
Where a.id1 in (x,y,z)

Вам необходимо выполнять OUTER-соединения по всей длине, иначе строки, отсутствующие в B, также вызовут данныеот А до фильтрации из результирующего набора.Соединяя C с B (а не напрямую с A), вы используете B для фильтрации.Вы можете сделать это с помощью сложного предложения EXISTS, но это чище.

...