【问题标题】:Using 'OR' between HAVING and WHERE clause in MySQL?在 MySQL 中的 HAVING 和 WHERE 子句之间使用“或”?
【发布时间】:2010-11-21 22:37:48
【问题描述】:

我正在尝试使用一个简单的已提交字段来获取 MySQL 中的记录。更准确地说,用户输入一个名字(名字或姓氏或全名),服务器应该返回匹配的行。

到目前为止,我正在做的事情是这样的:

SELECT * FROM people 
WHERE 
   firstname LIKE '%user_submitted_data%' OR 
   lastname LIKE '%user_submitted_data%'

目前效果很好,但是(显然)在用户提交全名时不起作用。有没有办法在整个“WHERE 类型条件”和“HAVING 类型条件”之间添加 OR?这样我可以做类似的事情:

SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname 
FROM people 
WHERE 
   firstname LIKE '%user_submitted_data%' OR 
   lastname LIKE '%user_submitted_data%' OR 
   HAVING fullname LIKE '%user_submitted_data%'

我知道我可以只拆分原始字符串,但这会产生一些负面影响,因为您必须处理包含空格的名称,例如“戴高乐”之类的东西。

【问题讨论】:

    标签: sql mysql search


    【解决方案1】:

    只需将所有条件放入HAVING 子句即可。

    SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname 
    FROM people 
    HAVING firstname LIKE '%user_submitted_data%'
    OR      lastname LIKE '%user_submitted_data%'
    OR      fullname LIKE '%user_submitted_data%

    WHERE 子句可以提前丢弃行,但由于您在之后 评估计算列上的条件之前不能丢弃它们,并且必须等到HAVING,它购买你没什么好用的WHERE

    【讨论】:

    • 为我工作,似乎是一个比使用子查询更优雅的解决方案。谢谢!
    【解决方案2】:

    做一个子查询:

    SELECT [some fields]
    FROM
      SELECT firstname, lastname, CONCAT(firstname, ' ', lastname) as fullname
      FROM people) AS tmp
    WHERE firstname LIKE '%user_submitted_data%'
    OR lastname LIKE '%user_submitted_data%'
    OR fullname LIKE '%user_submitted_data%'
    

    【讨论】:

      【解决方案3】:

      让我们考虑一些可能的输入:

      John
      Smith
      John Smith
      

      您的初始示例查询是:

      SELECT * FROM people 
      WHERE 
         firstname LIKE '%user_submitted_data%' OR 
         lastname LIKE '%user_submitted_data%'
      

      现在,当用户输入第一个输入时,此查询将选择所有名字中包含“约翰”的人;它还将挑选所有姓氏包含“John”的人(例如,数据库中的所有 Johnsons)。同样,第二个输入将选择所有名字中包含“Smith”的人;它还将挑选所有姓氏中包含“Smith”的人(例如,Smithsons 和 Smithers)。到目前为止,一切都很好;由于区分大小写的问题,它并不完美(从这里开始我将忽略区分大小写,但您可能根本不应该忽略它),但没关系。

      第三个输入只会选择名字包含“John Smith”的人;它还会选择姓氏包含“John Smith”的人。但是,很可能很少有人符合这些标准 - 那些名为 John Smith 的人的名字中只有 John,姓中只有 Smith。这不太可能是您的想法。

      不清楚表中是否有名为“fullname”的列。如果这样做,那么您可以只匹配该列,而不是分别匹配名字和姓氏。如果你不这样做,也许你可以制造这样一个列,然后针对它运行查询。

      SELECT *
        FROM (SELECT firstname || ' ' || lastname AS fullname, ... FROM people) AS t 
       WHERE t.fullname LIKE '%user_submitted_data%'
      

      这很好用。

      但是,如果您担心诸如“Charles De Gaulle”(或“Charles de Gaulle”)或“Michael van den Berg”之类的名字,那么如果有人输入“Charles Gaulle”或“Michael”,匹配将失败伯格”,更不用说迈克尔·范登伯格了。您可能还需要用“%”符号替换用户输入中的任何空格字符。即使这样,您也面临着单词必须完全按照用户给定的顺序出现的问题——这可能无关紧要,但您应该有意识地认为这无关紧要。例如,如果输入是“亚当·约翰·史密斯”,那么查询将不会捕捉到“约翰·亚当·史密斯”;如果输入是“Smith, John”,那么它不会接听任何人(很可能)。

      如果您想对此进行管理,您可能需要对用户的输入进行标记,并搜索单独的单词。当心有人询问某个单词的子字符串(例如,有人询问 'de' 作为名称单词) - 目前没有任何查询可以确保用户输入的单词与值中的整个单词匹配(John vs Johnson),而使用 SQL 标准 LIKE 运算符这样做几乎是不可能的。

      【讨论】:

      • 感谢您提供深入的遮阳篷。出于某种原因,MySQL 在我的系统(OSX)上似乎不区分大小写,所以到目前为止一切正常。就单词顺序而言,LIKE 运算符非常适合上下文。当用户搜索 John Adam Smith 时,我不需要(也不希望)Johh Smith 出来。搜索样本非常有限。
      【解决方案4】:

      如果您在子查询中定义了 WHERE 子句中的计算列,则可以引用该列:

      SELECT p.*
      FROM (
        SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname 
        FROM people
      ) p
      WHERE 
         p.firstname LIKE '%user_submitted_data%' OR 
         p.lastname LIKE '%user_submitted_data%' OR 
         p.fullname LIKE '%user_submitted_data%';
      

      但老实说,对于您正在执行的搜索类型,带有通配符的LIKE 是一个糟糕的解决方案。您应该考虑使用FULLTEXT 索引:

      CREATE FULLTEXT INDEX people_names ON people(firstname, lastname);
      
      SELECT *
      FROM people
      WHERE MATCH(firstname, lastname) AGAINST( ? );
      

      PS:FULLTEXT 索引仅适用于 MyISAM 存储引擎。另一种更快的解决方案是使用Sphinx Search 进行全文索引。

      【讨论】:

        【解决方案5】:

        虽然使用子查询效果很好,但它会产生影响,因为您没有命中任何索引。

        向表中添加计算列 (firstname || ' ' || lastname) 并为其添加索引怎么样?肯定会快很多。

        如果你不能这样做,我认为查询就像

        WHERE firstname || ' ' || lastname LIKE '%user_submitted_data%'
        

        应该仍然比两个ORs 和一个子查询更快。

        【讨论】:

        • 我认为没有办法在 MySQL 中创建索引计算列,或者展示我的研究。如果这是可行的,我很想听听。
        • 你可能是对的,我认为根本没有办法使用计算列!?
        • 同时 MySQL 支持索引计算列。但如果模式以占位符 (%) 开头,则 LIKE 条件不能使用索引。
        猜你喜欢
        • 2017-10-07
        • 1970-01-01
        • 1970-01-01
        • 2010-12-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多