2012-12-19 1 views
8

Я получаю дубликаты, когда я делаю два LEFT JOINs, чтобы перейти к «event_name» в моем примере ниже. Я получаю 112 случаев, когда он настроен таким образом. Однако, если я избавлюсь от двух линий LEFT JOIN и запустим запрос, я получаю 100 записей без дубликатов. Я попробовал DISTINCT с кодом ниже, но я все еще получаю 112 с дубликатами.Как избежать дубликатов в sql-запросе по трем связанным таблицам

SELECT "cases"."id", "cases"."date", "cases"."name", "event"."event_name" 
FROM "cases" 
LEFT JOIN "middle_table" ON "cases"."serial" = "middle_table"."m_serial" 
LEFT JOIN "event" ON "middle_table"."e_serial" = "event"."ev_serial" 
WHERE "cases"."date" BETWEEN '2012-12-11' AND '2012-12-13' 

Как я могу определить, что я хочу только точные 100 случаев от «дел», и что я ничего из таблиц в соединяющем производить какие-либо дополнительные строки не хочу?

Спасибо!

+1

Как связаны таблицы? 1: N от 'cases' до' middle_table'? Не могли бы вы рассказать об этом немного? –

+0

Вы могли бы предоставить некоторые тестовые данные? [sql-скрипка] (http://www.sqlfiddle.com) отлично подходит для этого. – Nico

+1

В моем случае я получал дубликаты из-за присоединения к соединению один-ко-многим.Единственное решение, которое я мог найти, это использовать подзапросы. A имеет много Bs. B имеет много Cs и много Ds. D имеет много Es и много Fs. Мне нужно было получить все Bs (соответствующие строке поиска), а также агрегировать все связанные Cs, Es и Fs для каждого совпадения. Я использовал внешнее соединение для получения ABC, затем использовал два подзапроса для агрегирования DE и DF. – bambams

ответ

7

Вы должны расширить свои ON положения включить условие, так что для каждой записи в cases есть только одна запись в middle_table, что соответствует условию, и для каждой записи в middle_table есть только одна запись в event:

LEFT JOIN middle_table ON cases.serial = middle_table.m_serial AND some_condition 

Вы можете, конечно, использовать DISTINCT. Если это не работает, это означает, что ваши результаты в разных полях cases.id, cases.date, cases.name и event.event_name. Изучите результаты и определите, какую из записей вы хотите выбросить, и включите это условие в предложение ON.

+0

Привет, AndreKR, так, как я упоминал в сообщении JohnLBevan ниже: Мне все равно, какой ребенок я падаю, так как они идентичны. Вы могли бы дать теоретическое условие? Я только сейчас понимаю, что функция Макс означает «возвращает наибольшее значение выбранного столбца». Есть ли простой способ просто использовать это в поле id в одной или обеих соединенных таблицах? – Chain

+0

Для этого вам нужно использовать один из этих трех методов: http://dev.mysql.com/doc/refman/5.5/en/example-maximum-column-group-row.html Но если они действительно идентичны, DISTINCT отфильтровал бы их. – AndreKR

+0

Обратите внимание, что указанная выше ссылка специфична для MySQL, но, вероятно, работает и с другими системами. Вы всегда должны указывать, какую СУБД вы используете в своем вопросе. – AndreKR

5

Проблема в том, что у вас есть несколько совпадений в таблицах, с которыми вы закончили соединение. Эффективно ваш код говорит:

select * 
from parent 
left outer join child on parent.id = child.parentId 

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

Если вы хотите получить родителя только после того, как вам нужно идти на компромисс; вы не можете иметь обоих детей. Либо выполнить агрегатную функцию на колоннах из дочерней таблицы и сделать группу, на колоннах из родительской таблицы, или использовать rownumber() over partition by (list,of,parent,columns order by list,of,child,columns) r во внутреннем заявлении и where r=1 во внешнем заявлении, например, как показано ниже:

select p.id, p.name, max(c.id), max(c.name) --nb: child id and name may come from different records 
from parent p 
left outer join child c on parent.id = child.parentId 
group by p.id, p.name 

или

select * 
from 
(
    select p.id, p.name, c.id, c.name 
    , rownumber() over (partition by p.id order by c.id desc) r 
    from parent p 
    left outer join child c on parent.id = child.parentId 
) x 
where x.r = 1 

UPDATE

Как уже упоминалось в комментариях, если данные ребенка точно так же, вы можете сделать это:

select p.id, p.name, c.name 
from parent p 
left outer join 
(
    select distinct c.parentId, c.name 
    from child 
) c on parent.id = child.parentId 

или (если несколько полей различны, но вы не заботитесь, которые вы получаете)

select p.id, p.name, c.id, c.name 
from parent p 
left outer join 
(
    select max(c.id) id, c.parentId, c.name 
    from child 
    group by c.parentId, c.name 
) c on parent.id = child.parentId 
+0

пс. @AndreKR также делает хорошее предложение; т. е. добавить условную логику к соединению, чтобы ограничить результаты до максимального числа одного ребенка на одного родителя. – JohnLBevan

+0

Привет, я думаю, что я понимаю большую часть этого. Да. В моем случае у некоторых родителей есть несколько детей ... но это какая-то избыточность в средней таблице или таблице событий в больницах. В принципе, оба ребенка некоторых родителей являются точными дубликатами: так что мне все равно, какой ребенок я выбираю, потому что они оба одинаковы. – Chain

+0

В этом случае любой из вышеперечисленных хорош (второй метод, вероятно, более эффективен). В качестве альтернативы вы можете отфильтровывать повторяющихся детей на ранней стадии (хотя я подозреваю, что отличная инструкция не работает, есть некоторые различия в том, что возвращается на уровне ребенка, даже если это только идентификаторы). – JohnLBevan

1

Дубликаты являются результатом наличия нескольких полей для «middle_table» и «событие» для " случаи». Вы можете ограничить выбор до значений, которые являются уникальными, используя «GROUP BY» ключевое слово (которое обычно используется для сопоставления функций, таких как COUNT и SUM), следующим образом:

SELECT "cases"."id", "cases"."date", "cases"."name", "event"."event_name" 
FROM "cases" 
LEFT JOIN "middle_table" ON "cases"."serial" = "middle_table"."m_serial" 
LEFT JOIN "event" ON "middle_table"."e_serial" = "event"."ev_serial" 
GROUP BY "cases"."id", "cases"."date", "cases"."name", "event"."event_name" 
WHERE "cases"."date" BETWEEN '2012-12-11' AND '2012-12-13' 
Смежные вопросы