使用多个 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吗?

    我可以对查询进行任何其他优化吗?喜欢合并LIKEs 或使用其他功能,例如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

Oracle Regexp_like 使用 AND 子句

如何在sql语句中使用正则表达式

在 regexp_like 中组合 like 和 not like 以优化 Amazon Athena 中的配置单元查询

mysql模糊匹配查询like,regexp,in