|
|
|
| Есть две таблицы:
Статьи:
CREATE TABLE articles(
id_article INT(11) NOT NULL AUTO_INCREMENT,
title TINYTEXT NOT NULL COMMENT 'Заголовок статьи',
description TINYTEXT NOT NULL COMMENT 'Описание статьи (используется для анонса)',
body TEXT NOT NULL COMMENT 'Тело статьи',
showhide ENUM('0', '1') NOT NULL DEFAULT '0' COMMENT 'Видимость статьи',
type ENUM('article', 'news', 'other') NOT NULL COMMENT 'Тип статьи: Статья, Новость, Проче',
PRIMARY KEY (id_article)
)
ENGINE = MYISAM
AUTO_INCREMENT = 367
COMMENT = 'Статьи';
|
и метки к статьям:
CREATE TABLE labels(
id INT(11) NOT NULL COMMENT 'Ключ на объект',
label VARCHAR(255) NOT NULL COMMENT 'Метка',
type ENUM('article', 'news', 'nmc') DEFAULT NULL COMMENT 'Тип: Статья, Новость или Номенклатура'
)
ENGINE = MYISAM
COMMENT = 'Метки к статьям, новостям и номенклатуре';
|
Нужно сделать выборку статей, и меток к ним. Т.е. нужно сделать постарничную навигацию, по каждой записи выводить Название, Описание, и список меток.
Мне в голову пришел только такой запрос:
SELECT
articles.id_article,
articles.title,
articles.descroption,
(SELECT GROUP_CONCAT(label) FROM labels WHERE type = 'article' AND id = articles.id_article) AS label
FROM articles
ORDER BY $order $sort
LIMIT $start, $numrec";
|
А потом в скрипте резать "label" на части и обрамлять в ссылки. Но, че-то мне эта идея самому не нравится.
Может есть какой-нибудь другой, более граматный, вариант? | |
|
|
|
|
|
|
|
для: tAleks
(25.04.2011 в 23:41)
| | Коррелированный подзапрос? В принципе можно, если скорость его выполнения приемлемая? А почему не хотите двух-табличный через JOIN сделать (функцию GROUP_CONCAT() там можно применить точно так же)? | |
|
|
|
|
|
|
|
для: cheops
(25.04.2011 в 23:49)
| | Это как? Можно примерчик? | |
|
|
|
|
|
|
|
для: tAleks
(25.04.2011 в 23:50)
| | Имеется в виду вот что
SELECT
articles.id_article,
articles.title,
articles.descroption,
GROUP_CONCAT(labels.label) AS label
FROM
articles
LEFT JOIN
labels
ON
labels.type = 'article' AND
labels.id = articles.id_article
ORDER BY $order $sort
LIMIT $start, $numrec
|
| |
|
|
|
|
|
|
|
для: cheops
(26.04.2011 в 00:00)
| | Спасибо за пример. И еще вопрос: А как-то по другому, без применения GROUP_CONCAT() решить данную задачу? | |
|
|
|
|
|
|
|
для: tAleks
(26.04.2011 в 15:33)
| | В один запрос? Если да, то скорее всего не выйдет, GROUP_CONCAT() самое разумное тут решение. Если вызов запросов в цикле не пугает, можно ими воспользоваться. Т.е. вызываем один запрос, а затем в цикле дополнительные запросы на каждой итерации цикла обработки результирующей таблицы. | |
|
|
|
|
|
|
|
для: cheops
(26.04.2011 в 15:46)
| | Не, это еще страшней, про доп запросы в цикле на каждую запись.
Сделал по вышеукзанному примеру (без конструкции GROUP BY articles.id_article ругался). Добавил эту конструкцию - в принципе все работает.
Теперь еще нужно реализовать задачу, чтобы при клике на метку, выводились все статьи с этой меткой, в таком же формате. т.е. заголовок, описание, и список меток.
Когда добавляю в этот запрос условие:
WHERE labels.label LIKE '%спорт%'
|
То, оно работает (статьи с такой меткой фильтруются), но при этом в списке меток всех статей, показывается только одна эта метка, в данном случае "спорт".
Подскажите что подправить. Как сделать чтобы при фильтре с меткой выводились все метки к сатьям? | |
|
|
|
|
|
|
|
для: tAleks
(26.04.2011 в 17:33)
| | Тогда наверное придется возвращаться к коррелированным подзапросам или вводить третью таблицу labels (назначая ей алиас) специально для извлечения меток. | |
|
|
|
|
|
|
|
для: cheops
(26.04.2011 в 22:54)
| | Добавил еще одну таблицу, так:
SELECT
articles.id_article,
articles.id_group,
articles.title,
articles.showhide,
articles.datetime_add,
articles_labels.label,
GROUP_CONCAT(labels.label) AS labels
FROM articles
LEFT JOIN articles_labels ON articles_labels.id_article = articles.id_article
LEFT JOIN articles_labels AS labels ON labels.id_article = articles.id_article
GROUP BY articles.id_article
ORDER BY datetime_add DESC
LIMIT 0, 15
|
Но при таком запросе, список меток извлекается как-то странно. Если метка одна, то все нормально. Если меток две - то они извлекаеются дважды, если три - то трижды.
Может я че-то напутал?
PS.: Таблицу labels переименовал в articles_labels и грохнул из нее колонку type. | |
|
|
|
|
|
|
|
для: tAleks
(27.04.2011 в 08:25)
| | Это в поле labels? Используйте ключевое слово DISTINCT
SELECT
articles.id_article,
articles.id_group,
articles.title,
articles.showhide,
articles.datetime_add,
articles_labels.label,
GROUP_CONCAT(DISTINCT labels.label) AS labels
FROM articles
LEFT JOIN articles_labels ON articles_labels.id_article = articles.id_article
LEFT JOIN articles_labels AS labels ON labels.id_article = articles.id_article
GROUP BY articles.id_article
ORDER BY datetime_add DESC
LIMIT 0, 15
|
| |
|
|
|
|
|
|
|
для: cheops
(27.04.2011 в 10:51)
| | Спасибо!
Это будет быстрей чем кореллированный запрос? | |
|
|
|
|
|
|
|
для: cheops
(27.04.2011 в 10:51)
| | Спасибо!
Это будет быстрей чем кореллированный запрос? | |
|
|
|
|
|
|
|
для: tAleks
(27.04.2011 в 14:24)
| | Да, должно быть быстрее (если есть время погоняйте запросы на вашем сервере, только несколько раз каждый запрос, но я не думаю, что там будут какие-то неожиданности - подзапросы хуже оптимизируются (хотя в последнее время много сделано в этом направлении), а коррелированные подзапросы хуже всех (и вот тут помоему еще ничего не делали)). | |
|
|
|
|
|
|
|
для: cheops
(27.04.2011 в 14:31)
| | Еще, по задаче нужно сделать связку статей и пользователей. Т.е. на определенных субодменах (которые принадлежат консультантам) выводить статьи только этих консультантов.
Четырехтабличный запрос (если к этому запросу еще прибавить связку пользователей) это будет вообще жесть? Или нормально? | |
|
|
|
|
|
|
|
для: tAleks
(28.04.2011 в 15:32)
| | Как проиндексируете...
PS А вообще если будет медленно, вы же всегда можете создать кэширующую таблицу, со связями консультатн - список всех его статей и сопутствующей информации. Да, это потребует определенных телодвижений на стадии добавления и редактирования, но снизит нагрузку по выборке из базы данных. Если есть место на диске, терпение и время, любую базу данных можно обуздать. | |
|
|
|
|
|
|
|
для: cheops
(28.04.2011 в 15:36)
| | Совсем забыл... еще же группы... статьи должны быть рассортированы по группам, у них должны быть метки, и еще по пользователям.... это ж, капец, вообще 5-и табличный запрос получается....
Как их лучше будет проиндексировать, в таком случае?
> вы же всегда можете создать кэширующую таблицу
Что за кэширующая таблица? Первый раз слышу. Как ее создавать? | |
|
|
|
|
|
|
|
для: tAleks
(28.04.2011 в 17:06)
| | >Что за кэширующая таблица? Первый раз слышу. Как ее создавать?
Это обычная таблица, пусть вам нужно связать пользователей и их статьи, создаете таблицу, где пользователям соответствуют их статьи
id_user - articles
45 - 2, 8, 10, 15, 17, 22
46 - 1, 9, 11, 18, 36
47 - 4, 12, 23, 37
| Как только пользователь добавляет или удаляет статью, вы уничтожаете для него запись в этой таблице и расчитываете её по новой. Потребовались вас статьи пользователя 46, разбросанные по множеству разделов, вместо того чтобы строить многоэтажные запросы вы извлекаете запись из этой таблице (благодаря индексированию это будет очень быстро) и формируете запрос
SELECT * FROM tbl WEHRE id_article IN (1, 9, 11, 18, 36)
| т.е. вместо того, чтобы мучительно составлять список каждый раз, вы держите его наготове заранее вычисленным в специальной таблице. И таких таблиц под наиболее трудоекие задачи можно построить несколько. Да у вас немного замедляется и затрудняется процесс добавления статьи и усложняется структура базы данных. Однако вставка - это редкая операция, а выборка частая. | |
|
|
|