我有以下查询(带子查询):
SELECT ROUND(SUM(cb.points)) AS points
FROM clients_bonuses cb
WHERE cb.cb_year = 2016
AND cb.account_id IN (
SELECT CONCAT('85500/',uc.contract)
FROM users_contracts uc , users_details ud
WHERE ud.id=uc.users_details_id
AND uc.platform_id = 1
AND ud.id=6 )
在我的本地MySQL服务器(10.1.9-MariaDB
)上运行完全正常( 0.2秒),但是,在我的生产MySQL服务器(5.5.46-0ubuntu0.14.04.2
)上运行 35秒。完成。
我的本地数据库是生产数据库的完全复制,硬件配置仅因视频适配器而异(本地具有内置Intel Graphics)。
我的问题是 - 可能是问题的原因是什么?我可以(以及如何)优化此查询吗?
来自EXPLAIN的结果上述查询 (注意FirstMatch(cb); Using join buffer (flat, BNL join)
中的差异)
(生产服务器):
"id" "select_type" "table" "type" "possible_keys" "key" "key_len" "ref" "rows" "Extra"
"1" "PRIMARY" "cb" "ALL" \N \N \N \N "394886" "Using where"
"2" "DEPENDENT SUBQUERY" "ud" "const" "PRIMARY" "PRIMARY" "4" "const" "1" "Using index"
"2" "DEPENDENT SUBQUERY" "uc" "ALL" \N \N \N \N "12243" "Using where"
(本地机器):
"id" "select_type" "table" "type" "possible_keys" "key" "key_len" "ref" "rows" "Extra"
"1" "PRIMARY" "ud" "const" "PRIMARY" "PRIMARY" "4" "const" "1" "Using index"
"1" "PRIMARY" "cb" "ALL" \N \N \N \N "394537" "Using where"
"1" "PRIMARY" "uc" "ALL" \N \N \N \N "12238" "Using where; FirstMatch(cb); Using join buffer (flat, BNL join)"
表(由于此编辑器,从表列名称中删除了引号):
users_contracts
CREATE TABLE users_contracts (
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
users_details_id INT(11) NOT NULL DEFAULT '0',
platform_id INT(11) NOT NULL DEFAULT '0',
contract VARCHAR(20) NOT NULL DEFAULT '',
createdon TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
createdby INT(11) NOT NULL DEFAULT '0',
updatedon TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
updatedby INT(11) NOT NULL DEFAULT '0',
portfolio_id INT(11) NULL DEFAULT NULL,
PRIMARY KEY (id)
)
COLLATE='cp1251_general_ci'
ENGINE=MyISAM
AUTO_INCREMENT=13617;
clients_bonuses
CREATE TABLE clients_bonuses (
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
account_id VARCHAR(255) NOT NULL DEFAULT '',
bi_id INT(11) NOT NULL DEFAULT '0',
cb_year YEAR NOT NULL DEFAULT '0000',
cb_month TINYINT(1) NOT NULL DEFAULT '0',
cb_day TINYINT(1) NOT NULL DEFAULT '0',
bonus DECIMAL(10,4) NOT NULL DEFAULT '0.0000',
amount DECIMAL(20,4) NOT NULL DEFAULT '0.0000',
points DECIMAL(20,4) NOT NULL DEFAULT '0.0000',
month_lots DECIMAL(10,4) NOT NULL DEFAULT '0.0000',
PRIMARY KEY (id)
)
COLLATE='cp1251_general_ci'
ENGINE=MyISAM
AUTO_INCREMENT=395015;
users_details
CREATE TABLE users_details (
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
email VARCHAR(100) NOT NULL DEFAULT '',
phone VARCHAR(32) NULL DEFAULT NULL,
interest TINYINT(1) NOT NULL DEFAULT '0',
contact TINYINT(1) NOT NULL DEFAULT '0',
instrument TINYINT(1) NOT NULL DEFAULT '0',
instrument1 TINYINT(1) NOT NULL DEFAULT '0',
comments TEXT NOT NULL,
reminder TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
reminder1 TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
last_accessed TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
dealer INT(11) NOT NULL DEFAULT '0',
dealer1 INT(11) NOT NULL DEFAULT '0',
real_client TINYINT(1) NOT NULL DEFAULT '0',
real_client_meta TINYINT(1) NOT NULL DEFAULT '0',
real_client_bgtrader TINYINT(1) NOT NULL DEFAULT '0',
real_client_bmpro TINYINT(1) NOT NULL DEFAULT '0',
advmails TINYINT(1) NOT NULL DEFAULT '0',
analysis_status TINYINT(4) NULL DEFAULT '0',
real_client_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
real_client_meta_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
real_client_bgtrader_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
real_client_bmpro_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
reg_status TINYINT(1) NOT NULL DEFAULT '0',
password VARCHAR(100) NOT NULL DEFAULT '',
real_name VARCHAR(100) NOT NULL DEFAULT '',
date_of_first_points TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
trader_points INT(11) NOT NULL DEFAULT '0',
metatrader_points INT(11) NOT NULL DEFAULT '0',
bgtrader_points INT(11) NOT NULL DEFAULT '0',
bmpro_points INT(11) NOT NULL DEFAULT '0',
vps_status TINYINT(1) NOT NULL DEFAULT '0',
vps_username VARCHAR(50) NOT NULL DEFAULT '',
vps_password VARCHAR(50) NOT NULL DEFAULT '',
vps_start_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
metatrader5_points INT(11) NOT NULL DEFAULT '0',
mt_points INT(11) NOT NULL DEFAULT '0',
reminder_from INT(11) NOT NULL DEFAULT '0',
reminder_mt4 DATE NULL DEFAULT NULL,
taken_bonus DATE NOT NULL DEFAULT '0000-00-00',
taken_bonus_from DATE NOT NULL DEFAULT '0000-00-00',
experience INT(11) NOT NULL DEFAULT '0',
invalid_phone TINYINT(4) NOT NULL DEFAULT '0',
invalid_email TINYINT(4) NOT NULL DEFAULT '0',
number_of_calls INT(11) NOT NULL DEFAULT '0',
last_call DATETIME NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE INDEX email (email),
INDEX users_details_email (email(30))
)
COLLATE='cp1251_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=63820;
答案 0 :(得分:1)
您应该将ud.id=uc.users_details_id AND uc.platform_id = 1 AND ud.id=
6从WHERE
移至INNER JOIN
为了让它正常工作,为uc.users_details_id
添加INDEX ud.id是主键,因此它已被索引。
答案 1 :(得分:1)
IN ( SELECT ... )
效果不佳。
将其转换为JOIN
可能会使聚合(SUM
)膨胀。
FROM ( SELECT ... )
消除了IN
问题,但存在“膨胀”的风险。问题
所以,EXISTS ( SELECT * ... )
可能是最好的答案:
SELECT ROUND(SUM(cb.points)) AS points
FROM clients_bonuses cb
WHERE cb.cb_year = 2016
AND EXISTS
( SELECT *
FROM users_contracts uc
JOIN users_details ud ON ud.id = uc.users_details_id
WHERE uc.platform_id = 1
AND ud.id=6
AND CONCAT('85500/', uc.contract) = cb.account_id
)
您可以从中受益:
users_contracts : INDEX(users_details_id, platform_id) -- in either order
clients_bonuses : INDEX(cb_year, account_id, points) -- in that order
看起来你可以通过摆脱users_details进一步加快速度:
SELECT ROUND(SUM(cb.points)) AS points
FROM clients_bonuses cb
WHERE cb.cb_year = 2016
AND EXISTS
( SELECT *
FROM users_contracts uc
WHERE uc.platform_id = 1
AND uc.users_details_id = 6
AND CONCAT('85500/', uc.contract) = cb.account_id
)
自4.1以来,这些公式可能适用于MySQL / MariaDB的所有变体。