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: -)