繁体   English   中英

MYSQL - 索引和优化选择查询

[英]MYSQL - Indexing and Optimize Select Query

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

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

这是来自该查询的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  |
+----+-------------+----------+-------+---------------+------+---------+------+---------+----------------------+

我使用了复合索引AliasAlias1Alias2Alias3Alias4 )。 但我相信这不是最好的。 请建议我为此选择查询查找正确的索引。

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

OR别名3(或别名4)条款违反了这条规则,因为它说“我不关心左边的部分(别名1或别名2(或alias3))是什么,因为我不依赖于它们”。

然后,全表扫描需要查看是否有符合条件的任何别名3(或alias4)值。

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

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

实际统计数据和计划选择需要进一步调查 - 但至少现在查询计划程序可以使用。


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

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。

我建议使用以下解决方案,创建一个包含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.填写新的Field ComplexAliasQuery

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

3.用于更新其中一个字段Alias1,Alias2,Alias3,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 ,然后只需加入此表即可。

以上都不是。 重新设计架构。

如果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;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM