使用多个 LIKE 语句和 REGEXP 的查询可以更有效吗?
Posted
技术标签:
【中文标题】使用多个 LIKE 语句和 REGEXP 的查询可以更有效吗?【英文标题】:Can my query with multiple LIKE statements and REGEXP be more efficient? 【发布时间】:2011-07-14 05:25:57 【问题描述】:我正在构建一个动态查询以从我的数据库中选择已删除的域名。目前有十几行,但我很快就会得到数据,其中将有多达 500,000 行的记录。
架构只是一个包含 4 列的表:
CREATE TABLE `DroppedDomains` (
`domainID` int(11) NOT NULL AUTO_INCREMENT,
`DomainName` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`DropDate` date DEFAULT NULL,
`TLD` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`domainID`)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
我没有创建架构,这是实时数据库架构。这是示例数据:
我已经构建了可能是下面最复杂的查询类型。标准如下:
选择任意数量的域
以“开始”一词开头 以“结束”一词结尾 在域名的任何位置包含“containsThis”一词 在域名的任意位置包含“ContainsThisToo”一词 至少包含一位数字 域名必须至少有 49 个字符。多字节需要计数 作为一个字符(我使用了 CHAR_LENGTH )。 域名必须至少少于 65 个字符。 TLD 必须是“org” DropDate 需要晚于
2009-11-01
这是我目前的查询:
SELECT
*
FROM
DroppedDomains
WHERE
1=1
AND DomainName LIKE 'starts%ends'
AND DomainName LIKE '%containsThis%'
AND DomainName LIKE '%containsThisToo%'
AND DomainName LIKE '%-%'
AND DomainName REGEXP '[0-9]'
AND CHAR_LENGTH(DomainName) > 49
AND CHAR_LENGTH(DomainName) < 65
AND TLD = 'org'
AND DropDate > '2009-11-01'
这是我的问题
如果我将TLD
列设为自己的表并仅将TLD
列设为该列的外键,考虑到我将有50 万行,这是否会极大地提高性能?将只有 5 个 TLD(com、net、org、info、biz)。我知道现实世界中有更多的 TLD,但这个应用程序只有 5 个。用户不能指定自己的 TLD。
我知道REGEXP
和 500,000 行可能会导致灾难。无论如何我可以避免REGEXP
吗?
我可以对查询进行任何其他优化吗?喜欢合并LIKE
s 或使用其他功能,例如INSTR
?我应该实现任何特定类型的缓存机制吗?
【问题讨论】:
注意:我知道我不应该使用SELECT.*
,因为将来可能会添加更多列,并且通过选择不必要的列可能会影响性能。它仅用于演示/测试目的。
【参考方案1】:
如果您有一个以常量前缀开头的 LIKE 模式并且您在该字段上有一个索引,那么该索引可用于非常快速地找到以该前缀开头的行。幸运的是,您在这里遇到了这种情况:
AND DomainName LIKE 'starts%ends'
如果只有少数值以starts
开头,那么这些行将很快被找到,而其他表达式将只针对这些行进行测试。你可以通过运行EXPLAIN SELECT ...
来检查索引是否被使用。
【讨论】:
这是否意味着我的查询几乎已经尽可能优化了? @meder:如果您有正确的索引,那么可以。您可能还需要在此处考虑多列索引,因为您有多个要测试的字段。注意:多列索引中的列顺序很重要。您可能需要进行试验以查看哪个索引效果最好。这在很大程度上取决于数据的分布。例如,您可能会发现(TLD, DomainName)
上的索引对某些查询效果很好,但(TLD, DropDate)
对其他查询效果更好。尝试几个不同的索引,看看哪个最好。
索引是指内部存储的索引,还是我应该实际上以某种方式生成数据库的一些“缓存”状态?问的我都觉得很傻,我不是学过sql/dbs的关键部分吗?
转念一想,我认为它会回到我身边,但我一直没有使用索引。
@meder:是的,我想说理解索引是使用 RDBMS 的关键部分。您可以参考How mysql uses indexes,其中还提到了我在回答中写的 LIKE 优化。我还建议买一本好的 MySQL 书,因为 MySQL 文档有时阅读量很大,不如教程好。 PS:创建索引可以使用CREATE INDEX
语句。【参考方案2】:
您应该根据您计划使用的查询计划要创建的索引。
如果您有过滤的查询 只有 DropDate,然后是一个索引 DropDate 会很有用。 如果您有分组依据的查询 TLD,那么 TLD 上的索引将是 有用。 如果您有搜索查询 仅按域名的长度,则 您可以考虑添加一个字段 DomainNameLength 正是这样(以及对此的索引)所以 长度不是每次都计算的 运行查询的时间。 如果您的查询按两个字段(例如 TLD 和 DropDate)进行搜索(过滤),那么您可能需要在这些字段上使用 2 列索引。 等等……如果您将使用的唯一查询是您提到的复杂查询,那么 Mark 的建议(关于 DomainName 上的索引)是最好的。
关于TLD
字段的问题1:
如果您真的只有少量(例如 5 个)选项,并且您不打算使用所有可用的 tld,则可以使用 ENUM
type。
CREATE TABLE(
....
tld ENUM('com', 'net', 'org', 'info', 'biz')
)
【讨论】:
哦,我肯定会有不同的查询,所以看起来我需要在一堆列上抛出索引:i.imgur.com/Z9aSb.jpg。我现在有了数据,所以我将尝试比较使用和不使用索引的查询速度 - 感谢您的提示。以上是关于使用多个 LIKE 语句和 REGEXP 的查询可以更有效吗?的主要内容,如果未能解决你的问题,请参考以下文章
在Oracle中使用REGEXP_LIKE和使用LIKE的性能哪个好
当 regexp_like 和 regexp_extract 工作正常时,Impala regexp_like 查询返回 null
在 regexp_like 中组合 like 和 not like 以优化 Amazon Athena 中的配置单元查询