2014-08-27 2 views
1

У меня есть таблицы, подобные этим:SQL Outer Join?

Fields (ID int, Name varchar(20)) 

ID  Name 
=============== 
1  FieldName 
2  FeildDesc 

Container (ID int, Name varchar(100)) 

ID  Name 
==================================== 
1  C1  
2  C2 
3  C3 

ContainerField (ContainerId int, FieldId int, FieldValue varchar(100)) 

ContainerId FieldId FieldValue 
==================================== 
1    1   Container1 
1    2   Container1 Desc 
2    1   Container2 
2    2   Container3 Desc 
3    1   Container3 

Я хотел бы, чтобы мой результат выглядит следующим образом:

ContainerId Name   Desc 
=================================== 
1    Container1 Container1 Desc 
2    Container2 Container2 Desc 
3    Container3 NULL 

Я пытался сделать LEFT OUTER JOIN между ContainerField и поле, но тот Бесполезный» t кажутся работать. Я был бы признателен, если бы кто-нибудь мог помочь.

+3

"*, что не seemt работать *" не является приемлемым описание ошибки –

+3

Как не работает? Какой запрос вы попробовали? – Barmar

ответ

3

Соедините таблицу дважды, один раз для названий и один раз для описания.

select 
    c.Id as ContainerId, 
    cfn.FieldValue as Name, 
    cfd.FieldValue as Desc 
from 
    Container c 
    left join ContainerField cfn 
    on cfn.ContainerId = c.ID and cfn.FieldId = 1 
    left join ContainerField cfd 
    on cfd.ContainerId = c.ID and cfd.FieldId = 2 

Как вы можете видеть, а не группы или агрегации. Кроме того, вам действительно не нужен сам поле «Поля» в запросе, хотя хорошо иметь его в вашей базе данных, поскольку он обеспечивает ссылочную целостность (если у вас есть соответствующие ключи), и он предоставляет своего рода документацию. Но вы также можете использовать перечисление для этого.

В качестве альтернативы, если у вас есть не только два поля, но и неизвестное их число, вы можете рассмотреть pivot tables. Должен признаться, что я не очень хорош с теми, особенно в TSQL, поэтому я дам вам только ссылку для изучения: http://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx Или, надеюсь, кто-то другой может дать вам конкретный пример.

0

сделать именно, кажется, нуждается в left join:

select c.id, c.name, cf.FieldValue 
from Container c join 
    ContainerField cf 
    on cf.ContainerId = c.id and cf.FieldId = 2; 

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

1

Нет необходимости в внешнем соединении, внутреннее соединение сделает это. Вы также ничего не используете от Container (если вы хотите отобразить имя контейнера вместо ID, вы можете сделать внутреннее соединение с ним, чтобы получить это).

SELECT c.id AS ContainerID, 
     MAX(CASE WHEN f.name = 'FieldName' THEN c.FieldValue END) AS Name, 
     MAX(CASE WHEN f.name = 'FieldDesc' THEN c.FieldValue END) AS `Desc` 
FROM ContainerField AS c 
JOIN Fields AS f ON c.FieldID = f.id 
GROUP BY ContainerID 

DEMO

Обратите внимание, что у вас есть орфографические ошибки в вашей Fields таблице: FeildsDesc должны быть FieldsDesc.

+0

Это будет работать. Я не думаю, что «злоупотребление» группой подобным образом является самым чистым способом, но опять же, я использовал это как в ответах SO, так и в производственном коде, поэтому давайте не будем лицемером.+1 :-) – GolezTrol

0

Я думаю, что это то, что вы после:

SELECT t1.ContainerId, t1.FieldValue, t2.FieldValue 
FROM ContainerField t1 
LEFT JOIN ContainerField t2 ON t1.ContainerId = t2.ContainerId 
     AND t1.FieldId = 1 and t2.FieldId = 2 

EDIT (SQL Fiddle):

SELECT DISTINCT t1.ContainerId, 
(
    SELECT t2.FieldValue FROM ContainerField t2 
    WHERE t2.FieldId = 1 AND t1.ContainerId = t2.ContainerId 

) AS [Name], 
(
    SELECT t3.FieldValue FROM ContainerField t3 
    WHERE t3.FieldId = 2 AND t1.ContainerId = t3.ContainerId 
) AS [Desc] 
FROM ContainerField t1 
+0

Спасибо за ответ, но это не показывает последнюю строку, которая имеет NULL в качестве столбца Desc. – notlkk

+0

Я думаю, что это может сработать, если вы переместите условие 't1.FieldId = 1' в предложение where. Если вы этого не сделаете, я думаю, что нет условия, которое предотвращает возврат t1 из полей типа 2. – GolezTrol

+0

** @ notlkk **, я обновляю свой ответ, чтобы показать, что вы можете получить желаемые результаты только через таблицу ('ContainerField'). См. Мое редактирование. – Linger

1

Может также бросить в мои два цента. Это даст вам то, что вам нужно. Это сделано в Oracle, но должно работать одинаково.

EDIT: SQL Fiddle Demo для примера работы.

Select * From (

WITH 
/* Example Tables.*/ 
Container As 
(
    Select 1 as ID, 'C1' as Name From Dual Union All  
    Select 2 as ID, 'C2' as Name From Dual Union All 
    Select 3 as ID, 'C3' as Name From Dual 
) 

,ContainerField as 
(
    Select 1 as ContainerId, 1 as FieldId, 'Container1  ' as FieldValue From Dual UNION ALL 
    Select 1 as ContainerId, 2 as FieldId, 'Container1 Desc' as FieldValue From Dual UNION ALL 
    Select 2 as ContainerId, 1 as FieldId, 'Container2  ' as FieldValue From Dual UNION ALL 
    Select 2 as ContainerId, 2 as FieldId, 'Container2 Desc' as FieldValue From Dual UNION ALL 
    Select 3 as ContainerId, 1 as FieldId, 'Container3  ' as FieldValue From Dual 
) 

/*Code*/ 
Select 
    C.ID, 
    Max(Case When CF.FieldId = 1 Then FieldValue End) as Name, 
    Max(Case When CF.FieldId = 2 Then FieldValue End) as Descrption 
From 
    Container C 
    Left Join ContainerField CF on C.ID = CF.ContainerID 

Group By 
    C.ID, 
    C.Name 
Order By 
    C.ID 
) 

Выход:

CONTAINERID  NAME  DESCRPTION 
1    Container1 Container1 Desc 
2    Container2 Container2 Desc 
3    Container3  
+1

Вы можете найти http://sqlfiddle.com/ интересно. Затем вы можете ссылаться на запрос. Я только говорю это, потому что у меня есть МНОГИЕ ответы, подобные вам. Уверяю вас, что это проще и выглядит более «профессиональным». – SQLMason

+1

С помощью 'CF.ContainerID' вы немного обманываете, и он не сработает, если нет никакого контейнера ContainerField для контейнера для типа поля' Name'. Вместо этого вы должны выбрать '' '' '' '' '' '' '' '' '' '' '' '' '' '. Или, если вы предполагаете, что всегда есть поле имени, измените первое соединение на 'inner join', чтобы сделать это предположение понятным (и сделайте запрос более оптимизированным). И как только вы это исправите, ваш ответ по существу тот же, что и у Бармера. :) – GolezTrol

+0

@GolezTrol Хорошая добыча! Я обычно группируюсь в этом методе, но, по-моему, в шквал я помещаю «ContainerID», что, безусловно, ненадежно. – Phillip