【问题标题】:MySQL Search Query on two different fields两个不同字段上的 MySQL 搜索查询
【发布时间】:2010-03-18 11:22:08
【问题描述】:

我需要使用 LIKE 函数搜索两个字段,并且也应该以相反的顺序匹配。我的表使用没有全文搜索的 InnoDB。

考虑以下情况:

我有包含 first_name 和 last_name 列的 users 表。上面有一行,值如下:

{
    first_name: 'Ludwig',
    last_name: 'van Beethoven',
}

案例:

  1. 可以搜索“路德维希·范·贝多芬”
  2. 可以搜索“贝多芬路德维希”
  3. 可以搜索“路德维希”
  4. 可以搜索“贝多芬”

我尝试了这条 SQL 语句,但没有成功。

SELECT CONCAT(first_name, ' ', last_name) as fullname 
  FROM users 
 WHERE fullname LIKE '%Ludwig van Beethoven%';

【问题讨论】:

  • 目前,我正在使用这条 SQL 语句。 SELECT CONCAT(first_name, ' ' , last_name) as fullname FROM users WHERE CONCAT(first_name, ' ', last_name) LIKE '%string%' AND CONCAT(last_name, ' ', first_name) LIKE '%string%';它适用于所有情况。

标签: mysql


【解决方案1】:

您需要在 where 子句中重新声明 concat 表达式。

 SELECT CONCAT(first_name, ' ', last_name) as fullname 
      FROM users 
     WHERE CONCAT(first_name, ' ', last_name) LIKE '%doe%';

不幸的是,“as”只是创建了一个列别名,而不是可以在其他地方使用的变量。

【讨论】:

  • +1 for (answer + reason) 我从我的(现已删除的)答案中取消了查询。
  • -1 函数不应在 WHERE 子句中使用。可以使用HAVING 引用列别名,请参阅我的回答。
  • @Andy:绝对同意你应该避免在WHERE 子句中你有其他选项的函数,但它们并不是自动不好,而不是如果可以在索引上计算函数(请参阅我的答案以了解我的意思)。我不认为 HAVING 在这里有帮助(我比较了执行计划;请参阅我的回答中的注释),但我会对你所拥有的任何参考资料非常感兴趣,以了解它为什么会这样做。
【解决方案2】:

主要的事情

确保您在 first_namelast_name 上有一个复合索引。否则,无论您如何处理,最终都会进行全表扫描真的很容易。因此,如果您还没有,请创建一个:

CREATE INDEX users_firstandlast ON users(first_name, last_name);

语法选项

一旦该索引到位,您就有一些选择:

选项1:正如Willis Blackburn 所说,在WHERE 子句中重复CONCAT(因为AS 不会创建可以在WHERE 子句中使用的名称):

SELECT CONCAT(first_name, ' ', last_name) as fullname 
FROM users 
WHERE CONCAT(first_name, ' ', last_name) LIKE '%doe%';

使用EXPLAIN 来检查您的具体情况,但在我的测试中它说它使用复合索引,即使您在WHERE 子句中使用了一个函数。

选项 2:在这种特殊情况下,您始终可以在 WHERE 子句中使用两个 LIKE

SELECT CONCAT(first_name, ' ', last_name) as fullname 
FROM users 
WHERE first_name LIKE '%doe%' or last_name LIKE '%doe%';

再次,这可以利用复合索引(虽然它不会利用 first_namelast_name 列上的单个索引 - 如果您没有使用通配符,但根据EXPLAIN [而且您的里程可能会有所不同,请务必检查],在这种情况下,它与表扫描一起使用。

选项 3his answer 中,Andy 说您可以为此使用 HAVING。我的read of the MySQL manual 建议它将首先构建结果集,然后才在最后应用HAVING,然后再将其发送给客户端,所以我对此持怀疑态度。 但是,在我快速而肮脏的测试中,EXPLAIN 告诉我,如果你有我上面提到的复合索引,HAVING 版本会进行索引搜索,而不是表扫描。如果您使用真实数据进行的测试证明了这一点,那么这对您来说可能是一个不错的选择。以这种方式使用HAVING 是一个MySQL 扩展(非标准),但话说回来,CONCAT 也是如此,所以我们已经进入了MySQL 特定的东西。 :-) 但是,再次检查您的真实生活环境。

结论

如果您还没有索引,请创建索引,如果可能的话,我会选择选项 2;否则,选项 1 除非您可以找到(或 Andy 可以提供)HAVING 的参考,而不是构建大量临时结果集(如果不是标准的话,那将非常酷)。无论如何,请与 EXPLAIN 联系,并在您的特定环境中进行测试。

【讨论】:

  • 是否有可能或需要创建相关性索引以将最接近的结果推送到顶部,或者 mysql 隐式执行?
【解决方案3】:

SELECT * FROM users WHERE CONCAT(first_name, ' ', last_name) LIKE '%Ludwig%' OR CONCAT(last_name, ' ', first_name) LIKE '%Ludwig%';

返回的所有搜索案例包括“Beethoven Ludwig”。

【讨论】:

    【解决方案4】:

    当您 CONCAT() 两列时,LIKE 变得区分大小写。因此,这应该会为您找到结果,但对于性能来说并不是最佳的:

    SELECT CONCAT(first_name, ' ', last_name) AS fullname FROM users 
    WHERE LOWER(CONCAT(first_name, ' ', last_name)) LIKE LOWER('%doe%');
    

    不过,这会让 MySQL 对每一行都进行处理。

    【讨论】:

    • “当你 CONCAT() 两列时,LIKE 变得区分大小写” 真的吗?你有这方面的参考吗?我不明白为什么您创建值的方式会对它是否区分大小写产生任何影响。
    • 更新:在文档中没有看到任何内容,只是尝试了一下。 不, concat 不会使 like 运算符突然区分大小写,至少在我使用 MyISAM 或 InnoDB 表的 MySQL 5.1 安装(Windows 和 Ubuntu)中不会(它真的不应该是存储特定):pastie.org/875248
    • 这是我上次这样做的一个问题,也许现在已经解决了。我当时在博客上写过。 serberus.net/2007/06/24/case-sensitivity-in-mysql
    • 在你的博文中,你没有说它是什么版本。在 2007 年,我正在考虑 5.0,但是......我在 MySQL 错误数据库中闲逛了一段时间,但结果太多(其中大部分是人们误解 like 应该如何工作,但至少有一个是真正的错误——但在您发布前几年在 v4.1.11 中修复)。显然,排序规则设置也可能是一个问题,但我看到的(只是总数的一小部分)都不是关于使用导致这种情况的函数,这真的很奇怪。
    猜你喜欢
    • 2020-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-17
    • 1970-01-01
    相关资源
    最近更新 更多