如何提高此子查询的性能

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。