如何优化连接3个表的SQL查询

时间:2016-01-11 15:23:55

标签: mysql join inner-join right-join

Howdie do,

我有以下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个表:

Order,Manifest和Tracking_Update。

查询将返回符合以下条件的订单:

  1. 订单&lt;任何交付状态30天
  2. 订单&gt; 30天而非送货状态
  3. 如果该订单的最新tracking_update状态类型为&#39; D&#39;则认为订单已下达。或者&#39; XD&#39;

    现在,订单列在Order表中。 Order表有一个名为manifest_id的列,它引用上传订单时创建的清单。

    清单可以有多个订单。这是用于确定订单是否已在过去30天内上传的内容。

    最后,tracking_update表包含每个订单的tracking_updates。订单可以包含多个tracking_updates。

    目前,tracking_update表的记录长度超过1M。

    下面列出了每个表的create语句:

    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")
    );
    

    清单表:

    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")
    );
    

    以下是使用JSON导出的select语句的EXPLAIN:

    {
        "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
            }
        ]
    }
    

    感谢任何帮助

    更新

    这是我当前使用的查询速度更快:

    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 个答案:

答案 0 :(得分:1)

使用以下选项来优化查询的执行:

  1. 加入类型:您确定需要左右连接吗?您想报告甚至不包含在清单中的订单吗?如果没有,则使用内部连接而不是左右。

  2. 其他索引:manifest.upload_date字段上没有索引,您应该添加一个索引。我还会在tracking_update表上的order_id,update_date和status_type字段(按此顺序!)上创建一个复合索引。

  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个查询的联合。 2个查询将与现有查询相同,直到where部分。第一个查询在where子句中只有(m.upload_date >= '2015-12-12 00:00:00')条件,而第二个查询将具有(m.upload_date <='2015-12-12 00:00:00' and tu.status_type != 'D' and tu.status_type != 'XD')条件。

  4. 如果添加新索引,请通过运行新解释来检查查询是否使用它们。