我有一张超过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 |
+----+-------------+----------+-------+---------------+------+---------+------+---------+----------------------+
我使用了综合索引Alias
(Alias1
,Alias2
,Alias3
,Alias4
)。
但我相信这不是最好的。请建议我为此选择查询查找正确的索引。
答案 0 :(得分:3)
要使查询引擎使用复合索引中的列,必须首先满足左侧的列。也就是说,列必须用作限制,将候选行从左到右减少。
OR别名3(或alias4)条款违反了这条规则,因为它说"我不在乎左边的部分(alias1或alias2(或alias3))是什么,因为我不依赖它们& #34;
然后需要进行全表扫描,以查看是否有符合条件的任何 alias3(或alias4)值。
此条件下的潜在有用索引将是:
实际统计信息and plan selection需要进一步调查 - 但至少现在查询规划器可以使用。
话虽如此 - 而且我不确定"别名"的作用是什么?是 - 规范化表可能是有意义的。以下确实稍微改变了语义,因为它删除了"别名位置" (可以重新加入)并且应该验证语义正确性。
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。
答案 1 :(得分: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。填写新的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。您可以使用触发器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 ,然后只需加入此表。
答案 2 :(得分: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;