我有一个有趣的问题,试图从一个表中选择行,其中where子句中的VARCHAR列有多种可能性。
这是我的表(大约有700万行):
CREATE TABLE `search_upload_detailed_results` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`surId` bigint(20) DEFAULT NULL,
`company` varchar(100) DEFAULT NULL,
`country` varchar(45) DEFAULT NULL,
`clei` varchar(100) DEFAULT NULL,
`partNumber` varchar(100) DEFAULT NULL,
`mfg` varchar(100) DEFAULT NULL,
`cond` varchar(45) DEFAULT NULL,
`price` float DEFAULT NULL,
`qty` int(11) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`description` varchar(500) DEFAULT NULL,
`status` varchar(45) DEFAULT NULL,
`fileId` bigint(20) DEFAULT NULL,
`nmId` bigint(20) DEFAULT NULL,
`quoteRequested` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `sudr.surId` (`surId`),
KEY `surd.clei` (`clei`),
KEY `surd.pn` (`partNumber`),
KEY `surd.fileId` (`fileId`),
KEY `surd.price` (`price`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
我正在尝试匹配partNumber列。问题是partNumber有不同的格式,可以在搜索表单中以多种格式输入。
示例:零件编号'300-1231-932'可以是:
像这样的简单选择需要0.0008秒。
select avg(price) as price from search_upload_detailed_results where
partNumber LIKE '3001231932%' and price > 0;
但它并没有给我所有我需要的比赛。所以我写了这个查询。
select avg(price) as price from search_upload_detailed_results
where REPLACE(REPLACE(partNumber,'-',''),' ','') LIKE REPLACE(REPLACE('3001231932%','-',''),' ','') and price > 0;
这给了我所有正确的匹配,但它在3.3秒时超级慢。
我玩了一些东西,试图减少我正在进行替换的行数,并想出了这个。
select avg(price) as price from search_upload_detailed_results
where price > 0 AND
partNumber LIKE('300%') AND
REPLACE(REPLACE(partNumber,'-',''),' ','') LIKE REPLACE(REPLACE('3001231932%','-',''),' ','');
执行需要0.4秒。相当快,但在多部分搜索中仍然有点耗时。
我想让它快一点,但这是我能得到的。有没有其他方法来优化此查询?
更新以显示第三个查询的解释:
# id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
1, SIMPLE, search_upload_detailed_results, range, surd.pn,surd.price, surd.pn, 103, , 89670, Using where
答案 0 :(得分:1)
显而易见的解决方案是只存储零件号,表中没有多余的字符。然后从用户输入中删除这些字符,只需执行简单的WHERE partnumber = @input
查询。
如果无法做到这一点,您可以将其添加为附加列。在MySQL 5.7中,您可以使用generated column;在早期版本中,您可以使用填充此列的触发器。
答案 1 :(得分:0)
我想让它快一点,但这是我能得到的。有没有其他方法来优化此查询?
正如Barmar所说,如果你真的需要速度(3.3s慢?)的最佳解决方案是在其中包含未转换数据的列(希望现在标准化),这样就可以让你查询它了没有指定所有不同类型的零件号。
示例:零件编号' 300-1231-932'可能是:
300-1231-932 || 3001231932 || 300 1231 932
我认为你应该担心数据的呈现,拥有所有不同的格式'会让它变得困难 - 你可以格式化为一个标准(在它到达数据库之前)吗?
这是我的桌子(大约有700万行):
别忘了你的索引!
答案 2 :(得分:0)
如其他地方所述,问题是表格格式。如果这是不可协商的,那么另一种选择是:
如果有一些格式,但不是太多,并且它们是众所周知的(例如,您已经显示的三种格式),那么可以通过明确地预先计算所有格式并搜索它们中的任何一种来使查询运行得更快
select avg(price) as price from search_upload_detailed_results where
partNumber IN ('300-1231-932', '3001231932', '300 1231 932')
这将充分利用您可能在partNumber上拥有的索引。
答案 3 :(得分:0)
您可能会发现MySQL可以充分利用精心选择的正则表达式的索引。
从search_upload_detailed_results中选择avg(price)作为价格 partNumber REGEXP' ^ 300 [ - ]?1231 [ - ]?932';