2014-09-05 3 views
1

Мне нужна «небольшая» помощь с SQL-запросом (MySQL).SQL-запрос, соединяющий несколько таблиц (MySQL)

У меня есть следующие таблицы:

COURIERS стол:

+------------+ 
    | COURIER_ID | 
    +------------+ 

DELIVERIES стол:

+-------------+------------+------------+ 
    | DELIVERY_ID | COURIER_ID | START_DATE | 
    +-------------+------------+------------+ 

стола:

+----------+-------------+-------------+ 
    | ORDER_ID | DELIVERY_ID | FINISH_DATE | 
    +----------+-------------+-------------+ 

COORDINATES стол:

+-------------+-----+-----+------+ 
    | DELIVERY_ID | LAT | LNG | DATE | 
    +-------------+-----+-----+------+ 

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

Что мне нужно?

  • SQL-запрос, который возвращает все курьеров [COURIER_ID], их последний доставка [DELIVERY_ID] (на основе последнего START_DATE), то доставка Последняя координата [LAT и LNG] (на основе последнего DATE) и количество оставшихся ордеров (всего заказов последней поставки, которые не имеют FINISH_DATE).

  • курьер не может иметь никаких поставок, в данном случае я хочу DELIVERY_ID = NULL, LAT = NULL и LNG = NULL в результате.

  • Подающая не может иметь никаких координат, в данном случае я хочу LAT = NULL и LNG = NULL в результате.

Что я мог сделать?

SELECT c.`COURIER_ID`, 
     d.`DELIVERY_ID`, 
     r.`LAT`, 
     r.`LNG`, 
    (SELECT COUNT(DISTINCT `ORDER_ID`) 
    FROM `ORDERS` 
    WHERE `DELIVERY_ID` = d.`DELIVERY_ID` 
    AND `FINISH_DATE` IS NULL) AS REMAINING_ORDERS 

FROM `COURIERS` AS c 
LEFT JOIN `DELIVERIES` AS d USING (`COURIER_ID`) 
LEFT JOIN `COORDINATES` AS r ON r.`DELIVERY_ID` = d.`DELIVERY_ID` 

WHERE (CASE WHEN 
     (SELECT MAX(`START_DATE`) 
      FROM `DELIVERIES` 
      WHERE `COURIER_ID` = c.`COURIER_ID`) IS NULL THEN d.`START_DATE` IS NULL ELSE d.`START_DATE` = 
     (SELECT MAX(`START_DATE`) 
      FROM `DELIVERIES` 
      WHERE `COURIER_ID` = c.`COURIER_ID`) END) 
    AND (CASE WHEN 
     (SELECT MAX(`DATE`) 
      FROM `COORDINATES` 
      WHERE `DELIVERY_ID` = d.`DELIVERY_ID`) IS NULL THEN r.`DATE` IS NULL ELSE r.`DATE` = 
     (SELECT MAX(`DATE`) 
      FROM `COORDINATES` 
      WHERE `DELIVERY_ID` = d.`DELIVERY_ID`) END) 
GROUP BY c.`COURIER_ID` 
ORDER BY d.`START_DATE` DESC 

Проблема заключается в том, что этот запрос очень медленно (от 5 до 20 секунд), когда у меня есть более 5к COORDINATES и не возвращает все курьеры иногда.

Большое вам спасибо за любое решение.

+0

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

+0

Я считаю, что это вопрос, потому что этот запрос работает не так, как мне нужно, независимо от его производительности. Производительность - лишь одна из проблем. Благодарю. –

+0

Должны ли вы возвращать все результаты в одном запросе базы данных? Или было бы хорошо, если бы вы отправили пару запросов в базу данных, пока конечным результатом является правильный набор данных? – axiopisty

ответ

1

Я не был в состоянии проверить этот запрос, так как у меня нет базы данных MySQL устанавливается прямо сейчас, гораздо меньше, с этой схемой и выборки данных.Но я думаю, что это сработает для вас:

select 
    c.courier_id 
    , d.delivery_id 
    , co.lat 
    , co.lng 
    , oc.cnt as remaining_orders 
from 
    couriers c 
    left join (
    select 
     d.delivery_id 
     , d.courier_id 
    from 
     deliveries d 
     inner join (
     select 
      d.delivery_id 
      , max(d.start_date) as start_date 
     from 
      deliveries d 
     group by 
      d.delivery_id 
    ) dmax on dmax.delivery_id = d.delivery_id and dmax.start_date = d.start_date 
) d on d.courier_id = c.courier_id 
    left join (
    select 
     c.delivery_id 
     , c.lat 
     , c.lng 
    from 
     coordinates c 
     inner join (
     select 
      c.delivery_id 
      , max(c.date) as date 
     from 
      coordinates c 
     group by 
      c.delivery_id 
    ) cmax on cmax.delivery_id = c.delivery_id and cmax.date = c.date 
) co on co.delivery_id = d.delivery_id 
    left join (
    select 
     o.delivery_id 
     , count(o.order_id) as cnt 
    from 
     orders o 
    where 
     o.finish_date is null 
    group by 
     o.delivery_id 
) oc on oc.delivery_id = d.delivery_id 
+0

Спасибо, я попробую ваш ответ, и я вернусь, чтобы узнать, сработало ли это. –

+0

Без проблем, спасибо. –

2

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

SELECT C.COURIER_ID, D.DELIVERY_ID, D.START_DATE, D.FINISH_DATE, 
     B.LAT, B.LNG, B.DATE, C.NoOfOrders 
FROM COURIERS C 
LEFT JOIN (SELECT * 
      FROM (SELECT * 
        FROM DELIVERIES D 
        ORDER BY D.COURIER_ID, D.START_DATE DESC 
       ) A 
      GROUP BY COURIER_ID 
     ) AS A ON C.COURIER_ID = A.COURIER_ID 
LEFT JOIN (SELECT * 
      FROM (SELECT * 
        FROM COORDINATES CO 
        ORDER BY CO.DELIVERY_ID, CO.DATE DESC 
       ) B 
      GROUP BY CO.DELIVERY_ID 
     ) AS B ON A.DELIVERY_ID = B.DELIVERY_ID 
LEFT JOIN (SELECT O.DELIVERY_ID, COUNT(1) NoOfOrders 
      FROM ORDERS O WHERE FINISH_DATE IS NULL 
      GROUP BY O.DELIVERY_ID 
     ) AS C ON A.DELIVERY_ID = C.DELIVERY_ID; 
+0

Спасибо, я попробую ваш ответ, и я вернусь, чтобы узнать, сработало ли это. –

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