MYSQL - 索引和优化选择查询

San*_*ndy 5 mysql indexing select

我有一张超过500万行的表.当我执行选择查询时,大约需要20秒.

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

这是表结构:

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
Run Code Online (Sandbox Code Playgroud)

这是来自该查询的EXPLAIN:

--------+------------------------------------------------------------------------------------------------------+
| id | select_type | table        | type  | possible_keys | key    | key_len  | ref  | rows    | Extra         |
+----+-------------+----------+-------+---------------+------+---------+------+---------+----------------------+
|  1 | SIMPLE      | CompanyTable | ALL   |     Alias     | NULL   | NULL     | NULL | 5255929 |  Using where  |
+----+-------------+----------+-------+---------------+------+---------+------+---------+----------------------+
Run Code Online (Sandbox Code Playgroud)

我使用的复合指数Alias (Alias1,Alias2,Alias3,Alias4).但我相信这不是最好的.请建议我为此选择查询查找正确的索引.

use*_*740 4

对于要使用复合索引中的列的查询引擎,必须首先满足左侧的列。也就是说,必须使用列作为限制,以减少从左到右读取的候选行。

OR alias3(或alias4)子句违反了此规则,因为它表示“我不关心左侧部分(alias1或alias2(或alias3))是什么,因为我不依赖它们”。

然后需要进行全表扫描,看看是否有满足条件的alias3(或alias4)值。

在这种情况下,潜在有用的指数是:

  • INDEX(alias1, alias2):alias1 AND alias2 覆盖此复合索引
  • 索引(别名3)
  • 索引(别名4)

实际统计数据和计划选择需要进一步调查 - 但至少现在查询规划器有一些可以使用的东西。


话虽如此 - 我不确定“别名”的作用是什么 - 规范化表格可能是有意义的。以下确实稍微改变了语义,因为它删除了“别名位置”(可以重新添加),并且应该验证语义的正确性。

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)
)
Run Code Online (Sandbox Code Playgroud)

然后可以与原始查询大致相似地查询它,不同之处在于它不关心哪个“别名”与哪个值匹配:

-- 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')
Run Code Online (Sandbox Code Playgroud)

我希望这样的查询能够在 SQL Server - YMMV 和 MySQL 中有效地实现。