MySQL中的复杂搜索

时间:2014-07-14 15:51:28

标签: php mysql sql

我需要在MySQL数据库中进行复杂的搜索。我想用一个查询来解决它,以避免PHP代码中的大多数更改。

这是它应该如何运作的:

  1. 用户在搜索字段中输入文字
  2. 系统搜索关键字
  3. 系统搜索名称
  4. 系统搜索文字
  5. 系统搜索类别。
  6. 如果系统在关键字中找到匹配项:1点,如果它也在名称中:1点,如果它也在文本中:1点/提,如果它也在类别中:1点。

    点数最多的用户排在第一位,排名第二的用户位居第二,依此类推

    这个查询是否可以使用IF,ELSE,CASE,WHEN?我从来没有做过这种复杂的搜索。问题要大一些,我正在看两张桌子:

    TABLE USERS:
    id    |    name    |    lastname    |    category    |    keywords    |    text    |
    TABLE PAGES:
    id    |    user_id    |
    

    我需要列出表格"用户"但是如果用户有自己的页面来首先显示具有"页面的用户"然后列出其他没有"页面"。

    的人

2 个答案:

答案 0 :(得分:2)

您可以使用简单的order by执行此操作。以下是使用like的示例,仅用于搜索字词:

select u.*
from users u
order by ((keywords like '%@SEARCHTERM%') +
          (name like '%@SEARCHTERM%') +
          (text like '%@SEARCHTERM%') +
          (category like '%@SEARCHTERM%')
         ) desc;

要获取有关页面的信息,请将其加入:

select u.*
from users u left outer join
     (select user_id, count(*) as numpages
      from pages p
      group by user_id
     ) p
     on u.id = p.user_id
order by (p.numages > 0) desc,
         ((keywords like '%@SEARCHTERM%') +
          (name like '%@SEARCHTERM%') +
          (text like '%@SEARCHTERM%') +
          (category like '%@SEARCHTERM%')
         ) desc;

order by中的条件使用MySQL将布尔值视为数字的事实,1表示true,0表示false。您可以将它们相加以获得匹配的总数。

此示例使用like。如果您更喜欢全文搜索,则可以使用match . . . against执行类似操作。

编辑:

如果您担心这些列可能包含NULL,请使用coalesce()

order by (p.numages > 0) desc,
         ((coalesce(keywords, '') like '%@SEARCHTERM%') +
          (coalesce(name, '') like '%@SEARCHTERM%') +
          (coalesce(text, '') like '%@SEARCHTERM%') +
          (coalesce(category, '') like '%@SEARCHTERM%')
         ) desc;

答案 1 :(得分:1)

是的,这是可能的。

我提供的规范表示如果在keyword列中找不到匹配项,则点数为0.如果在keyword但未在name列中找到匹配项,则如果在keywordname列中找到匹配,但在text列中找不到,则得分为2.等等(我可能误解了规范,将更多含义归因于列出匹配的顺序,以及“如果”......如果在name中找到匹配但在keywords中找不到,那么我们就不会不要说“它匹配名称”。)

另一种方法是使用MySQL IF()函数。

SELECT t.id
     , t.name
     , t.lastname
     , t.category
     , t.keywords
     , t.text
     , IF(t.keywords LIKE '%text%'
         ,IF(t.name LIKE '%text%'
            ,IF(t.text LIKE '%text%'
               ,IF(t.category LIKE '%text%'
                  ,4
                  ,3)
               ,2)
            ,1)
         ,0) AS `points`
  FROM `users` t
  LEFT
  JOIN ( SELECT p.user_id FROM pages p GROUP BY p.user_id ) q
    ON q.user_id = t.id
HAVING `points` > 0
 ORDER
    BY `points` DESC, q.user_id IS NULL DESC

使用ANSI标准CASE表达式

可以实现等效
SELECT t.id
     , t.name
     , t.lastname
     , t.category
     , t.keywords
     , t.text
     , CASE WHEN t.keywords LIKE '%text%' THEN
         CASE WHEN t.name     LIKE '%text%' THEN
           CASE WHEN t.text     LIKE '%text%' THEN
             CASE WHEN t.category LIKE '%text%' THEN
               4
             ELSE 3 END
           ELSE 2 END
         ELSE 1 END
       ELSE 0 END AS `points`
  FROM `users`
  LEFT
  JOIN ( SELECT p.user_id FROM pages p GROUP BY p.user_id ) q
    ON q.user_id = t.id
HAVING `points` > 0
 ORDER
    BY `points` DESC, q.user_id IS NULL DESC

<强>后续

如果规范意味着如果用户提供的文本出现在四列中的任何一列中,并且每行的“点”总数是具有匹配的行中的列数,那么我们可以使用简单添加条件测试,而不是嵌套它们。

SELECT t.id
     , t.name
     , t.lastname
     , t.category
     , t.keywords
     , t.text
     , IF(t.keywords LIKE '%text%',1,0) +
       IF(t.name     LIKE '%text%',1,0) +
       IF(t.text     LIKE '%text%',1,0) +
       IF(t.category LIKE '%text%',1,0) AS `points`
  FROM `users` t
  LEFT
  JOIN ( SELECT p.user_id FROM pages p GROUP BY p.user_id ) q
    ON q.user_id = t.id
HAVING `points` > 0 WHERE 
 ORDER BY `points` DESC, q.user_id IS NULL DESC

此查询(与先前的查询一样)将首先列出points计算值最高的用户,然后列出pages表中是否找到匹配的行。 (如果q.user_id中没有与pages id匹配的行,则表达式users将为NULL,如果匹配则为非NULL。因此,使用“q.user_id IS NULL”表达式排序会在没有pages的用户之前为pages用户排序。