Группировать результат по конкретному ключевому слову в MySQL? - PullRequest
3 голосов
/ 12 ноября 2011

У меня есть страница, помеченная несколькими тегами с ключевым словом, которое я ищу, и иногда оно не помечается этим ключевым словом, поэтому, когда у него есть эти теги, он вернет результат, подобный приведенному ниже,

запрос,

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

, поэтому я должен использовать GROUP BY для решения этой проблемы,

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

Но я после этого результата имею ключевое слово 1017 *, которое я ищу,

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

Ответы [ 3 ]

2 голосов
/ 12 ноября 2011

Старайтесь не использовать условие в левом соединении:

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_title LIKE '%article title 8%'
OR t.tag_name LIKE '%story%')
AND p.page_hide != '1'


GROUP BY p.page_id
ORDER BY (t.tag_name+0) ASC
1 голос
/ 12 ноября 2011

Oucha.

Ваши запросы SQl довольно странные, я думаю.

Несколько замечаний:

  • Использование bar LIKE '%foo%' очень сложно для движка SQL, он должен последовательно сканировать все строки и искать подстроку 'foo' в строке столбцов. Использование индекса недоступно. Поэтому избегайте этого, если можете. Используйте по крайней мере bar LIKE 'foo%', если можете (индекс доступен, если у вас есть начало). И в вашем случае у вас могут быть страницы с заголовком «article title 80», вы уверены, что вам просто не нужен p.page_title = 'article title 8'?
  • почему вы делаете +0 в заказе по инструкции? Вы действительно хотите предотвратить использование индекса?
  • p.page_hide != '1', p.page_hide не крошечный? это строка? зачем использовать символы в кодировке UTF8 для хранения 0 или 1?

Но это не проблема.

Одна из ваших проблем заключается в том, что использование группы по GROUP BY p.page_id на самом деле неправильно в SQL, но MySQL скрывает этот факт. Инструкция group by должна содержать, по крайней мере, каждый элемент, который не является аггегатом в части SELECT (агрегат - это count или sum, или avg и т. Д.). Здесь вы группируете по идентификатору и получаете случайную вещь, MySQL думает, что вы знаете, что делаете, и вы уверены, что все остальные поля в выборе одинаковы, когда идентификатор одинаков (что не так, tag_name отличается).

А если у вас есть несколько тегов, соответствующих вашему ключевому слову (здесь «история»), не хотите ли вы, чтобы страница отображалась несколько раз? со всеми тегами?

Зв

Вы хотите выбрать страницу, на которой у вас есть тег. Я бы сказал, использовать ключевое слово 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

С первым INNER JOIN я сохраняю только страницы с тегами. Со вторым INNER JOIN я сохраняю только строки из root_mm_pages, имеющие соответствующий тег в root_tags. Я думаю, что ваш NULL пришел из строк в этих таблицах, связанных с другими несоответствующими тегами (поэтому наличие поля NULL в таблице root_tags приводит к вашему запросу). Так что не используйте LEFT JOIN, если вам нужны только результаты совпадений .

Если вам нужен только один результат для каждой таблицы, вам понадобится GROUP BY p.page_id, p.page_name, и вам нужно будет добавить статистическую функцию в оставшееся поле t.tag_name. Вы можете использовать GROUP_CONTACT(t.tag_name ORDER BY t.tag_name ASC SEPARATOR ","), чтобы получить список всех подходящих тегов для этой таблицы.

EDIT

Так что на самом деле вам нужны страницы с подходящим заголовком ИЛИ страниц с подходящим ключевым словом. В этом случае вы должны использовать LEFT JOIN, и у вас будут значения NULL. Если вам не нужен тег в результате, ключевое слово EXISTS по-прежнему является вашим лучшим другом, просто замените AND EXISTS на OR EXISTS. Это самое быстрое решение.

Если вам нужны совпадающие теги в результате или NULL, если они не были тегами, у вас есть 2 решения. UNION смешивание запросов является результатом простого запроса по заголовкам и запроса по тегам с внутренними объединениями или создания хорошей группы с помощью GROUP_CONCAT. Если вы не используете GROUP_CONCAT (как в ответе @Dmitry Teplyakov), вы можете получить результаты, в которых заголовок страницы не совпадает, только ключевое слово, но поле tag_name будет показывать NULL в качестве первого tag_row, указанного до применения GROUP BY в запросе есть пустое поле - на странице 3 ключевых слова, соответствующее ключевое слово не первое в запросе -.

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;

Но здесь мы теряем ваш заказ по тэгу. Упорядочивание по tag_name означает, что вы хотите, чтобы одна и та же страница появлялась в нескольких строках, если она несколько раз совпадает с ключевым словом Или если имя совпадает, а ключевое слово тоже ... или, может быть, нет. Так что на самом деле решение запросов UNION может быть лучше. Но главное - вы должны объяснить, что вы хотите в поле tag_name: -)

0 голосов
/ 12 ноября 2011

вот пример запроса, который я упоминаю в комментарии:

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
...