Mysql select查询,带索引的子查询执行时间长(28秒)
我试图在MYsql上运行下面的查询,该查询运行时间太长。 我需要为每个供应商提取: 采购总额 总销售额 如果供应商的期初余额不同于null,则在子查询的where子句中使用,否则使用“2020-01-01” 以下是我正在使用的查询:Mysql select查询,带索引的子查询执行时间长(28秒),mysql,subquery,Mysql,Subquery,我试图在MYsql上运行下面的查询,该查询运行时间太长。 我需要为每个供应商提取: 采购总额 总销售额 如果供应商的期初余额不同于null,则在子查询的where子句中使用,否则使用“2020-01-01” 以下是我正在使用的查询: SELECT sup.name AS supplier_name, sup.id AS supplier_id, sup.opening_balance_date, sup.opening_balance AS opening_balance, ( SEL
SELECT
sup.name AS supplier_name,
sup.id AS supplier_id,
sup.opening_balance_date,
sup.opening_balance AS opening_balance,
(
SELECT
IFNULL(SUM(pop.quantity * pop.cost),0) AS total_purchases
FROM purchase_order_products pop
JOIN purchase_order po ON
pop.purchase_order_id = po.id
WHERE
DATE(po.created_at) >= IFNULL(sup.opening_balance_date, '2020-01-01')
AND DATE(po.created_at) < '2020-03-01'
AND po.status = 'approved'
AND po.supplier_id = sup.id
) as total_purchases,
(
SELECT
IFNULL(sum(soi.total_cost),0) AS total_sales
FROM
sales_order_item soi
JOIN sales_order so use index (date_status_completed) ON
so.id = soi.sales_order_id
WHERE
soi.total_cost > 0
AND soi.supplier_id = sup.id
AND so.order_status = 'complete'
AND so.completed_returned = 0
AND so.desired_delivery_date >= IFNULL(sup.opening_balance_date, '2020-01-01')
) AS total_sales
FROM supplier sup
WHERE sup.is_active = 1
group by sup.id
ORDER BY sup.name;
我认为问题就在这里:soi.supplier\u id=sup.id
下面是解释
这里是数据库
创建查询:
--
-- Table structure for table `purchase_order`
--
CREATE TABLE `purchase_order` (
`id` int(11) NOT NULL,
`supplier_id` int(11) NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`status` varchar(150) DEFAULT 'Pending',
`total` float NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `purchase_order_products`
--
CREATE TABLE `purchase_order_products` (
`id` int(11) NOT NULL,
`purchase_order_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`quantity` int(50) NOT NULL,
`cost` decimal(15,3) NOT NULL,
`reporting_quantity` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `sales_order`
--
CREATE TABLE `sales_order` (
`id` varchar(20) NOT NULL,
`order_status` varchar(50) DEFAULT NULL,
`desired_delivery_date` date DEFAULT NULL,
`completed_returned` int(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `sales_order_item`
--
CREATE TABLE `sales_order_item` (
`id` varchar(20) NOT NULL,
`sales_order_id` varchar(20) NOT NULL,
`total_cost` float NOT NULL DEFAULT '0',
`supplier_id` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `supplier`
--
CREATE TABLE `supplier` (
`id` int(11) NOT NULL,
`name` varchar(400) NOT NULL,
`opening_balance` decimal(10,2) NOT NULL DEFAULT '0.00',
`opening_balance_date` date DEFAULT NULL,
`is_active` tinyint(4) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Indexes for dumped tables
--
--
-- Indexes for table `credit_note`
--
-- Indexes for table `purchase_order`
--
ALTER TABLE `purchase_order`
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`),
ADD KEY `supplier_id` (`supplier_id`),
ADD KEY `created_at` (`created_at`,`status`),
ADD KEY `supplier_id_2` (`supplier_id`,`created_at`),
ADD KEY `id_date_status` (`supplier_id`,`created_at`,`status`) USING BTREE;
--
-- Indexes for table `purchase_order_products`
--
ALTER TABLE `purchase_order_products`
ADD PRIMARY KEY (`id`),
ADD KEY `purchase_order_id` (`purchase_order_id`),
ADD KEY `product_id` (`product_id`),
ADD KEY `cost` (`cost`,`reporting_quantity`);
--
-- Indexes for table `sales_order`
--
ALTER TABLE `sales_order`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `idx_id` (`id`),
ADD KEY `idx_desired_delivery_date` (`desired_delivery_date`),
ADD KEY `date_status_completed` (`order_status`,`desired_delivery_date`,`completed_returned`) USING BTREE,
ADD KEY `completed_returned` (`completed_returned`),
ADD KEY `order_status` (`order_status`),
ADD KEY `order_status_2` (`order_status`,`completed_returned`);
--
-- Indexes for table `sales_order_item`
--
ALTER TABLE `sales_order_item`
ADD PRIMARY KEY (`id`),
ADD KEY `sales_order_id` (`sales_order_id`),
ADD KEY `reporting_supplier` (`supplier_id`) USING BTREE,
ADD KEY `total_cost` (`total_cost`),
ADD KEY `supplier_cost` (`supplier_id`,`total_cost`) USING BTREE,
ADD KEY `supplier_id` (`supplier_id`);
--
-- Indexes for table `supplier`
--
ALTER TABLE `supplier`
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`),
ADD KEY `is_active` (`is_active`);
--
-- AUTO_INCREMENT for table `purchase_order`
--
ALTER TABLE `purchase_order`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=26763;
--
-- AUTO_INCREMENT for table `purchase_order_products`
--
ALTER TABLE `purchase_order_products`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=41884;
--
-- AUTO_INCREMENT for table `supplier`
--
ALTER TABLE `supplier`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=182;
没有足够的信息来回答这个问题,但是您在第二个子查询中使用的USE INDEX子句可能会迫使MySQL扫描更多的行。尝试删除使用索引日期\u状态\u已完成。主键是唯一键:
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`), -- Toss this; it is redundant
不要混合使用INT和VARCHAR:
当您不需要执行以下操作时,不要使用日期:
DATE(po.created_at) < '2020-03-01'
对mysql版本8.0.19上测试的查询的修订,假设purchase\u order和sales\u order\u item表彼此稀疏,但两者都应该有一个外键引用supplier表,但不一定是supplier上的任何相同记录 通过将“采购订单”或“销售订单”项目下推到单个EXISTS子查询中,然后将另一个转换为左外部联接,可以减少必要的扫描次数。 SUM的功能消除了对采购订单产品执行空合并操作的需要。 由于sales_order.completed_returned是一个布尔整数,因此我们可以将其合并到1-so.completed_returned中,将“returned”项目的总成本转换为聚合中无害的零;同样,最大化也避免了总成本>0筛选条件的需要。 此外,如果每个采购订单供应商也是销售订单供应商,则可以使用内部连接替换左侧外部连接,以获得正确的输出。 最后,如前所述,这里也不用麻烦使用DATE,因为条件已经暗示了它 挑选 供应商名称作为供应商名称, 作为供应商的sup.id\u id, MINsup.期初余额日期作为期初余额日期, MINsup.期初余额作为期初余额, 总采购数量*总采购成本, 合并最大soi.总成本之和,0*1-so.已完成退货,0作为总销售额 来自供应商支持、采购订单和产品pop 左侧外部连接销售\订单\ soi上的项目soi.supplier\u id=sup.id 左外接销售订单等 so.id=soi.sales\u order\u id和 so.order_status='complete'和 so.desired_delivery_date>=合并期初余额_date,'2020-01-01' 其中sup.is_active=1 存在 选择1 来自采购订单po 哪里 订单创建日期>=合并支持期初余额日期'2020-01-01' 并在<'2020-03-01'处创建po.U 采购订单状态=‘已批准’ 采购订单供应商id=供应商id 和pop.purchase\u order\u id po.id 按辅助id、辅助名称分组 按供应商名称订购 解释格式=树
-> Sort: <temporary>.name
-> Table scan on <temporary>
-> Aggregate using temporary table
-> Nested loop inner join (cost=1.75 rows=1)
-> Nested loop inner join (cost=1.40 rows=1)
-> Nested loop left join (cost=1.05 rows=1)
-> Nested loop left join (cost=0.70 rows=1)
-> Index lookup on sup using is_active (is_active=1) (cost=0.35 rows=1)
-> Index lookup on soi using reporting_supplier (supplier_id=sup.id) (cost=0.35 rows=1)
-> Filter: ((so.order_status = \'complete\') and (so.desired_delivery_date >= coalesce(sup.opening_balance_date,\'2020-01-01\'))) (cost=0.35 rows=1)
-> Single-row index lookup on so using PRIMARY (id=soi.sales_order_id) (cost=0.35 rows=1)
-> Filter: ((po.created_at >= coalesce(sup.opening_balance_date,\'2020-01-01\')) and (po.created_at < TIMESTAMP\'2020-03-01 00:00:00\') and (po. = \'approved\')) (cost=0.35 rows=1)
-> Index lookup on po using supplier_id (supplier_id=sup.id) (cost=0.35 rows=1)
-> Index lookup on pop using purchase_order_id (purchase_order_id=po.id), with index condition: (pop.purchase_order_id <=> po.id) (cost=0.35 rows=1)
看看这在您的用例中是否能更好地工作——如果不知道所有相关表格的内容,就很难确切地知道重新工作将如何进行
粗略地看一下解释计划表明,可以通过对该查询使用半连接和BNL/NO\n优化来进一步优化。我已经删除了这个:使用索引日期\u状态\u完成。相同的结果以文本形式写出表格和字段,而不仅仅是屏幕抓图,张贴每个表格中的一些示例数据,以便人们可以在不准备的情况下制作SQLFIDLE。至少张贴SHOW CREATE table sales_order的结果。索引日期\u status\u completed似乎不太理想。我在文本中添加了表结构。是否有公共场所可以导入我的数据库,您可以在那里访问它?这对我帮助很大。我用你的提示现在查询执行时间是3秒。感谢you@Mohammad-让我们看看解释选择。。。变更后的等;可能还有更多的事情需要调整。
DATE(po.created_at) < '2020-03-01'
po.created_at < '2020-03-01'
sup: (is_active, id)
so: (completed_returned, order_status, desired_delivery_date, id)
soi: (supplier_id, total_cost, sales_order_id)
po: (supplier_id, status, created_at, id)
-> Sort: <temporary>.name
-> Table scan on <temporary>
-> Aggregate using temporary table
-> Nested loop inner join (cost=1.75 rows=1)
-> Nested loop inner join (cost=1.40 rows=1)
-> Nested loop left join (cost=1.05 rows=1)
-> Nested loop left join (cost=0.70 rows=1)
-> Index lookup on sup using is_active (is_active=1) (cost=0.35 rows=1)
-> Index lookup on soi using reporting_supplier (supplier_id=sup.id) (cost=0.35 rows=1)
-> Filter: ((so.order_status = \'complete\') and (so.desired_delivery_date >= coalesce(sup.opening_balance_date,\'2020-01-01\'))) (cost=0.35 rows=1)
-> Single-row index lookup on so using PRIMARY (id=soi.sales_order_id) (cost=0.35 rows=1)
-> Filter: ((po.created_at >= coalesce(sup.opening_balance_date,\'2020-01-01\')) and (po.created_at < TIMESTAMP\'2020-03-01 00:00:00\') and (po. = \'approved\')) (cost=0.35 rows=1)
-> Index lookup on po using supplier_id (supplier_id=sup.id) (cost=0.35 rows=1)
-> Index lookup on pop using purchase_order_id (purchase_order_id=po.id), with index condition: (pop.purchase_order_id <=> po.id) (cost=0.35 rows=1)