簡體   English   中英

內部連接 3 個表並添加索引后慢 Mysql 查詢

[英]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_idmeta_keymeta_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) 時,將前者扔掉。

其他注意事項:

(不要在FLOATDOUBLE上使用(m,n) 。如果要四舍五入到一定數量的小數位,請切換到DECIMAL(m,n)

  • 從 MyISAM 切換到 InnoDB。

  • 似乎tbl_exchange_symbol的“自然” PRIMARY KEY(code)(code, country)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM