Mysql: 是否有更高效的嵌套聚合更新方式?
Mysql: Is there a more performant way of updating with nested aggregation?
我正在尝试根据 2 个嵌套查询的总和计算结果更新 table 中的单个值。这是我试过的查询,它似乎可以工作但是很慢。
update WALLET w
set total =
(
select
min(
(select sum(earned) from WALLET_TRANSACTION t where t.wallet_id = w.id and type not in ('FEE', 'REDEEM')) +
(select sum(earned) from WALLET_TRANSACTION t where t.wallet_id = w.id and type in ('FEE', 'REDEEM'))
) as sumredeemed
from WALLET_TRANSACTION
);
目前更新单个钱包条目需要 52.77 秒。
无论如何我可以优化此查询以在 Mysql 8 中加快它的速度吗?
钱包table:
mysql> show create table WALLET\G
*************************** 1. row ***************************
Table: WALLET
Create Table: CREATE TABLE `WALLET` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`reference` varchar(50) NOT NULL,
`total` decimal(8,2) NOT NULL DEFAULT '0.00',
`user_id` bigint(20) NOT NULL DEFAULT '0',
`target` decimal(8,2) NOT NULL DEFAULT '0.00',
`target_manually_adjusted` bit(1) DEFAULT b'0',
`deleted` bit(1) DEFAULT b'0',
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(50) DEFAULT NULL,
`last_modified` datetime DEFAULT CURRENT_TIMESTAMP,
`last_modified_by` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `reference` (`reference`,`user_id`),
KEY `wallet_reference` (`reference`),
KEY `wallet_user_id` (`user_id`),
KEY `wallet_target_manually_adjusted` (`target_manually_adjusted`)
) ENGINE=InnoDB AUTO_INCREMENT=1611167816726 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.02 sec)
WALLET_TRANSACTION:
Table: WALLET_TRANSACTION
Create Table: CREATE TABLE `WALLET_TRANSACTION` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`wallet_id` bigint(20) NOT NULL DEFAULT '0',
`user_id` bigint(20) NOT NULL DEFAULT '0',
`merchant_id` bigint(20) DEFAULT NULL,
`merchant_name` varchar(100) DEFAULT NULL,
`merchant_logo_url` varchar(200) DEFAULT NULL,
`product_id` bigint(20) DEFAULT '0',
`product_redeem_window_id` bigint(20) DEFAULT NULL,
`offer_id` bigint(20) DEFAULT NULL,
`outlet_id` bigint(20) DEFAULT NULL,
`offer_type` varchar(30) DEFAULT NULL,
`product_delta_type` varchar(30) DEFAULT NULL,
`external_reference` varchar(100) DEFAULT NULL,
`client_external_reference` varchar(100) DEFAULT NULL,
`earned` decimal(8,2) NOT NULL DEFAULT '0.00',
`spent` decimal(8,2) NOT NULL DEFAULT '0.00',
`earned_percent` decimal(8,2) DEFAULT '0.00',
`partner` varchar(10) DEFAULT NULL,
`type` varchar(25) DEFAULT NULL,
`status` varchar(100) NOT NULL,
`note` varchar(1000) DEFAULT NULL,
`commission` decimal(8,2) DEFAULT '0.00',
`date_of_transaction` datetime DEFAULT NULL,
`approved` bit(1) DEFAULT b'0',
`enabled` bit(1) DEFAULT b'1',
`deleted` bit(1) DEFAULT b'0',
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(50) DEFAULT NULL,
`last_modified` datetime DEFAULT CURRENT_TIMESTAMP,
`last_modified_by` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`,`wallet_id`),
KEY `wallet_transaction_type` (`type`),
KEY `wallet_transaction_status` (`status`),
KEY `wallet_transaction_wallet_id` (`wallet_id`),
KEY `wallet_transaction_approved` (`approved`),
KEY `wallet_transaction_spent` (`spent`),
KEY `wallet_transaction_earned` (`earned`),
KEY `wallet_transaction_prwid` (`product_redeem_window_id`),
KEY `wallet_transaction_client_external_reference` (`client_external_reference`)
) ENGINE=InnoDB AUTO_INCREMENT=1611097810451 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
嗯。 . .您似乎只需要 earned
的总和来匹配行:
update WALLET w
set total = (select sum(t.earned)
from WALLET_TRANSACTION t
where t.wallet_id = w.id
);
我认为没有理由将其分为收费和免费,然后将结果加在一起。
不用子查询也可以这样做:
START TRANSACTION;
UPDATE WALLET SET total = 0;
UPDATE WALLET AS w JOIN WALLET_TRANSACTION AS t ON t.wallet_id = w.id
SET w.total = w.total + t.earned
COMMIT;
确保 t.wallet_id
上有索引。
我假设 w.id
是一个主键,所以它已经被索引了。
删除最后的 from WALLET_TRANSACTION
-- 您不需要为 table 的每一行重新计算。
总体来说还是可以的
SELECT expression;
-- 即没有 FROM。在某些情况下,您可能需要伪 table dual
:
SELECT expression FROM DUAL;
另一件事...MIN(col)
用于跨 行 的聚合,而 LEAST(expression, expression, ...)
是这些表达式的“最小值”。因此,也将 MIN
更改为 LEAST
。
我正在尝试根据 2 个嵌套查询的总和计算结果更新 table 中的单个值。这是我试过的查询,它似乎可以工作但是很慢。
update WALLET w
set total =
(
select
min(
(select sum(earned) from WALLET_TRANSACTION t where t.wallet_id = w.id and type not in ('FEE', 'REDEEM')) +
(select sum(earned) from WALLET_TRANSACTION t where t.wallet_id = w.id and type in ('FEE', 'REDEEM'))
) as sumredeemed
from WALLET_TRANSACTION
);
目前更新单个钱包条目需要 52.77 秒。
无论如何我可以优化此查询以在 Mysql 8 中加快它的速度吗?
钱包table:
mysql> show create table WALLET\G
*************************** 1. row ***************************
Table: WALLET
Create Table: CREATE TABLE `WALLET` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`reference` varchar(50) NOT NULL,
`total` decimal(8,2) NOT NULL DEFAULT '0.00',
`user_id` bigint(20) NOT NULL DEFAULT '0',
`target` decimal(8,2) NOT NULL DEFAULT '0.00',
`target_manually_adjusted` bit(1) DEFAULT b'0',
`deleted` bit(1) DEFAULT b'0',
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(50) DEFAULT NULL,
`last_modified` datetime DEFAULT CURRENT_TIMESTAMP,
`last_modified_by` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `reference` (`reference`,`user_id`),
KEY `wallet_reference` (`reference`),
KEY `wallet_user_id` (`user_id`),
KEY `wallet_target_manually_adjusted` (`target_manually_adjusted`)
) ENGINE=InnoDB AUTO_INCREMENT=1611167816726 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.02 sec)
WALLET_TRANSACTION:
Table: WALLET_TRANSACTION
Create Table: CREATE TABLE `WALLET_TRANSACTION` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`wallet_id` bigint(20) NOT NULL DEFAULT '0',
`user_id` bigint(20) NOT NULL DEFAULT '0',
`merchant_id` bigint(20) DEFAULT NULL,
`merchant_name` varchar(100) DEFAULT NULL,
`merchant_logo_url` varchar(200) DEFAULT NULL,
`product_id` bigint(20) DEFAULT '0',
`product_redeem_window_id` bigint(20) DEFAULT NULL,
`offer_id` bigint(20) DEFAULT NULL,
`outlet_id` bigint(20) DEFAULT NULL,
`offer_type` varchar(30) DEFAULT NULL,
`product_delta_type` varchar(30) DEFAULT NULL,
`external_reference` varchar(100) DEFAULT NULL,
`client_external_reference` varchar(100) DEFAULT NULL,
`earned` decimal(8,2) NOT NULL DEFAULT '0.00',
`spent` decimal(8,2) NOT NULL DEFAULT '0.00',
`earned_percent` decimal(8,2) DEFAULT '0.00',
`partner` varchar(10) DEFAULT NULL,
`type` varchar(25) DEFAULT NULL,
`status` varchar(100) NOT NULL,
`note` varchar(1000) DEFAULT NULL,
`commission` decimal(8,2) DEFAULT '0.00',
`date_of_transaction` datetime DEFAULT NULL,
`approved` bit(1) DEFAULT b'0',
`enabled` bit(1) DEFAULT b'1',
`deleted` bit(1) DEFAULT b'0',
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(50) DEFAULT NULL,
`last_modified` datetime DEFAULT CURRENT_TIMESTAMP,
`last_modified_by` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`,`wallet_id`),
KEY `wallet_transaction_type` (`type`),
KEY `wallet_transaction_status` (`status`),
KEY `wallet_transaction_wallet_id` (`wallet_id`),
KEY `wallet_transaction_approved` (`approved`),
KEY `wallet_transaction_spent` (`spent`),
KEY `wallet_transaction_earned` (`earned`),
KEY `wallet_transaction_prwid` (`product_redeem_window_id`),
KEY `wallet_transaction_client_external_reference` (`client_external_reference`)
) ENGINE=InnoDB AUTO_INCREMENT=1611097810451 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
嗯。 . .您似乎只需要 earned
的总和来匹配行:
update WALLET w
set total = (select sum(t.earned)
from WALLET_TRANSACTION t
where t.wallet_id = w.id
);
我认为没有理由将其分为收费和免费,然后将结果加在一起。
不用子查询也可以这样做:
START TRANSACTION;
UPDATE WALLET SET total = 0;
UPDATE WALLET AS w JOIN WALLET_TRANSACTION AS t ON t.wallet_id = w.id
SET w.total = w.total + t.earned
COMMIT;
确保 t.wallet_id
上有索引。
我假设 w.id
是一个主键,所以它已经被索引了。
删除最后的 from WALLET_TRANSACTION
-- 您不需要为 table 的每一行重新计算。
总体来说还是可以的
SELECT expression;
-- 即没有 FROM。在某些情况下,您可能需要伪 table dual
:
SELECT expression FROM DUAL;
另一件事...MIN(col)
用于跨 行 的聚合,而 LEAST(expression, expression, ...)
是这些表达式的“最小值”。因此,也将 MIN
更改为 LEAST
。