2009-07-01 6 views
0

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

select days_of_months.`Date`, 
     m.NAME as "Model", 
     count(t.ID) as "Count" 
    from MODEL m 
    left join APPLIANCE_UNIT a on (m.ID = a.MODEL_FK and a.NUMBER_OF_UNITS > 0) 
    left join NEW_TICKET t on (a.NEW_TICKET_FK = t.ID and t.TYPE = 'SALES' 
and t.SALES_ORDER_FK is not null) 
right join (select date(concat(2009,'-',temp_months.id,'-',temp_days.id)) as "Date" 
       from temp_months 
       inner join temp_days on temp_days.id <= temp_months.last_day 
       where temp_months.id = 3 -- March 
      ) days_of_months on date(t.CREATION_DATE_TIME) = 
date(days_of_months.`Date`) 
group by days_of_months.`Date`, 
     m.ID, m.NAME 

Я создал временные таблицы temp_months и temp_days для того, чтобы получить все дни в течение любого месяца. Я использую MySQL 5.1, но я пытаюсь сделать запрос ANSI-совместимым.

ответ

6

Вы должны CROSS JOIN ваши даты и модели, так что у вас есть ровно одна запись для каждого дня пары моделей независимо от того, что, а затем LEFT JOIN другие таблицы:

SELECT date, name, COUNT(t.id) 
FROM (
     SELECT ... 
     ) AS days_of_months 
CROSS JOIN 
     model m 
LEFT JOIN 
     APPLIANCE_UNIT a 
ON  a.MODEL_FK = m.id 
     AND a.NUMBER_OF_UNITS > 0 
LEFT JOIN 
     NEW_TICKET t 
ON  t.id = a.NEW_TICKET_FK 
     AND t.TYPE = 'SALES' 
     AND t.SALES_ORDER_FK IS NOT NULL 
     AND t.CREATION_DATE_TIME >= days_of_months.`Date` 
     AND t.CREATION_DATE_TIME < days_of_months.`Date` + INTERVAL 1 DAY 
GROUP BY 
     date, name 

То, как вы это делаете теперь вы получите NULL в model_id за те дни, когда у вас нет продаж, и они сгруппированы вместе.

Обратите внимание на JOIN условие:

AND t.CREATION_DATE_TIME >= days_of_months.`Date` 
AND t.CREATION_DATE_TIME < days_of_months.`Date` + INTERVAL 1 DAY 

вместо

DATE(t.CREATION_DATE_TIME) = DATE(days_of_months.`Date`) 

Это поможет сделать ваш запрос sargable (оптимизировано индексами)

+0

Вам не нужно вводить специальные соединения. Я не слишком хорошо знаком с mysql, но в sql-сервере, если вы не учитываете внутренний/внешний, он по умолчанию является внутренним. –

+1

@dan s: LEFT и RIGHT подразумевают OUTER. – Quassnoi

+0

Это решило мою проблему. Большое спасибо! –

0

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

http://dev.mysql.com/doc/refman/5.1/en/join.html

+0

Если вы прочтете запрос, вы заметите, что я уже использую левое и правое внешние соединения. –

0

Вы ищете OUTER присоединиться. Левое внешнее соединение создает результирующий набор с записью с левой стороны соединения, даже если на правой стороне нет записи, к которой нужно подключиться. Правое внешнее соединение делает то же самое в противоположном направлении, создает запись для правой боковой таблицы, даже если левая сторона не имеет соответствующей записи. Любой столбец, проецируемый из таблицы, который не имеет записи, будет иметь значение NULL в результате объединения.

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