提问者:小点点

为什么使用内连接本身比哪里更快


这是我的桌子:

CREATE TABLE `cerp_oms_order` (
  `id` bigint NOT NULL,
  `company_id` bigint NOT NULL,
  `order_no` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `source_type` int NOT NULL,
  `shop_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `outer_shop` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `origin_status` int NOT NULL,
  `system_status` int NOT NULL,
  `created_time` datetime DEFAULT NULL,
  `paid_time` datetime DEFAULT NULL,
  `sent_time` datetime DEFAULT NULL,
  `end_time` datetime DEFAULT NULL,
  `modify_time` datetime DEFAULT NULL,
  `delivery_deadline_time` datetime DEFAULT NULL,
  `amount` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
  `spu_kind` int NOT NULL,
  `sku_kind` int NOT NULL,
  `total_quantity` decimal(16,4) NOT NULL,
  `buyer_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `outer_buyer_identifier` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  `tax_info` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `warehouse_owner` int DEFAULT NULL,
  `warehouse_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `logistics_type` int NOT NULL,
  `logistics_outer_info` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `delivery_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `delivery_no` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `wave_no` varchar(64) DEFAULT '',
  `is_deleted` tinyint NOT NULL DEFAULT '0',
  `backend_processing_type` tinyint NOT NULL,
  `create_type` tinyint NOT NULL,
  `is_hang_up` tinyint NOT NULL,
  `hang_up_case_type` smallint DEFAULT NULL,
  `hang_up_case_id` bigint DEFAULT NULL,
  `rc_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `rm_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `vat` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `weight` decimal(16,4) NOT NULL DEFAULT '0.0000',
  `volume` decimal(16,4) NOT NULL DEFAULT '0.0000',
  `is_abnormal` tinyint NOT NULL DEFAULT '0',
  `estimate_profit` decimal(16,4) NOT NULL DEFAULT '0.0000',
  `business_man_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `business_man` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `currency` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `net_amount_summary` decimal(19,4) NOT NULL,
  `domestic_amount` decimal(19,4) NOT NULL,
  `secret_key` varchar(64) DEFAULT '',
  `secretKey` varchar(255) DEFAULT NULL,
  `sale_id` bigint DEFAULT NULL,
  `total_refund_include_tax` decimal(16,4) NOT NULL DEFAULT '0.0000',
  `total_refund_money` decimal(16,4) NOT NULL DEFAULT '0.0000',
  `total_refund_tax` decimal(16,4) NOT NULL DEFAULT '0.0000',
  `total_return_goods` decimal(16,2) NOT NULL DEFAULT '0.00',
  PRIMARY KEY (`id`),
  UNIQUE KEY `key_order_no` (`order_no`),
  KEY `idx_order_company_id` (`company_id`,`created_time`),
  KEY `IDX_RM_TIME` (`rm_time`),
  KEY `IDX_IS_ABNORMAL` (`is_abnormal`),
  KEY `cerp_oms_order_company_id_index` (`company_id`),
  KEY `idx_order_company_status_deleted` (`company_id`,`is_deleted`,`system_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='system order table'
explain select *
from cerp_oms_order
         inner join (select id
                     from cerp_oms_order
                     where source_type = 43
                     order by created_time) as tmp using (id);

使用内连接执行:7 ms,获取:109 ms

VS

explain
select *
from cerp_oms_order
where source_type = 43
order by created_time;

使用简单的where子句执行:80 ms,获取:138 ms

我不明白为什么使用内连接可以加快我的sql?


共1个答案

匿名用户

如果你有

INDEX(source_type, created_time)

两种配方都会运行得更快。我认为,第二种会更快。

至于“为什么”。看看第二个做了什么:

  1. 扫描整个表(表扫描),查找source_type=43的行。
  2. 对于每个匹配的行,将该行中的所有列(*)收集到一个临时表中。
  3. 对临时表进行排序。请注意,这比只携带id的子查询要笨重。
  4. 交付结果。

使用INDEX我建议:

  1. 在索引的B树中查找所有source_type=43的行。
  2. 由于这些行是按created_time排序的,所以现在或以后都不需要排序传递。
  3. 这些索引行中的每一个都将包含id。(这是InnoDB获取其余列的方式),所以
  4. 在数据的B树中查找所需的行并拉出所有(*)列。
  5. 交付它们。

请注意,JOIN版本的工作原理基本相同,但是从技术上讲,ORDER BY可能会丢失。如果它确实丢失了,您需要添加第二个ORDER BY,从而强制排序。

(无关)您的KEYcerp_oms_order_company_id_indexcompany_id)可以被删除,因为还有两个以company_id开头的索引。

有关优化索引的更多信息:索引食谱