2016-01-11 2 views
2

Howdie сделать,Как оптимизировать запрос SQL, который соединяет 3 таблицы

Я следующий SQL-запрос, который занимает около 4 секунд для запуска:

select 
    o.id, tu.status_type, m.upload_date 
from 
    (select order_id, max(last_updated) as maxudate from tracking_update group by order_id) t 
inner join 
    tracking_update tu on t.order_id=tu.order_id and t.maxudate=tu.last_updated 
right join 
    fgw247.order o on t.order_id=o.id 
left join 
    manifest m on o.manifest_id=m.id 
where 
    (m.upload_date >= '2015-12-12 00:00:00') or (m.upload_date <='2015-12-12 00:00:00' and tu.status_type != 'D' and tu.status_type != 'XD') 

Запрос присоединяется следующие 3 таблицы:

Порядок, манифест и отслеживание_обмена.

Запрос будет возвращать заказы, которые отвечают следующим критериям:

  1. Заказы < 30 дней с любым статусом доставки
  2. Заказы> 30 дней, а не статус доставки

Приказ есть считается, что если последний параметр отслеживания_загрузки для этого заказа имеет статус_тип 'D' или 'XD'

Теперь заказы перечислены в таблицу заказа. В таблице Order есть столбец с именем manifest_id, который ссылается на манифест, который был создан при заказе заказа.

манифест может иметь несколько заказов. Это то, что используется для определения того, был ли заказ загружен за последние 30 дней.

И наконец, таблица tracking_update содержит отслеживание_updates за заказ. В заказе может быть несколько параметров отслеживания.

В настоящее время таблица tracking_update имеет длину более 1M.

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

CREATE TABLE "order" (
    "id" int(11) NOT NULL AUTO_INCREMENT, 
    "ShipmentId" varchar(50) DEFAULT NULL, 
    "RecipientName" varchar(160) DEFAULT NULL, 
    "CompanyName" varchar(160) DEFAULT NULL, 
    "Address1" varchar(160) DEFAULT NULL, 
    "Address2" varchar(160) DEFAULT NULL, 
    "City" varchar(50) DEFAULT NULL, 
    "State" varchar(3) DEFAULT NULL, 
    "ZIP" int(11) DEFAULT NULL, 
    "TEL" int(11) DEFAULT NULL, 
    "Email" varchar(255) DEFAULT NULL, 
    "Bottles" int(11) DEFAULT NULL, 
    "Weight" float DEFAULT NULL, 
    "Resi" tinyint(1) DEFAULT NULL, 
    "Wave" int(11) DEFAULT NULL, 
    "url_slug" varchar(50) DEFAULT NULL, 
    "manifest_id" int(11) DEFAULT NULL, 
    "shipment_date" datetime DEFAULT NULL, 
    "tracking_number" varchar(30) DEFAULT NULL, 
    "shipping_carrier" varchar(10) DEFAULT NULL, 
    "last_tracking_update" datetime DEFAULT NULL, 
    "delivery_date" datetime DEFAULT NULL, 
    "customer_code" varchar(50) DEFAULT NULL, 
    "sub_cust_code" int(11) DEFAULT NULL, 
    PRIMARY KEY ("id"), 
    UNIQUE KEY "ShipmentID" ("ShipmentId"), 
    KEY "manifest_id" ("manifest_id"), 
    KEY "order_idx3" ("tracking_number"), 
    KEY "order_idx4" ("customer_code"), 
    CONSTRAINT "order_ibfk_1" FOREIGN KEY ("manifest_id") REFERENCES "manifest" ("id") 
); 

Tracking_Update стол:

CREATE TABLE "tracking_update" (
    "id" int(11) NOT NULL AUTO_INCREMENT, 
    "order_id" int(11) DEFAULT NULL, 
    "ship_update_date" datetime DEFAULT NULL, 
    "message" varchar(400) DEFAULT NULL, 
    "location" varchar(100) DEFAULT NULL, 
    "status_type" varchar(2) DEFAULT NULL, 
    "last_updated" datetime DEFAULT NULL, 
    "hash" varchar(32) DEFAULT NULL, 
    PRIMARY KEY ("id"), 
    KEY "order_id" ("order_id"), 
    KEY "tracking_update_idx2" ("status_type"), 
    KEY "tracking_update_idx3" ("ship_update_date"), 
    CONSTRAINT "tracking_update_ibfk_1" FOREIGN KEY ("order_id") REFERENCES "order" ("id") 
); 

Manifest стол:

CREATE TABLE "manifest" (
    "id" int(11) NOT NULL AUTO_INCREMENT, 
    "upload_date" datetime DEFAULT NULL, 
    "name" varchar(100) DEFAULT NULL, 
    "destination_gateway" varchar(40) DEFAULT NULL, 
    "arrived" tinyint(1) DEFAULT NULL, 
    "customer_code" varchar(50) DEFAULT NULL, 
    "upload_user" varchar(50) DEFAULT NULL, 
    "trip_id" int(11) DEFAULT NULL, 
    PRIMARY KEY ("id") 
); 

Здесь также EXPLAIN на оператора выбора экспортируется с помощью JSON:

{ 
    "data": 
    [ 
     { 
      "id": 1, 
      "select_type": "PRIMARY", 
      "table": "m", 
      "type": "ALL", 
      "possible_keys": "PRIMARY", 
      "key": null, 
      "key_len": null, 
      "ref": null, 
      "rows": 220, 
      "Extra": "Using where" 
     }, 
     { 
      "id": 1, 
      "select_type": "PRIMARY", 
      "table": "o", 
      "type": "ref", 
      "possible_keys": "manifest_id", 
      "key": "manifest_id", 
      "key_len": "5", 
      "ref": "fgw247.m.id", 
      "rows": 246, 
      "Extra": "Using index" 
     }, 
     { 
      "id": 1, 
      "select_type": "PRIMARY", 
      "table": "tu", 
      "type": "ref", 
      "possible_keys": "order_id", 
      "key": "order_id", 
      "key_len": "5", 
      "ref": "fgw247.o.id", 
      "rows": 7, 
      "Extra": "Using where" 
     }, 
     { 
      "id": 1, 
      "select_type": "PRIMARY", 
      "table": "<derived2>", 
      "type": "ref", 
      "possible_keys": "<auto_key0>", 
      "key": "<auto_key0>", 
      "key_len": "11", 
      "ref": "fgw247.o.id,fgw247.tu.last_updated", 
      "rows": 13, 
      "Extra": "Using index" 
     }, 
     { 
      "id": 2, 
      "select_type": "DERIVED", 
      "table": "tracking_update", 
      "type": "index", 
      "possible_keys": "order_id", 
      "key": "order_id", 
      "key_len": "5", 
      "ref": null, 
      "rows": 1388275, 
      "Extra": null 
     } 
    ] 
} 

Любая помощь приветствуется

UPDATE

Это текущий запрос, который я использую, который гораздо быстрее:

SELECT 
    o.*, tu.* 
FROM 
    fgw247.`order` o 
JOIN 
    manifest m 
ON 
    o.`manifest_id` = m.`id` 

JOIN 
    `tracking_update` tu 
ON 
    tu.`order_id` = o.`id` and tu.`ship_update_date` = (select max(last_updated) as last_updated from tracking_update where order_id = o.`id` group by order_id) 
WHERE 
    m.`upload_date` >= '2015-12-14 11:50:12' 
    OR 
     (o.`delivery_date` IS NULL AND m.`upload_date` < '2015-12-14 11:50:12') 
LIMIT 100 

ответ

1

Consdider на следующих вариантов оптимизировать выполнение вашего запроса:

  1. Присоединиться к типам: вы уверены, что вам нужны левое и правое соединения? Вы хотите сообщить о заказах, которые даже не включены в манифест? Если нет, используйте внутреннее соединение вместо левого и правого.

  2. Дополнительные индексы: Нет индекса в поле manifest.upload_date, вы должны добавить его. Я также создал бы составной индекс в полях order_id, update_date и status_type (в этом порядке!) В таблице tracking_update.

  3. Использование union вместо or критерий где: Mysql традиционно не очень хорошо на оптимизацию or критерия в where положений. Поэтому включите (m.upload_date >= '2015-12-12 00:00:00') or (m.upload_date <='2015-12-12 00:00:00' and tu.status_type != 'D' and tu.status_type != 'XD') в объединение с двумя запросами. 2 запроса будут такими же, как и существующий запрос до той части. 1-й запрос будет иметь только условие (m.upload_date >= '2015-12-12 00:00:00') в предложении where, а второе - для критериев (m.upload_date <='2015-12-12 00:00:00' and tu.status_type != 'D' and tu.status_type != 'XD').

Если вы добавляете новые индексы, тогда PLS проверяет, использует ли запрос их, запустив новое объяснение.

+0

Благодарим вас за ответ. См. Мой обновленный запрос. Я все еще не уверен, что понимаю союз. Вы хотите создать два разных оператора выбора? – Jimmy

+0

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

+0

Я знаю, что предпочитаю иметь производную версию таблицы по вашему выбору, потому что в случае производной таблицы подзапрос выполняется только один раз. С вашей версией существует риск того, что для каждой записи подзапрос выполняется отдельно. Однако, если соединения удалят большую часть записей в производной таблице, то решение в вашей обновленной версии также может быть быстрым. Я не знаю ваших данных, поэтому не могу комментировать это. – Shadow

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