如何提高此子查询的性能
how to improve performance for this subquery
我使用此查询来获取聚合子查询的结果,但性能非常差,它需要大约 12 秒,尽管行数 只有 92,
tborders table 有大约 37000 行,但它认为这两个时间要花很多时间
你能对这个查询提出一些改进建议吗?
select id, group_name,merchant_id,
(select count(id) from tborders where tborders.group_id = og.id) as ords_count,
(select SUM(order_price) from tborders where tborders.group_id = og.id) as total_order_price,
(select SUM(delivery_price) from tborders where tborders.group_id = og.id) as total_delivery_price
from tborders_groups og
这也是此 table
的架构
CREATE TABLE `tborders_groups` (
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`merchant_id` int(11) DEFAULT NULL,
`group_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='orders groubs';
--
-- Dumping data for table `tborders_groups`
--
--
-- Indexes for table `tborders_groups`
--
ALTER TABLE `tborders_groups`
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`),
ADD KEY `merchant_id` (`merchant_id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `tborders_groups`
--
ALTER TABLE `tborders_groups`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=95;
COMMIT;
这是 tborders 的架构
CREATE TABLE `tborders` (
`id` int(11) NOT NULL,
`device_type` varchar(255) DEFAULT 'android',
`order_code` int(20) NOT NULL,
`order_status` tinyint(1) NOT NULL DEFAULT 1,
`merchant_id` int(11) NOT NULL,
`driver_id` int(11) DEFAULT NULL,
`client_id` int(11) DEFAULT 0,
`from_lng` decimal(9,6) DEFAULT NULL,
`to_lat` decimal(8,6) DEFAULT NULL,
`to_lng` decimal(9,6) DEFAULT NULL,
`distance` int(11) DEFAULT NULL,
`speed` int(11) DEFAULT NULL,
`orders_count` tinyint(1) NOT NULL DEFAULT 0,
`order_address` varchar(500) CHARACTER SET utf8 NOT NULL,
`canceled_at` time DEFAULT NULL,
`taken_at` time DEFAULT NULL,
`canceled_by` enum('merchant','driver') DEFAULT NULL,
`accepted_at` time DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`from_lat` decimal(8,6) DEFAULT NULL,
`order_price` int(11) NOT NULL DEFAULT 0,
`order_type` tinyint(1) NOT NULL DEFAULT 2,
`group_id` int(10) UNSIGNED NOT NULL,
`client_address` varchar(100) CHARACTER SET utf8 NOT NULL,
`delivery_price` int(10) UNSIGNED NOT NULL,
`notes` varchar(100) CHARACTER SET utf8 NOT NULL,
`has_paid` tinyint(1) NOT NULL DEFAULT 0,
`finished_at` timestamp NULL DEFAULT NULL,
`finished_attt` timestamp NULL DEFAULT current_timestamp(),
`area_id` tinyint(4) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table `tborders`
--
--
-- Indexes for dumped tables
--
--
-- Indexes for table `tborders`
--
ALTER TABLE `tborders`
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`),
ADD KEY `order_status` (`order_status`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `tborders`
--
ALTER TABLE `tborders`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=37169;
COMMIT;
您应该为此查询使用联接和分组依据:
select og.id, og.group_name,og.merchant_id, count(tb.id) as ords_count,
sum(tb.order_price) as total_order_price, sum(tb.delivery_price) as total_delivery_price
from tborders_groups og
join tborders tb on tb.group_id=og.id
group by og.id, og.group_name,og.merchant_id
无论如何,查询的时间不(仅)取决于答案的行数。这取决于处理查询以分析所有数据集以获取这些行的时间。
对于您的索引,这应该是您的 tborders 索引:
ALTER TABLE `tborders`
ADD PRIMARY KEY (`id`),
ADD KEY `group_id` (`group_id`),
ADD KEY `order_status` (`order_status`);
将 id 同时作为主键和键是没有意义的,因为主键是唯一的键。
所以 tborders_groups 他们应该是:
ALTER TABLE `tborders_groups`
ADD PRIMARY KEY (`id`),
ADD KEY `merchant_id` (`merchant_id`);
这可能 运行 更快(可能比玉米片更快):
SELECT id, group_name, merchant_id, ct, order_total, delivery_total
FROM (
SELECT group_id,
COUNT(*) AS ct,
SUM(order_price) AS order_total,
SUM(delivery_price) AS delivery_total
FROM tborders
GROUP BY group_id
) t
JOIN tb_orders_groups AS og ON og.id = t.group_id
tborders
上的这个索引也可能有帮助:
INDEX(group_id, order_price, delivery_price)
另外,切换到 InnoDB。
我使用此查询来获取聚合子查询的结果,但性能非常差,它需要大约 12 秒,尽管行数 只有 92, tborders table 有大约 37000 行,但它认为这两个时间要花很多时间
你能对这个查询提出一些改进建议吗?
select id, group_name,merchant_id,
(select count(id) from tborders where tborders.group_id = og.id) as ords_count,
(select SUM(order_price) from tborders where tborders.group_id = og.id) as total_order_price,
(select SUM(delivery_price) from tborders where tborders.group_id = og.id) as total_delivery_price
from tborders_groups og
这也是此 table
的架构CREATE TABLE `tborders_groups` (
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`merchant_id` int(11) DEFAULT NULL,
`group_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='orders groubs';
--
-- Dumping data for table `tborders_groups`
--
--
-- Indexes for table `tborders_groups`
--
ALTER TABLE `tborders_groups`
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`),
ADD KEY `merchant_id` (`merchant_id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `tborders_groups`
--
ALTER TABLE `tborders_groups`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=95;
COMMIT;
这是 tborders 的架构
CREATE TABLE `tborders` (
`id` int(11) NOT NULL,
`device_type` varchar(255) DEFAULT 'android',
`order_code` int(20) NOT NULL,
`order_status` tinyint(1) NOT NULL DEFAULT 1,
`merchant_id` int(11) NOT NULL,
`driver_id` int(11) DEFAULT NULL,
`client_id` int(11) DEFAULT 0,
`from_lng` decimal(9,6) DEFAULT NULL,
`to_lat` decimal(8,6) DEFAULT NULL,
`to_lng` decimal(9,6) DEFAULT NULL,
`distance` int(11) DEFAULT NULL,
`speed` int(11) DEFAULT NULL,
`orders_count` tinyint(1) NOT NULL DEFAULT 0,
`order_address` varchar(500) CHARACTER SET utf8 NOT NULL,
`canceled_at` time DEFAULT NULL,
`taken_at` time DEFAULT NULL,
`canceled_by` enum('merchant','driver') DEFAULT NULL,
`accepted_at` time DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`from_lat` decimal(8,6) DEFAULT NULL,
`order_price` int(11) NOT NULL DEFAULT 0,
`order_type` tinyint(1) NOT NULL DEFAULT 2,
`group_id` int(10) UNSIGNED NOT NULL,
`client_address` varchar(100) CHARACTER SET utf8 NOT NULL,
`delivery_price` int(10) UNSIGNED NOT NULL,
`notes` varchar(100) CHARACTER SET utf8 NOT NULL,
`has_paid` tinyint(1) NOT NULL DEFAULT 0,
`finished_at` timestamp NULL DEFAULT NULL,
`finished_attt` timestamp NULL DEFAULT current_timestamp(),
`area_id` tinyint(4) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table `tborders`
--
--
-- Indexes for dumped tables
--
--
-- Indexes for table `tborders`
--
ALTER TABLE `tborders`
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`),
ADD KEY `order_status` (`order_status`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `tborders`
--
ALTER TABLE `tborders`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=37169;
COMMIT;
您应该为此查询使用联接和分组依据:
select og.id, og.group_name,og.merchant_id, count(tb.id) as ords_count,
sum(tb.order_price) as total_order_price, sum(tb.delivery_price) as total_delivery_price
from tborders_groups og
join tborders tb on tb.group_id=og.id
group by og.id, og.group_name,og.merchant_id
无论如何,查询的时间不(仅)取决于答案的行数。这取决于处理查询以分析所有数据集以获取这些行的时间。
对于您的索引,这应该是您的 tborders 索引:
ALTER TABLE `tborders`
ADD PRIMARY KEY (`id`),
ADD KEY `group_id` (`group_id`),
ADD KEY `order_status` (`order_status`);
将 id 同时作为主键和键是没有意义的,因为主键是唯一的键。
所以 tborders_groups 他们应该是:
ALTER TABLE `tborders_groups`
ADD PRIMARY KEY (`id`),
ADD KEY `merchant_id` (`merchant_id`);
这可能 运行 更快(可能比玉米片更快):
SELECT id, group_name, merchant_id, ct, order_total, delivery_total
FROM (
SELECT group_id,
COUNT(*) AS ct,
SUM(order_price) AS order_total,
SUM(delivery_price) AS delivery_total
FROM tborders
GROUP BY group_id
) t
JOIN tb_orders_groups AS og ON og.id = t.group_id
tborders
上的这个索引也可能有帮助:
INDEX(group_id, order_price, delivery_price)
另外,切换到 InnoDB。