[英]Slow Mysql query after inner joining 3 tables and adding indexes
我發現我的 mysql 查詢非常慢,根據我僅從這個查詢進行的測試,需要 1 秒。 我試過添加索引。 但這似乎效果不佳。 我附上了我的解釋、表格和索引。 我現在僅限於 mysql 5.6。 我不確定還有什么辦法可以讓它更快。 有任何想法嗎?
SELECT symbol.name, symbol.code, tbl_company_data.id as dataid, symbol.id as companyid , tbl_company_data.company_size as size
FROM symbol
INNER JOIN tbl_company_data ON symbol.id=tbl_company_data.company_symbol_id
INNER JOIN tbl_eta metatbl ON metatbl.symbol_id=symbol.id
INNER JOIN tbl_eta metatbl_0 ON metatbl_0.symbol_id=symbol.id
INNER JOIN tbl_eta metatbl_1 ON metatbl_1.symbol_id=symbol.id
WHERE 1 AND ((metatbl_0.meta_key= 'word2' AND metatbl_0.meta_value!=''
AND CAST(metatbl_0.meta_value AS DECIMAL) BETWEEN CAST(0 AS DECIMAL) AND CAST(1000 AS DECIMAL))
AND (metatbl_1.meta_key= 'word1' AND metatbl_1.meta_value!=''
AND CAST(metatbl_1.meta_value AS DECIMAL) BETWEEN CAST(-0.0001 AS DECIMAL) AND CAST(2 AS DECIMAL)))
GROUP BY companyid
HAVING 1
ORDER BY symbol.code DESC
LIMIT 0, 1500
顯示創建表
CREATE TABLE `tbl_exchange_symbol` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`code` varchar(150) NOT NULL,
`name` varchar(255) NOT NULL,
`country` varchar(100) NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=56287 DEFAULT CHARSET=latin1
CREATE TABLE `tbl_company_data` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`company_symbol_id` bigint(20) NOT NULL,
`company_size` double(30,2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `company_symbol_id_2` (`company_symbol_id`),
KEY `company_symbol_id` (`company_symbol_id`)
) ENGINE=MyISAM AUTO_INCREMENT=18361 DEFAULT CHARSET=latin1
CREATE TABLE `tbl_eta` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`symbol_id` bigint(20) NOT NULL,
`meta_key` text NOT NULL,
`meta_value` longtext NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `symbol_id` (`symbol_id`),
KEY `TEST` (`symbol_id`,`meta_key`(255))
) ENGINE=MyISAM AUTO_INCREMENT=470350 DEFAULT CHARSET=latin1
Markdown 樣本數據 tbl_eta
ID | symbol_id | 元密鑰 | 元值 |
---|---|---|---|
1個 | 1個 | 詞1 | 1個 |
2個 | 1個 | 詞2 | 0 |
3個 | 2個 | 詞1 | 0 |
4個 | 2個 | 詞2 | 0 |
5個 | 3個 | 詞1 | 1個 |
6個 | 3個 | 詞2 | 25 |
7 | 4個 | 詞1 | 10 |
8個 | 4個 | 詞2 | 8個 |
9 | 5個 | 詞1 | 3個 |
10 | 5個 | 詞2 | 22 |
象征
ID | 代碼 | 名稱 | 國家 |
---|---|---|---|
1個 | AAA級 | 公司1 | 美國 |
2個 | AAB | 公司2 | 美國 |
3個 | 有聲學協會 | 公司3 | 美國 |
4個 | AAD | 公司4 | 美國 |
5個 | AAE | 公司5 | 美國 |
6個 | AAF | 公司6 | 美國 |
7 | AAG | 公司7 | 美國 |
8個 | AAH | 公司8 | 美國 |
9 | 人工智能 | 公司9 | 美國 |
10 | AAJ | 公司10 | 美國 |
tbl_company_data
ID | 公司_符號_id | 公司規模 |
---|---|---|
1個 | 1個 | 100000 |
2個 | 2個 | 200000 |
3個 | 3個 | 50000000 |
當您必須CAST()
一個列值以在WHERE
子句中使用時,它不再與您可能在該列上擁有的任何索引中存儲的值相同,因此您的索引對於此查詢毫無價值。 更糟糕的是,直到轉換之后,您才能知道給定行中的值是否與子句匹配,這意味着您必須運行代碼以對表中的每一行執行此轉換,即使對於最終將無法通過測試的值也是如此。
這是我們盡量避免使用實體-屬性-值 (EAV) 模式模式的原因之一(在幾個原因中),如在tbl_eta metatbl
表和朋友中所見:如果您需要考慮,則無法真正正確地索引它們不同類型的價值。
相比之下,如果您的數據庫中的值已經是小數,並且您在元表上有一個索引,其中包含symbol_id
、 meta_key
和meta_value
都在同一索引中並按此精確順序,您可以使用索引直接跳轉到哪里匹配的0
將開始進行BETWEEN
比較,遍歷索引直到到達最終值1000
,然后停在那里。 即使每個symbol_id
+ meta_key
組合始終只有一個值,您也應該以這種方式構建索引,因為這樣查詢就可以完全從索引中完全覆蓋,而無需回頭查看表。 它可能會使您的查詢只運行一小部分時間。
優化代碼的一種快速方法是在應用連接操作之前移動過濾條件(包含在WHERE
子句中),如下所示:
SELECT
symbol.name,
symbol.code,
tbl_company_data.id as dataid,
symbol.id as companyid ,
tbl_company_data.company_size as size
FROM
symbol
INNER JOIN
tbl_company_data ON symbol.id=tbl_company_data.company_symbol_id
INNER JOIN
tbl_eta metatbl ON metatbl.symbol_id=symbol.id
INNER JOIN
(
SELECT *
FROM tbl_eta
WHERE meta_key = 'word2'
AND meta_value != ''
AND CAST(meta_value AS DECIMAL) BETWEEN CAST(0 AS DECIMAL) AND CAST(1000 AS DECIMAL)
) metatbl_0 ON metatbl_0.symbol_id=symbol.id
INNER JOIN
(
SELECT *
FROM tbl_eta
WHERE meta_key = 'word3'
AND meta_value != ''
AND CAST(meta_value AS DECIMAL) BETWEEN CAST(-0.0001 AS DECIMAL) AND CAST(2 AS DECIMAL)))
) metatbl_1 ON metatbl_1.symbol_id=symbol_id
GROUP BY
companyid
HAVING
1
ORDER BY
symbol.code DESC
LIMIT
0, 1500
注意:我假設您的代碼完全正常工作並且您知道自己在做什么。 您在此處看到的解決方案只是對您編寫的解決方案的優化查詢。
EAV(實體屬性值)是設計模式的一種低效方法。 當詢問存儲在VARCHAR
(或TEXT
)中的數值范圍時,它變得非常低效,如CAST()
所示。
這些索引可能有助於提高性能:
tbl_company_data: INDEX(company_symbol_id, id, company_size)
tbl_eta: INDEX(symbol_id)
tbl_eta: INDEX(meta_key, meta_value, symbol_id)
由於INDEXes
不能包含TEXT
列,我問meta_key
是否可以只是VARCHAR(100)
(或類似的小東西)。 就目前而言,我對tbl_eta
的第二個建議是行不通的。
添加復合索引時,DROP 具有相同前導列的索引。 也就是說,當你同時擁有 INDEX(a) 和 INDEX(a,b) 時,將前者扔掉。
其他注意事項:
(不要在FLOAT
或DOUBLE
上使用(m,n)
。如果要四舍五入到一定數量的小數位,請切換到DECIMAL(m,n)
。
從 MyISAM 切換到 InnoDB。
似乎tbl_exchange_symbol
的“自然” PRIMARY KEY
是(code)
或(code, country)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.