Комплексное объединение - включает диапазоны дат и сумму - PullRequest
1 голос
/ 03 мая 2010

У меня есть две таблицы, к которым мне нужно присоединиться ... Я хочу объединить table1 и table2 для 'id' - однако в таблице два id не уникален. Я хочу только одно значение, возвращаемое для таблицы два, и это значение представляет собой сумму столбца с именем «total_sold» - в пределах указанного диапазона дат (скажем, один месяц), однако я хочу более одного диапазона дат одновременно

SELECT ta.id, sum(tb.total_sold) as total_sold_this_week, sum(tc.total_sold) as total_sold_this_month
FROM table_a as ta
LEFT JOIN table_b as tb ON ta.id=tb.id AND tb.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -1 WEEK) AND NOW()
LEFT JOIN table_b as tc ON ta.id=tc.id AND tc.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -1 MONTH) AND NOW()
GROUP BY ta.id

это работает, но не суммирует строки - возвращает только одну строку для каждого идентификатора ... как получить сумму из таблицы b вместо одной строки ??? Пожалуйста, критикуйте, если формат вопроса может использовать больше работы - я могу переписать и предоставить образцы данных, если требуется - это упрощенная версия гораздо более крупной проблемы.

-Спасибо

1 Ответ

6 голосов
/ 03 мая 2010

Использование подзапросов

Один из способов решить эту проблему - использовать подзапросы . LEFT JOIN создает новый «результат» для каждого совпадения в правой таблице, поэтому использование двух ЛЕВЫХ СОЕДИНЕНИЙ создает больше строк, чем вы хотите. Вы можете просто выбрать нужное значение, но это может быть медленным:

SELECT ta.id, 
   (SELECT SUM(total_sold) as total_sold 
    FROM table_b 
    WHERE date_sold BETWEEN ADDDATE(NOW(), INTERVAL -1 WEEK) AND NOW()
    AND id=ta.id) as total_sold_this_week, 
   (SELECT SUM(total_sold) as total_sold 
    FROM table_b 
    WHERE date_sold BETWEEN ADDDATE(NOW(), INTERVAL -1 MONTH) AND NOW() 
    AND id = ta.id) as total_sold_this_month 
FROM table_a ta;

Результат:

+----+----------------------+-----------------------+
| id | total_sold_this_week | total_sold_this_month |
+----+----------------------+-----------------------+
|  1 |                    3 |                     7 |
|  2 |                    4 |                     4 |
|  3 |                 NULL |                  NULL |
+----+----------------------+-----------------------+
3 rows in set (0.04 sec)

Использование SUM (CASE ...)

Этот метод не использует подзапросы (и, скорее всего, будет быстрее для больших наборов данных). Мы хотим объединить table_a и table_b один раз, используя наш «самый большой» диапазон дат, а затем использовать SUM() на основе CASE для вычисления «меньшего диапазона».

SELECT ta.*, 
  SUM(total_sold) as total_sold_last_month, 
  SUM(CASE 
    WHEN date_sold BETWEEN NOW() - INTERVAL 1 WEEK AND NOW() 
    THEN total_sold
    ELSE 0 
    END) as total_sold_last_week 
FROM table_a AS ta 
LEFT JOIN table_b AS tb 
   ON ta.id=tb.id AND tb.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -1 MONTH) AND NOW() 
GROUP BY ta.id;

Возвращает почти тот же набор результатов, что и в примере подзапроса:

+----+-----------------------+----------------------+
| id | total_sold_last_month | total_sold_last_week |
+----+-----------------------+----------------------+
|  1 |                     7 |                    3 |
|  2 |                     4 |                    4 |
|  3 |                  NULL |                    0 |
+----+-----------------------+----------------------+
3 rows in set (0.00 sec)

Единственная разница - 0 вместо NULL. Вы можете суммировать столько диапазонов дат, сколько захотите, используя этот метод, но все же, вероятно, все же лучше ограничить строки, возвращаемые самым большим диапазоном в предложении ON.

Просто чтобы показать, как это работает: удаление вызовов GROUP BY и SUM() и добавление date_sold к SELECT возвращает это:

+----+------------+-----------------------+----------------------+
| id | date_sold  | total_sold_last_month | total_sold_last_week |
+----+------------+-----------------------+----------------------+
|  1 | 2010-04-30 |                     2 |                    2 |
|  1 | 2010-04-24 |                     2 |                    0 |
|  1 | 2010-04-24 |                     2 |                    0 |
|  1 | 2010-05-03 |                     1 |                    1 |
|  2 | 2010-05-03 |                     4 |                    4 |
|  3 | NULL       |                  NULL |                    0 |
+----+------------+-----------------------+----------------------+
6 rows in set (0.00 sec)

Теперь, когда вы GROUP BY id и SUM() в двух столбцах total_sold у вас есть результаты!

Старый совет

До того, как вы добавили два разных диапазона дат в микс, вы могли бы использовать GROUP BY для группировки, используя идентификатор таблицы в table1, и агрегатную функцию SUM() для сложите возвращенные строки.

SELECT ta.id, SUM(tb.total_sold) as total_sold_this_week
FROM table_a as ta
LEFT JOIN table_b as tb 
ON ta.id=tb.id AND tb.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -3 WEEK) AND NOW()
GROUP BY ta.id
+----+----------------------+
| id | total_sold_this_week |
+----+----------------------+
|  1 |                    7 |
|  2 |                    4 |
|  3 |                 NULL |
+----+----------------------+
3 rows in set (0.00 sec)

Данные испытаний

NOW() - 2010-05-03

mysql> select * from table_a; select * from table_b;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+
3 rows in set (0.00 sec)

+----+------------+------------+
| id | date_sold  | total_sold |
+----+------------+------------+
|  1 | 2010-04-24 |          2 |
|  1 | 2010-04-24 |          2 |
|  1 | 2010-04-30 |          2 |
|  1 | 2010-05-03 |          1 |
|  2 | 2010-05-03 |          4 |
+----+------------+------------+
5 rows in set (0.00 sec)
...