在MySQL中使用HAVING和WHERE子句之间的“OR”?

时间:2009-08-22 18:01:44

标签: sql mysql search

我正在尝试使用简单的已提交字段在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%'

我知道我可以拆分原始字符串,但这有一些负面影响,因为你必须处理包含空格的名称,例如'De Gaule'和类似的东西。

5 个答案:

答案 0 :(得分:9)

将所有条件都放入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子句可以提前丢弃行,但是因为在之后之后你不能丢弃它们,所以你已经计算了计算列上的条件,并且必须等到{{1} },它没有任何东西可以使用HAVING

答案 1 :(得分:5)

做一个子查询:

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%'

答案 2 :(得分:1)

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

John
Smith
John Smith

您的初始样本查询是:

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

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

第三个输入只会选择名字中包含“John Smith”的人;它还将挑选那些姓氏包含“John Smith”的人。然而,很可能很少有人符合这些标准 - 那些名叫约翰史密斯的人将只有名字中的约翰和姓氏中的史密斯。这不太可能是你的想法。

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

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

这种方法运作得相当好。

但是,如果您担心“戴高乐”(或“戴高乐”)或“Michael van den Berg”这样的名字,那么如果有人进入“Charles Gaulle”或“Michael”,那么匹配就会失败伯格,更不用说迈克尔范登堡了。您可能还需要用'%'符号替换用户输入中的任何空格字符。即使这样,你也要面对这样的问题:单词必须完全按照用户给出的顺序出现 - 这可能无关紧要,但你应该有意识地决定它并不重要。例如,如果输入是'Adam John Smith',那么查询将不会捕获'John Adam Smith';如果输入是'Smith,John',那么它就不会接收任何人(最有可能)。

如果你想管理这个,你可能需要标记用户的输入,并搜索单独的单词。要小心有人问一个单词的子字符串(例如,有人问'de'作为名字) - 目前没有任何查询确保用户输入的单词与值中的整个单词匹配(John vs约翰逊),并且使用SQL标准LIKE运算符这样做已经足够不可能了。

答案 3 :(得分:0)

如果在子查询中定义该列,则可以在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进行全文索引。

答案 4 :(得分:0)

尽管使用子查询效果很好,但它会产生影响,因为您没有点击任何索引。

如何向表中添加计算列(firstname || ' ' || lastname)及其索引?肯定会快得多。

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

那样查询
WHERE firstname || ' ' || lastname LIKE '%user_submitted_data%'

仍应比两个OR和一个子查询更快地工作。