2016-03-02 108 views
5

我有一個超過500萬行的表。當我執行選擇查詢時,大約需要20秒。MYSQL - 索引和優化選擇查詢

SELECT CompUID,Weburl FROM `CompanyTable` WHERE (Alias1='match1' AND Alias2='match2')OR Alias3='match3' OR Alias4='match4' 

下面是表結構:

CREATE TABLE `CompanyMaster` (
    `CompUID` int(11) NOT NULL AUTO_INCREMENT, 
    `Weburl` varchar(150) DEFAULT NULL, 
    `CompanyName` varchar(200) DEFAULT NULL, 
    `Alias1` varchar(150) DEFAULT NULL, 
    `Alias2` varchar(150) DEFAULT NULL, 
    `Alias3` varchar(150) DEFAULT NULL, 
    `Alias4` varchar(150) DEFAULT NULL, 
    `Created` datetime DEFAULT NULL, 
    `LastModified` datetime DEFAULT NULL, 
    PRIMARY KEY (`CompUID`), 
    KEY `Alias` (`Alias1`,`Alias2`,`Alias3`,`Alias4`) 
) ENGINE=InnoDB AUTO_INCREMENT=5457968 DEFAULT CHARSET=latin1 

下面是從查詢的解釋:

--------+------------------------------------------------------------------------------------------------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows | Extra   | 
+----+-------------+----------+-------+---------------+------+---------+------+---------+----------------------+ 
| 1 | SIMPLE  | CompanyTable | ALL |  Alias  | NULL | NULL  | NULL | 5255929 | Using where | 
+----+-------------+----------+-------+---------------+------+---------+------+---------+----------------------+ 

我使用的複合索引AliasAlias1Alias2Alias3Alias4) 。 但我相信這不是最好的。請爲此選擇查詢查找建議正確的索引。

+1

的'或別名3 =「MATCH3」 OR Alias4 ='match4''是什麼迫使全表掃描。這個條款基本上是沒有索引的。爲了優化這個查詢,你需要在Alias3和Alias4上添加一個索引。 – drew010

+0

您可以通過提供如下提示來檢查查詢的性能:'SELECT CompUID,Weburl FROM'CompanyTable' use index(Alias)...'。這在速度上有所不同嗎? – zedfoxus

+0

以下查詢的共享計數:「從」CompanyTable「中選擇count(*),其中Alias1 ='match1'AND Alias2 ='match2';」和「從CompanyTable'中選擇count(*),其中Alias1 ='match3'」和「從CompanyTable'中選擇count(*),其中Alias1 ='match4」。 –

回答

3

對於查詢引擎要使用組合索引中的列,首先必須滿足左側的列必須滿足。也就是說,列必須使用作爲從左到右讀取候選行的限制。 (或alias4)子句違反了這條規則,因爲它說「我不在乎左邊的部分(alias1或alias2(或alias3))是什麼,因爲我不依賴它們」。

然後需要進行全表掃描,以查看是否存在符合條件的alias3(或alias4)值

潛在在這種情況下有用指數將是:

  • INDEX(alias1,別名2):alias1 AND別名2覆蓋該複合指數
  • INDEX(別名3)
  • INDEX(alias4)

實際數據and plan selection需要進一步調查 - 但至少現在查詢計劃員有一些工作機智H。


話雖這麼說 - 我不知道一個「別名」的作用 - 它可能是有意義的正常化表。下面的確會稍微改變語義,因爲它會丟棄「別名位置」(可以重新添加)並且應該驗證語義的正確性。

CREATE TABLE `CompanyMaster` (
    `CompUID` int(11) NOT NULL AUTO_INCREMENT 
,`CompanyName` varchar(200) DEFAULT NULL 
,PRIMARY KEY (`CompUID`) 
) 

-- (This establishes a unique alias-per-company, which may be incorrect.) 
CREATE TABLE `CompaniesAliases` (
    `CompUID` int(11) NOT NULL 
,`Alias` varchar(150) NOT NULL 
    -- Both CompUID and Alias appear in 'first' positions: 
    -- CompUID for Join, Alias for filter 
,PRIMARY KEY (`CompUID`, `Alias`) 
,KEY (`Alias`) 
-- Alternative, which may change plan selection by eliminating options: 
-- ,PRIMARY KEY (`Alias`, `CompUID`) -- and no single KEY/index on Alias or CompUID 
,FOREIGN KEY(CompUID) REFERENCES CompanyMaster(CompUID) 
) 

其價值然後它可以查詢大致相似原著,是不同,它並不關心「別名」比賽:

-- AND constructed by joins (could also use GROUP BY .. HAVING COUNT) 
SELECT c.CompUID FROM `CompanyTable` c 
JOIN `CompaniesAliases` ac1 
ON ac1.CompUID = c.CompUID AND Alias = 'match1' 
JOIN `CompaniesAliases` ac2 
ON ac2.CompUID = c.CompUID AND Alias = 'match2' 

-- OR constructed by union(s) 
UNION 
SELECT c.CompUID FROM `CompanyTable` c 
JOIN `CompaniesAliases` ac1 
ON ac1.CompUID = c.CompUID AND (Alias = 'match3' OR Alias = 'match4') 

我希望這樣的查詢在SQL Server中有效實現 - 帶MySQL的YMMV。

0

我會建議下面的解決方案,用complex_alias_field創建一個表。它增加了一點你的數據,你的數據現在是多餘的,但我認爲這是一個簡單直接的解決方案。

1。創建表

CREATE TABLE `CompanyMaster` (
`CompUID` int(11) NOT NULL AUTO_INCREMENT, 
    `Weburl` varchar(150) DEFAULT NULL, 
    `CompanyName` varchar(200) DEFAULT NULL, 
    `Alias1` varchar(150) DEFAULT NULL, 
    `Alias2` varchar(150) DEFAULT NULL, 
    `Alias3` varchar(150) DEFAULT NULL, 
    `Alias4` varchar(150) DEFAULT NULL, 
    `Created` datetime DEFAULT NULL, 
    `LastModified` datetime DEFAULT NULL, 
    `ComplexAliasQuery` BOOLEAN DEFAULT FALSE, 
    PRIMARY KEY (`CompUID`), 
    KEY `Alias` (`Alias1`,`Alias2`,`Alias3`,`Alias4`), 
    KEY `AliasQuery` (`ComplexAliasQuery`) 
) ENGINE=InnoDB AUTO_INCREMENT=5457968 DEFAULT CHARSET=latin1; 

2.填寫您的新領域ComplexAliasQuery

UPDATE CompanyMaster set ComplexAliasQuery = TRUE WHERE (Alias1='match1' AND Alias2='match2')OR Alias3='match3' OR Alias4='match4'; 

3.更新字段Alias1,別名2,別名3之一,Alias4

對於剛剛更新也填充ComplexAliasQuery。如果你不能使用觸發器,你可以用Trigger http://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html或在你的代碼中這樣做,因爲你正在運行一個集羣。

4.您簡單查詢是在結束

SELECT CompUID,Weburl FROM `CompanyMaster` WHERE ComplexAliasQuery IS TRUE; 

與打黑指數

+----+-------------+---------------+------+---------------+------+---------+------+------+-------------+ 
| id | select_type | table   | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+---------------+------+---------------+------+---------+------+------+-------------+ 
| 1 | SIMPLE  | CompanyMaster | ALL | NULL   | NULL | NULL | NULL | 1 | Using where | 
+----+-------------+---------------+------+---------------+------+---------+------+------+-------------+ 

另一種解決方案

如果你不喜歡在你的表中的字段CompanyMaster,你可以外包在一個新表中,並將其稱爲IndexAliasCompanyMaster然後只需加入此表。

0

以上都不是。重新設計架構。

如果4個別名只是一個公司的同義詞,那麼而不是在表格中顯示它們的數組,將它們移到另一個表中。 (user2864740了一半那裏,我說要一路走下去。)

CREATE TABLE `CompanyMaster` (
    `CompUID` int(11) NOT NULL AUTO_INCREMENT, 
    `Weburl` varchar(150) DEFAULT NULL, 
    `CompanyName` varchar(200) DEFAULT NULL, 
    `Created` datetime DEFAULT NULL, 
    `LastModified` datetime DEFAULT NULL, 
    PRIMARY KEY (`CompUID`), 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

CREATE TABLE `CompaniesAliases` (
    `CompUID` int(11) NOT NULL, 
    `Alias` varchar(150) NOT NULL, 
    PRIMARY KEY (Alias) -- Assuming no two companies can have the same Alias 
    KEY (CompUID) 
) ENGINE=InnoDB; 

(你真的應該所有錶轉換爲InnoDB的。)

現在,你原來的查詢變得

SELECT CompUID, Weburl 
    FROM `CompanyTable` 
    JOIN CompaniesAliases USING(CompUID) 
    WHERE Alias IN ('match1', 'match2', 'match3', 'match4'); 

它會運行得更快。

如果需要顯示公司名稱和它的別名,考慮

SELECT CompanyName, 
     GROUP_CONCAT(Alias) AS 'Also known as' 
    FROM `CompanyTable` 
    JOIN CompaniesAliases USING(CompUID) 
    WHERE ... 
    GROUP BY CompUID;