Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
Объектно-ориентированное программирование на PHP. Авторы: Кузнецов М.В., Симдянов И.В. MySQL на примерах. Авторы: Кузнецов М.В., Симдянов И.В. Социальная инженерия и социальные хакеры. Авторы: Кузнецов М.В., Симдянов И.В. PHP Puzzles. Авторы: Кузнецов М.В., Симдянов И.В. PHP 5. На примерах. Авторы: Кузнецов М.В., Симдянов И.В., Голышев С.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум MySQL

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Выборка статей и меток к ним
 
 автор: tAleks   (25.04.2011 в 23:41)   письмо автору
 
 

Есть две таблицы:

Статьи:

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" на части и обрамлять в ссылки. Но, че-то мне эта идея самому не нравится.

Может есть какой-нибудь другой, более граматный, вариант?

  Ответить  
 
 автор: cheops   (25.04.2011 в 23:49)   письмо автору
 
   для: tAleks   (25.04.2011 в 23:41)
 

Коррелированный подзапрос? В принципе можно, если скорость его выполнения приемлемая? А почему не хотите двух-табличный через JOIN сделать (функцию GROUP_CONCAT() там можно применить точно так же)?

  Ответить  
 
 автор: tAleks   (25.04.2011 в 23:50)   письмо автору
 
   для: cheops   (25.04.2011 в 23:49)
 

Это как? Можно примерчик?

  Ответить  
 
 автор: cheops   (26.04.2011 в 00:00)   письмо автору
 
   для: 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

  Ответить  
 
 автор: tAleks   (26.04.2011 в 15:33)   письмо автору
 
   для: cheops   (26.04.2011 в 00:00)
 

Спасибо за пример. И еще вопрос: А как-то по другому, без применения GROUP_CONCAT() решить данную задачу?

  Ответить  
 
 автор: cheops   (26.04.2011 в 15:46)   письмо автору
 
   для: tAleks   (26.04.2011 в 15:33)
 

В один запрос? Если да, то скорее всего не выйдет, GROUP_CONCAT() самое разумное тут решение. Если вызов запросов в цикле не пугает, можно ими воспользоваться. Т.е. вызываем один запрос, а затем в цикле дополнительные запросы на каждой итерации цикла обработки результирующей таблицы.

  Ответить  
 
 автор: tAleks   (26.04.2011 в 17:33)   письмо автору
 
   для: cheops   (26.04.2011 в 15:46)
 

Не, это еще страшней, про доп запросы в цикле на каждую запись.

Сделал по вышеукзанному примеру (без конструкции GROUP BY articles.id_article ругался). Добавил эту конструкцию - в принципе все работает.

Теперь еще нужно реализовать задачу, чтобы при клике на метку, выводились все статьи с этой меткой, в таком же формате. т.е. заголовок, описание, и список меток.

Когда добавляю в этот запрос условие:

WHERE labels.label LIKE '%спорт%'

То, оно работает (статьи с такой меткой фильтруются), но при этом в списке меток всех статей, показывается только одна эта метка, в данном случае "спорт".

Подскажите что подправить. Как сделать чтобы при фильтре с меткой выводились все метки к сатьям?

  Ответить  
 
 автор: cheops   (26.04.2011 в 22:54)   письмо автору
 
   для: tAleks   (26.04.2011 в 17:33)
 

Тогда наверное придется возвращаться к коррелированным подзапросам или вводить третью таблицу labels (назначая ей алиас) специально для извлечения меток.

  Ответить  
 
 автор: tAleks   (27.04.2011 в 08:25)   письмо автору
 
   для: 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.

  Ответить  
 
 автор: cheops   (27.04.2011 в 10:51)   письмо автору
 
   для: 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

  Ответить  
 
 автор: tAleks   (27.04.2011 в 14:24)   письмо автору
 
   для: cheops   (27.04.2011 в 10:51)
 

Спасибо!

Это будет быстрей чем кореллированный запрос?

  Ответить  
 
 автор: tAleks   (27.04.2011 в 14:24)   письмо автору
 
   для: cheops   (27.04.2011 в 10:51)
 

Спасибо!

Это будет быстрей чем кореллированный запрос?

  Ответить  
 
 автор: cheops   (27.04.2011 в 14:31)   письмо автору
 
   для: tAleks   (27.04.2011 в 14:24)
 

Да, должно быть быстрее (если есть время погоняйте запросы на вашем сервере, только несколько раз каждый запрос, но я не думаю, что там будут какие-то неожиданности - подзапросы хуже оптимизируются (хотя в последнее время много сделано в этом направлении), а коррелированные подзапросы хуже всех (и вот тут помоему еще ничего не делали)).

  Ответить  
 
 автор: tAleks   (28.04.2011 в 15:32)   письмо автору
 
   для: cheops   (27.04.2011 в 14:31)
 

Еще, по задаче нужно сделать связку статей и пользователей. Т.е. на определенных субодменах (которые принадлежат консультантам) выводить статьи только этих консультантов.

Четырехтабличный запрос (если к этому запросу еще прибавить связку пользователей) это будет вообще жесть? Или нормально?

  Ответить  
 
 автор: cheops   (28.04.2011 в 15:36)   письмо автору
 
   для: tAleks   (28.04.2011 в 15:32)
 

Как проиндексируете...

PS А вообще если будет медленно, вы же всегда можете создать кэширующую таблицу, со связями консультатн - список всех его статей и сопутствующей информации. Да, это потребует определенных телодвижений на стадии добавления и редактирования, но снизит нагрузку по выборке из базы данных. Если есть место на диске, терпение и время, любую базу данных можно обуздать.

  Ответить  
 
 автор: tAleks   (28.04.2011 в 17:06)   письмо автору
 
   для: cheops   (28.04.2011 в 15:36)
 

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

Как их лучше будет проиндексировать, в таком случае?

> вы же всегда можете создать кэширующую таблицу

Что за кэширующая таблица? Первый раз слышу. Как ее создавать?

  Ответить  
 
 автор: cheops   (28.04.2011 в 17:19)   письмо автору
 
   для: 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)
т.е. вместо того, чтобы мучительно составлять список каждый раз, вы держите его наготове заранее вычисленным в специальной таблице. И таких таблиц под наиболее трудоекие задачи можно построить несколько. Да у вас немного замедляется и затрудняется процесс добавления статьи и усложняется структура базы данных. Однако вставка - это редкая операция, а выборка частая.

  Ответить  
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования