Php MySQL中的复杂搜索
我需要在MySQL数据库中进行复杂的搜索。我想用一个查询来解决这个问题,以避免PHP代码中的大部分更改 这就是它的工作原理:Php MySQL中的复杂搜索,php,mysql,sql,Php,Mysql,Sql,我需要在MySQL数据库中进行复杂的搜索。我想用一个查询来解决这个问题,以避免PHP代码中的大部分更改 这就是它的工作原理: 用户在搜索字段中输入文本 系统搜索关键字 系统在名称中搜索 系统在文本中搜索 系统按类别进行搜索 如果系统在关键字:1点、名称:1点、文本:1点/提及、类别:1点中找到匹配项 得分最多的用户排名第一,得分第二多的用户排名第二,依此类推 使用IF,ELSE,CASE,WHEN这个查询可行吗?我从来没有做过这种复杂的搜索。问题更大,我看了两张表: TABLE USERS: i
TABLE USERS:
id | name | lastname | category | keywords | text |
TABLE PAGES:
id | user_id |
我需要列出表“用户”,但如果用户有自己的页面,则首先显示有“页面”的用户,然后再列出没有“页面”的其他用户。您可以通过简单的
按顺序执行此操作。下面是一个使用类似于
的搜索词的示例:
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;
orderby
中的条件使用MySQL将布尔值视为数字的事实,其中1
表示true,0
表示false。您可以将它们相加以获得匹配的总数
本例使用类似于
的。您可以使用match。如果您更喜欢全文搜索,请使用
编辑:
如果您担心列可能包含NULL
,请使用coalesce()
:
是的,有可能
我认为提供的规范意味着,如果在关键字
列中未找到匹配项,则分数为0。如果在关键字
中找到匹配项,但在名称
列中未找到匹配项,则分数为1。如果在关键字
和名称
列中找到匹配项,但在文本
列中未找到匹配项,则得分为2。等等(我可能误解了规范,将更多的含义归因于列出匹配项的顺序,以及“如果它也在”…如果在名称中找到匹配项,而不是在关键字中找到匹配项,那么我们不会说“它也匹配名称”。)
另一种方法是使用MySQLIF()
函数
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
此查询(与前面的查询一样)将首先列出点的计算值最高的用户,然后根据页面
表中是否有匹配行列出。(如果q.user\u id
页面中的行与id
中的users
匹配,则表达式q.user\u id
将为空,如果存在匹配,则表达式将为非空。因此,按表达式“q.user\u id为空
”排序在没有页面的用户之前,您是否会对使用页面的用户进行排序?
您研究过MySQL中的全文搜索功能集吗?它可能会产生您需要的结果。这几乎肯定是可能的,因为子查询本质上意味着您可以生成虚拟临时表,因此您可以慢慢构建结果呃,这可能不是特别有效。为什么需要避免使用PHP来处理结果?可能是联合查询?选择1作为点,“名称”作为源…联合所有选择2作为点,“关键字”作为源…等等。
?我在数据库中调查全文。我不想有太多sql查询,在PHP中使用“if”/“else”切换和计数。当前搜索页面很大,我需要更改所有内容。唯一的解决方案是只替换PHP中用于生成单词和字符串清理的查询和输入函数。我可能误解了规范。我将意义归因于搜索匹配列的特定顺序。我还将我归因于与规范“if is all in”一致。我认为这意味着,如果上一次检查成功,我们将加1分,否则,如果行之前没有匹配,我们将不会加1分。(如果我们希望为每个匹配的分数加1分,则派生分的表达式可以很容易地更改)不,这是额外的,但不是我需要的解决方案。请重新阅读规范。评分是一个很好的补充,但按照相同的顺序,我必须搜索数据库和用户谁有最多的分数,页面放在第一位,其他人放在下面。谢谢!没有特别的“顺序”数据库需要搜索的行。行满足谓词并返回,或不满足谓词并不返回。orderby
子句指定返回行的顺序。注意:如果我们保证“搜索”列(使用LIKE运算符),这是很好的不为NULL。在更一般的情况下,我们希望预测NULL
值将如何影响“points”表达式。为了适当地处理NULL值,我们可以按照IFNULL(category,”)这样做,如“%t%”
或IF(category,如“%t%”,1,0)
@spencer7593…这是一个很好的观点。我编辑了答案。
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