Php 在MySQL中按特定关键字对结果分组?

Php 在MySQL中按特定关键字对结果分组?,php,mysql,group-by,tagging,sql-like,Php,Mysql,Group By,Tagging,Sql Like,我有一个页面标记了多个标签,标签上有我正在搜索的关键字,有时它没有标记该关键字,所以当它有标签时,它将返回如下结果 质疑, SELECT* FROM root_pages AS p LEFT JOIN root_mm_pages_tags AS mm ON mm.page_id = p.page_id LEFT JOIN root_tags AS t ON t.tag_id = mm.tag_id AND t.tag_name LIKE '%story%' WHERE p.page_ti

我有一个页面标记了多个标签,标签上有我正在搜索的关键字,有时它没有标记该关键字,所以当它有标签时,它将返回如下结果

质疑,

SELECT*
FROM root_pages AS p

LEFT JOIN root_mm_pages_tags AS mm
ON mm.page_id = p.page_id

LEFT JOIN root_tags AS t
ON t.tag_id =  mm.tag_id
AND t.tag_name LIKE '%story%'

WHERE p.page_title LIKE '%article title 8%'
AND p.page_hide != '1'

ORDER BY (t.tag_name+0) ASC
结果,

page_id     page_url            tag_name    
17          article title 8     NULL
17          article title 8     NULL
17          article title 8     sys-rsv-story-1
所以我必须使用
分组方式
来解决这个问题

SELECT*
FROM root_pages AS p

LEFT JOIN root_mm_pages_tags AS mm
ON mm.page_id = p.page_id

LEFT JOIN root_tags AS t
ON t.tag_id =  mm.tag_id
AND t.tag_name LIKE '%story%'

WHERE p.page_title LIKE '%article title 8%'
AND p.page_hide != '1'

GROUP BY p.page_id
ORDER BY (t.tag_name+0) ASC
它会返回这样的结果

page_id     page_url            tag_name    
17          article title 8     NULL
但是我在寻找这个结果,它有我正在搜索的关键字

page_id     page_url            tag_name    
17          article title 8     sys-rsv-story-1
那么,是否可以按关键字对结果进行分组?还是其他更好的查询来存档

而且,如果关键字不在那里,它不应该返回结果,但它仍然返回结果

page_id     page_url            tag_name    
    17          article title 8     NULL
    17          article title 8     NULL
编辑:

我的新方案

 SELECT*
FROM root_pages AS p

INNER JOIN root_mm_pages_tags AS mm
ON mm.page_id = p.page_id

INNER JOIN root_tags AS t
ON t.tag_id =  mm.tag_id

WHERE p.page_title LIKE '%{group1}%'
AND t.tag_name LIKE '%story%'
AND p.page_hide != '1'

AND EXISTS (
    SELECT page_url
    FROM root_pages AS p

    LEFT JOIN root_mm_pages_tags AS mm
    ON mm.page_id = p.page_id

    LEFT JOIN root_tags AS t
    ON t.tag_id =  mm.tag_id

    WHERE page_url = 'article title 1d'
    AND t.tag_name LIKE '%story%'
    AND p.page_hide != '1'
)

ORDER BY (t.tag_name+0) ASC

尝试不在左联接中使用条件:

SELECT *
FROM root_pages AS p

LEFT JOIN root_mm_pages_tags AS mm
ON mm.page_id = p.page_id

LEFT JOIN root_tags AS t
ON t.tag_id =  mm.tag_id

WHERE p.page_title LIKE '%article title 8%'
AND p.page_hide != '1'
AND t.tag_name LIKE '%story%'

GROUP BY p.page_id
ORDER BY (t.tag_name+0) ASC
编辑:如果要获取页面标题包含“文章标题”的行,以及没有该标题但包含所需关键字的行,请使用此查询(建议使用@user985935):


下面是我在评论中提到的示例查询:

SELECT *
FROM root_pages AS p

LEFT JOIN root_mm_pages_tags AS mm
ON mm.page_id = p.page_id

LEFT JOIN root_tags AS t
ON t.tag_id =  mm.tag_id

WHERE p.page_hide != '1'
AND (t.tag_name LIKE '%story%' OR p.page_title LIKE '%article title 8%')
GROUP BY p.page_id
ORDER BY (t.tag_name+0) ASC
哎哟

我认为您的SQl查询非常奇怪

需要注意的几点:

  • 对于SQL引擎来说,使用像“%foo%”这样的
    条是非常困难的,他必须顺序扫描所有行并在列栏中搜索子字符串“foo”。索引用法不可用。所以,如果可以的话,尽量避免。如果可以,请至少使用类似于“foo%”的
    条。
    。在您的情况下,您可能会有标题“article title 80”匹配的页面,您确定不只是需要一个
    p.page\u title='article title 8'
  • 为什么要在order by指令中设置
    +0
    ?是否确实要阻止使用索引
  • p.page\u hide!='1’
    ,p.page\u hide不是一个小玩意吗?是一根绳子吗?为什么使用UTF8编码字符存储0或1
但这不是问题所在

您的问题之一是,在SQL中使用GROUPBY
GROUPBY p.page_id
实际上是错误的,但MySQL隐藏了这一事实。group by指令应至少包含SELECT部分中非聚集的每个元素(聚合是count或sum,或avg等)。在这里,你按id分组,得到一个随机的结果,MySQL认为你知道你在做什么,并且你确信当id相同时,select中的每个其他字段都是相同的(事实并非如此,标记名称不同)

如果你有几个标签匹配你的关键字('故事'在这里),你不想页面被列出几次吗?所有的标签

所以

您想选择一个有标签的页面。我会说使用
EXISTS
关键字,让事情变得更简单

可能是这样的:

SELECT * 
 FROM root_pages AS p
WHERE p.page_title = 'article title 8'
 AND p.page_hide != 1
 -- exists will return true as soon as the engine find one matching row
 AND EXISTS (
  SELECT mm.page_id
  FROM root_mm_pages_tags AS mm
    LEFT JOIN root_tags AS t
      ON t.tag_id =  mm.tag_id
  -- here we make a correlation between the subquery and the main query
  WHERE mm.page_id = p.page_id
  AND t.tag_name LIKE '%story%'
)
但是通过这个查询,您只能获得页面名称,而不是标记结果。如果您想列出某个页面的所有匹配标记,则需要另一个查询,该查询非常接近您所拥有的:

SELECT p.page_id, p.page_name, t.tag_name
 FROM root_pages AS p
   INNER JOIN root_mm_pages_tags AS mm
       ON mm.page_id = p.page_id
     INNER JOIN root_tags AS t
         ON (t.tag_id =  mm.tag_id 
         AND t.tag_name LIKE '%story%')
WHERE p.page_title = 'article title 8'
 AND p.page_hide != 1
对于第一个
内部连接
,我只保留带有标记的页面。使用第二个
内部连接
时,我只保留
根页面
中的行,在
根页面标签
中有一个匹配的标签。我认为您的NULL来自于此表中链接到其他不匹配标记的行(因此在root_tags表result中有NULL字段供您查询)因此,如果只需要匹配结果,请不要使用左连接

如果每个表只需要一个结果,则需要按p.page\u id、p.page\u name进行分组,并且需要在剩余字段
t.tag\u name
上添加聚合函数。您可以使用
GROUP\u CONTACT(t.tag\u name ORDER BY t.tag\u name ASC SEPARATOR“,”)
获取此表的所有匹配标记的列表

编辑

因此,事实上,您似乎想要标题匹配的页面,或者关键字匹配的页面。在这种情况下,您应该使用
左连接
,并且您将拥有空值。如果结果中不需要标记,EXISTS关键字仍然是您最好的朋友,只需将
和EXISTS
替换为
或EXISTS
。这是最快的解决方案

如果在结果中需要匹配的标记,或者在没有标记时为NULL,则有2种解决方案。一个
UNION
查询混合了对标题的简单查询和对带有内部连接的标记的查询,或者使用group\u CONCAT执行nice group by。如果不使用GROUP_CONCAT(如@Dmitry Teplyakov answer中所示),您可能会获得页面标题不匹配的结果,只有关键字,但tag_name字段将显示NULL,因为在查询中应用GROUP BY之前列出的第一个tag_行是一个NULL字段——页面为3个关键字,匹配的关键字不是查询中的第一个--


但在这里,我们按标签名称取消了您的订单。按tag_name排序意味着如果同一页与关键字多次匹配,您希望它出现在多行中。或者如果名称匹配且关键字也匹配。。。也许不是。所以实际上,联合查询解决方案可能更好。但关键的一点是,您应该在tag_name字段中解释您想要什么:-)

这是我的初始查询,但我需要使此查询动态化,因为有该关键字的行,以及没有该关键字的行。这有意义吗?谢谢。如果是这样的话,那么如果您使用或代替和条件(p.page_title,如'%article title 8%'或t.tag_name,如'%story%'),只需修改上面提供的查询,您就可以从中获得动态结果谢谢你的提醒,德米特里!:-)非常感谢你的回复,雷吉勒罗。事实上,我得到了你的建议后,我的结果-见我的编辑上面。谢谢你的帮助!:-)@劳蒂亚姆科克:看我的编辑,我不确定你真的有你想要的。取决于你真正想要什么。在SQL中要小心那些似乎给出了正确结果的查询,进行扩展测试cases.regilero,感谢您的编辑。我将代码更改为使用
内部连接
作为saf
SELECT p.page_id, p.page_name, t.tag_name
 FROM root_pages AS p
   INNER JOIN root_mm_pages_tags AS mm
       ON mm.page_id = p.page_id
     INNER JOIN root_tags AS t
         ON (t.tag_id =  mm.tag_id 
         AND t.tag_name LIKE '%story%')
WHERE p.page_title = 'article title 8'
 AND p.page_hide != 1
SELECT 
 p.page_id,
 p.page_name,
 GROUP_CONCAT(t.tag_name ORDER BY t.tag_name ASC SEPARATOR ",")
FROM root_pages AS p
   LEFT JOIN root_mm_pages_tags AS mm
       ON mm.page_id = p.page_id
     LEFT JOIN root_tags AS t
         ON t.tag_id =  mm.tag_id 
WHERE p.page_hide != 1
 AND (p.page_title = 'article title 8'
  OR t.tag_name LIKE '%story%')
GROUP BY p.page_id, p.page_name;