Я неправильно понимаю присоединения? - PullRequest
5 голосов
/ 28 марта 2012

Я пытаюсь выучить стандарт SQL ANSI-92, но, похоже, я не совсем понимаю его (я новичок в стандарте ANSI-89, а также в базах данных в целом).

В моем примере у меня три таблицы королевства - <семейство - <вид (биологические классификации). </p>

  • Могут быть царства без видов и семейств.
  • Могут быть семьи без видов и видов.
  • Могут быть виды без царства или семейств.

Почему это может произойти?

Скажем, биолог обнаружил новый вид, но он не классифицировал его как царство или семью, создал новое семейство, в котором нет вида, и не уверен, к какому царству оно должно принадлежать и т. Д.

Вот скрипка (см. Последний запрос): http://sqlfiddle.com/#!4/015d1/3

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

    select *
    from reino r
         left join (
             familia f             
             right join especie e
                 on f.fnombre = e.efamilia
                 and f.freino = e.ereino
         ) on r.rnombre = f.freino 
           and r.rnombre = e.ereino;

Я думаю, что это будет сделано:

  1. объединяет семейство и виды как правильное объединение, поэтому оно объединяет все виды, но не те семейства, у которых нет видов. Таким образом, если вид не был классифицирован в семью, он будет иметь нулевое значение в семействе.

  2. Затем присоединитесь к королевству с результатом как левое соединение, чтобы оно принесло каждое королевство, даже если в этом королевстве нет классифицированных семейств или видов.

Я не прав? Разве это не должно показывать мне те виды, которые не были классифицированы? Если я делаю внутренний запрос, он приносит то, что я хочу. Есть ли проблема, когда я группирую вещи?

Ответы [ 4 ]

2 голосов
/ 28 марта 2012

Вы правы в своем описании # 1 ... проблема с вашим запросом находится на шаге # 2.

Когда вы делаете left join из королевства в (семейство и виды), вы запрашиваете каждое королевство, даже если нет соответствия (семейство и виды) ... однако, это вам не вернет (семья и вид), в которой нет подходящего королевства.

Более близкий запрос будет:

select *
    from reino r
         full join (
             familia f             
             right join especie e
                 on f.fnombre = e.efamilia
                 and f.freino = e.ereino
         ) on r.rnombre = f.freino 
           and r.rnombre = e.ereino;

Обратите внимание, что left join был заменен на full join ...

однако, это возвращает только семейства, связанные с видом ... оно не возвращает никаких семейств, связанных с царством, но не виды.

Перечитав ваш вопрос, вы на самом деле хотите, чтобы вы хотели ...


РЕДАКТИРОВАТЬ: Если подумать, вы можете переписать ваш запрос следующим образом:

select *
from 
    especie e
    left join familia f 
        on f.fnombre = e.efamilia
        and f.freino = e.ereino
    full join reino r
        on r.rnombre = f.freino 
        and r.rnombre = e.ereino;

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

2 голосов
/ 28 марта 2012

В случае, если это помогает:

В относительном смысле, [OUTER JOIN] является своего рода браком с дробовиком: оно заставляет столы превращаться в некое объединение - да, я имею в виду объединение, а не объединение- даже когда данные таблицы не соответствуют обычным требованиям для объединения.Он делает это, по сути, заполняя одну или обе таблицы нулями перед выполнением объединения, тем самым заставляя их соответствовать тем обычным требованиям.Но нет причины, по которой это заполнение не должно выполняться с правильными значениями вместо нулей, как в этом примере:

SELECT SNO , PNO 
FROM   SP 
UNION  
SELECT SNO , 'nil' AS PNO 
FROM   S 
WHERE  SNO NOT IN ( SELECT SNO FROM SP )

Вышеуказанное эквивалентно:

SELECT SNO , COALESCE ( PNO , 'nil' ) AS PNO 
FROM   S NATURAL LEFT OUTER JOIN SP

Источник: SQL и реляционная теория: как написать точный код SQL по дате CJ

1 голос
/ 28 марта 2012

Если вы хотите, чтобы запрос был переписан с малейшим изменением от того, что у вас есть, вы можете изменить соединение LEFT на соединение FULL. Вы можете также удалить лишние скобки и r.rnombre = f.freino из условия ON:

select *
from reino r
     full join                      --- instead of LEFT JOIN
         familia f             
         right join especie e
             on f.fnombre = e.efamilia
             and f.freino = e.ereino
       on r.rnombre = e.ereino;
                                ---removed the:    r.rnombre = f.freino    
0 голосов
/ 28 марта 2012

Попробуйте использовать это:

select * 
from reino r 
join especie e on (r.rnombre = e.ereino) 
join familia f on (f.freino = e.ereino and f.fnombre = e.efamilia)

Может быть, вы поменяли efamilia и enombre в таблице especie?

...