2016-03-02 125 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;