2016-08-12 3 views
0

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

Главная:

| id | mname | sid | lid | 
|----|-------|-----|-----| 
| 1 | a1 | 1 | 2 | 
| 2 | a2 | 2 | 3 | 
| 3 | a3 | 1 | 1 | 

Short:

| id | lang | sdesc | 
|----|------|-------| 
| 1 | de | S1 | 
| 1 | en | S2 | 
| 2 | de | S3 | 
| 3 | en | S4 | 

(id, lang) уникален.

Long:

| id | lang | ldesc | 
|----|------|-------| 
| 1 | de | L1 | 
| 1 | en | L2 | 
| 2 | de | L3 | 
| 3 | en | L4 | 

(id, lang) уникален.

Я хочу, чтобы соединить эти три таблицы, чтобы иметь следующий результат:

| mname | lang | sdesc | ldesc | 
|-------|------|--------|--------| 
| a1 | de | S1  | L3  | 
| a1 | en | S2  | (null) | 
| a2 | de | S3  | (null) | 
| a2 | en | (null) | L4  | 
| a3 | de | S1  | L1  | 
| a3 | en | S2  | L2  | 

Моя первая попытка была (sqlfiddle)

select m.mname, s.lang, s.sdesc, l.lang, l.ldesc 
from main m 
left join short_desc s on s.id = m.sid 
left join long_desc l on l.id = m.lid 

, который дает слишком много записей.

Следующая один был (sqlfiddle)

select m.mname, s.lang, s.sdesc, l.lang, l.ldesc 
from main m 
left join short_desc s on s.id = m.sid 
left join long_desc l on l.id = m.lid 
where s.lang = l.lang 

, который выходит из допустимых записей.

После многих вариаций выше, и много играл вокруг, я придумал (sqlfiddle)

with x1 as (select m.id, m.mname, s.lang, s.sdesc 
      from main m 
      join short_desc s on s.id = m.sid), 
    x2 as (select m.id, m.mname, l.lang, l.ldesc 
      from main m 
      join long_desc l on l.id = m.lid) 
select coalesce(x1.mname, x2.mname) mname, 
     coalesce(x1.lang, x2.lang) lang, 
     x1.sdesc, 
     x2.ldesc 
from x1 
full outer join x2 on x2.id = x1.id and x2.lang = x1.lang 

, которая дает желаемый результат, но мне это кажется чрезмерным для такой простой (?) требование.

Итак, вот мой вопрос, есть ли более простой подход?

+1

'... от главного креста м Join (выберите * из unnest (массив [ 'де', 'ан'])) языки (код) левый присоединиться short_desc s на s.id = m.sid и lang.code = s.lang ... ' – Abelisto

+0

Ваши данные и их результат не соответствует. Ваш результат запрашивает строку a2, en, null, L4, но ваши данные имеют L4 против id 3. – RelativePHPNewbie

+0

@RelativePHPNewbie Да, 'm.lid = l.id', то есть' m = (2, a2, 2, * 3 *) 'и' l = (* 3 *, en, L4) ' –

ответ

1

IMO, что вы пропустили в вашей схеме данных является словарь доступных языков. Если ввести его, то все остальное станет проще.

Требуемый словарь холодно быть:

  • простой константа, как array['en','de','fr']
  • истинной таблицы
  • кода языка вид накопительной из таблиц, как short_desc, long_desc и т.д.

Это вам подходит случай, который более подходит для ваших целей.

В примере ниже мы рассмотрим язык словарь, как КТР:

with lang(code) as (values('en'),('de'),('fr')) 
select m.*, lang.code 
from main m cross join lang; 

╔════╤═══════╤═════╤═════╤══════╗ 
║ id │ mname │ sid │ lid │ code ║ 
╠════╪═══════╪═════╪═════╪══════╣ 
║ 1 │ a1 │ 1 │ 2 │ en ║ 
║ 2 │ a2 │ 2 │ 3 │ en ║ 
║ 3 │ a3 │ 1 │ 1 │ en ║ 
║ 1 │ a1 │ 1 │ 2 │ de ║ 
║ 2 │ a2 │ 2 │ 3 │ de ║ 
║ 3 │ a3 │ 1 │ 1 │ de ║ 
║ 1 │ a1 │ 1 │ 2 │ fr ║ 
║ 2 │ a2 │ 2 │ 3 │ fr ║ 
║ 3 │ a3 │ 1 │ 1 │ fr ║ 
╚════╧═══════╧═════╧═════╧══════╝ 
(9 rows) 

Как вы можете видеть, теперь у нас есть отдельные строки для каждого языка, а следующие и последний шаг присоединиться к другим две таблицы в запрос выше, с использованием id и lang поля:

with lang(code) as (values('en'),('de'),('fr')) 
select m.*, lang.code, s.sdesc, l.ldesc 
from main m cross join lang 
    left join short_desc s on s.id = m.sid and s.lang = lang.code 
    left join long_desc l on l.id = m.lid and l.lang = lang.code; 

╔════╤═══════╤═════╤═════╤══════╤═══════╤═══════╗ 
║ id │ mname │ sid │ lid │ code │ sdesc │ ldesc ║ 
╠════╪═══════╪═════╪═════╪══════╪═══════╪═══════╣ 
║ 3 │ a3 │ 1 │ 1 │ de │ S1 │ L1 ║ 
║ 3 │ a3 │ 1 │ 1 │ en │ S2 │ L2 ║ 
║ 3 │ a3 │ 1 │ 1 │ fr │ ░░░░ │ ░░░░ ║ 
║ 1 │ a1 │ 1 │ 2 │ de │ S1 │ L3 ║ 
║ 1 │ a1 │ 1 │ 2 │ en │ S2 │ ░░░░ ║ 
║ 1 │ a1 │ 1 │ 2 │ fr │ ░░░░ │ ░░░░ ║ 
║ 2 │ a2 │ 2 │ 3 │ de │ S3 │ ░░░░ ║ 
║ 2 │ a2 │ 2 │ 3 │ en │ ░░░░ │ L4 ║ 
║ 2 │ a2 │ 2 │ 3 │ fr │ ░░░░ │ ░░░░ ║ 
╚════╧═══════╧═════╧═════╧══════╧═══════╧═══════╝ 
0

Вы хотите left join с условием на langв в on положение:

select m.mname, s.lang, s.sdesc, l.lang, l.ldesc 
from main m left join 
    short_desc s 
    on s.id = m.sid left join 
    long_desc l 
    on l.id = m.lid and s.lang = l.lang; 

Это должно быть решение в Goldilock, в с правильным количеством строк.

Или даже:

select m.mname, s.lang, s.sdesc, l.lang, l.ldesc 
from main m left join 
    short_desc s 
    on s.id = m.sid left join 
    long_desc l 
    on l.id = m.lid and (s.lang = l.lang or s.lang is null); 
+0

Это работает для записей, где 'ldesc' равно null, но он не содержит записей, где 'sdesc' равно null, как в строке« a2 (null) L4 »([sqlfiddle] (http://sqlfiddle.com/#!15/b3cf7/9/0)). –

Смежные вопросы