Внешнее присоединиться с условиями? - PullRequest
2 голосов
/ 05 июня 2009

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

Это работает, но кажется слишком сложным и повторяющимся:

select volume.text_value as volume_value, issue.text_value as issue_value
    from metadatavalue item
    left outer join (select item_id, text_value from metadatavalue
                     where metadata_field_id = 90) volume
                    on item.item_id = volume.item_id
    left outer join (select item_id, text_value from metadatavalue
                     where metadata_field_id = 91) issue
                    on item.item_id = issue.item_id
    where item.metadata_field_id in (90, 91)

Есть ли более простой способ написать этот запрос?

Спасибо.

Ответы [ 5 ]

2 голосов
/ 05 июня 2009

PostgreSQL поддерживает полное внешнее соединение, которое может упростить запрос:

  select v.text_value as volume_value, i.text_value as issue_value
      from ( select item_id, text_value
               from metadatavalue
              where metadata_field_id = 90) v
           full join
           ( select item_id, text_value
               from metadatavalue
              where metadata_field_id = 91) i
           using (item_id)
2 голосов
/ 05 июня 2009

Попробуйте это

select  volume.text_value as volume_value, 
        issue.text_value as issue_value    
from    metadatavalue item    
        left outer join metadatavalue volume                    
            on item.item_id = volume.item_id    
        left outer join metadatavalue issue                    
            on item.item_id = issue.item_id    
where   volume.metadata_field_id = 90
and     issue.metadata_field_id = 91
2 голосов
/ 05 июня 2009
SELECT  DISTINCT ON (item_id)
        item_id,
        CASE metadata_field_id
        WHEN 90 THEN
                text_value
        ELSE    (
                SELECT  text_value
                FROM    metadatavalue m
                WHERE   m.metadata_field_id = 90
                        AND m.item_id = i.item_id
                )
        END AS volume,
        CASE metadata_field_id
        WHEN 91 THEN
                text_value
        ELSE    (
                SELECT  text_value
                FROM    metadatavalue m
                WHERE   m.metadata_field_id = 91
                        AND m.item_id = i.item_id
                )
        END AS issue
FROM    metadatavalue
WHERE   metadata_field_id IN (90, 91)
ORDER BY
        item_id

Наличие индекса на (item_id, metadata_field) улучшит этот запрос.

Это будет работать лучше, если есть несколько items с metadata из 90 и 91 по сравнению с общим числом items.

Если почти все items имеют эти metadata, просто используйте:

SELECT  *
FROM    (
        SELECT  item_id,
                (
                SELECT  text_value
                FROM    metadatavalue m
                WHERE   m.metadata_field_id = 90
                        AND m.item_id = i.item_id
                ) volume,
                (
                SELECT  text_value
                FROM    metadatavalue m
                WHERE   m.metadata_field_id = 91
                        AND m.item_id = i.item_id
                ) issue
        FROM    items
        ) q
WHERE   issue IS NOT NULL OR volume IS NOT NULL
0 голосов
/ 05 июня 2009

Вы можете вывести предикат из внутренней таблицы в условие соединения:

select volume.text_value as volume_value, issue.text_value as issue_value
from metadatavalue item
left outer join metadatavalue volume
                on volume.metadata_field_id = 90 and volume.item_id = item.item_id
left outer join metadatavalue issue
                on issue.metadata_field_id = 91 and issue.item_id = item.item_id
where item.metadata_field_id in (90, 91)

Хотя я не уверен, что понимаю вашу схему - мне кажется, что item должно ссылаться на таблицу, отличную от volume и issue? больше похоже на:

select item.*, volume.text_value as volume_value, issue.text_value as issue_value
from item
left outer join metadatavalue volume
                on volume.metadata_field_id = 90 and volume.item_id = item.item_id
left outer join metadatavalue issue
                on issue.metadata_field_id = 91 and issue.item_id = item.item_id

Это также делает довольно странным, когда условия исчезают. Вы могли бы поместить вышеупомянутый запрос в представление ("item_volume_issue"), и могло бы показаться, что объем и проблема - это просто дополнительные столбцы в item_volume_issue вместо отдельных таблиц (что для меня имеет больше смысла).

0 голосов
/ 05 июня 2009

Используя оператор case для разделения значений в сегменты, функция max позволит данным text_value перемещаться вверх.

select
   item_id
  ,max(case metadata_field_id when 90 then text_value else null end) as volume_value
  ,max(case metadata_field_id when 91 then text_value else null end) as issue_value
from
  metadatavalue
group by
   item_id

Вот что я использовал для тестирования:

select
   item_id
  ,max(case metadata_field_id when 90 then text_value else null end) as volume_value
  ,max(case metadata_field_id when 91 then text_value else null end) as issue_value
from
  (
   select 1 as item_id, 90 as metadata_field_id, '90-I am here' as text_value
   union
   select 1 as item_id, 91 as metadata_field_id, '91-Me too' as text_value
   union
   select 2 as item_id, 90 as metadata_field_id, null as text_value
   union
   select 2 as item_id, 91 as metadata_field_id, '91-funky' as text_value
   union
   select 3 as item_id, 90 as metadata_field_id, '90-fresh' as text_value
  ) metadatavalue
group by
   item_id

Результаты:

item_id  volume_value  issue_value
1        90-I am here  91-Me too
2        NULL          91-funky
3        90-fresh      NULL

Примечание. Я использовал SQL Server для моделирования и тестирования, а затем изменил синтаксис для соответствия Postgres.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...