За последние 24 часа нас посетили 34372 программиста и 1709 роботов. Сейчас ищут 803 программиста ...

Извлечь список строк с несколькими значениями параметра

Тема в разделе "PHP для новичков", создана пользователем bocharsky, 13 фев 2015.

  1. bocharsky

    bocharsky Новичок

    С нами с:
    18 апр 2014
    Сообщения:
    77
    Симпатии:
    1
    Не соображу, как "экономно" с точки зрения запросов к базе организовать вывод списка статей с набором рубрик к каждой.

    Есть заголовки:
    Статья1
    Статья2
    Статья3

    Есть конечный набор (несколько десятков) рубрик: авто, финансы, ритейл, авиа и пр.
    Каждой статье соответствует несколько рубрик.
    Статья 1: авто, финансы
    Статья 2: ритейл, авто, авиа
    Статья3: финансы
    и пр.

    Статьи лежат в своей таблице Article. Для рубрик есть своя таблица Category. Плюс я создал таблицу Art_cat, куда записывается соответствие статья - рубрика вида:
    id | article_id | cat_id
    1 | 104 | 2
    2 | 104 | 3
    3 | 123 | 5
    4 | 123 | 3
    5 | 123 | 1
    6 | 129 | 3

    Вопрос:
    Как мне "экономно" с точки зрения БД создать на вебе результат вида
    Статья 1 | авто, финансы
    Статья 2 | ритейл, авто, авиа
    Статья3 | финансы

    Если бы у каждой статьи была только одна рубрика, я бы хранил ее в таблице со статьями и извлекал бы одним запросом:
    SELECT article, category FROM Article

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

    Я понимаю, что можно сначала извлечь список статей, а затем для каждого id статьи извлекать его набор рубрик из таблицы Category. Но это будет означать пресловутый и осуждаемый "Селект в цикле".

    Понимаю, что эта задача стандартная и имеет какой-то принятый подход. Буду признателен указанию, как принято решать такие задачки.

    Спасибо!
     
  2. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    Во первых, поищи примеры запросов с функцией group_concat(). Во вторых, ты можешь заложить в таблицу articles небольшое излишество - список рубрик. Не для использования в запросах, а только для вывода.

    Добавлено спустя 2 минуты 19 секунд:
    Например в базе stackoverflow в таблице постов есть список поисковых тегов. При том, что существует отдельная таблица tags и таблица-связка post_tags.
    Можно считать это хорошей практикой. ;)
     
  3. bocharsky

    bocharsky Новичок

    С нами с:
    18 апр 2014
    Сообщения:
    77
    Симпатии:
    1
    Почитал. Очень круто! Спасибо огромное. Внедряю.

    Я в первом подходе так сделал. Завел в Articles колонку cats и хранил там строки вида 'авто, финансы, авива'.
    Для вывода очень удобно.

    Но если все равно нужно составлять сложный запрос с джойнами, который будет решать задачу работы с рубриками/тегами для выборок (например, вывести все запросы для "авто"), то какой тогда в этом смысл? Я же все равно получу, например, через group_concat тот же результат - строку вида 'авто, финансы, авива'.

    Т.е. в результате выполнения запроса у меня будет в результирущей таблице колонка со значением 'авто, финансы, авива', получeнная из Articles и колонка со значением 'авто, финансы, авива', полученная с помощью group_concat. В таком случае держать столбец со строкой рубрик в Articles нет смысла.
    Так?

    Я так понимаю, что это полезно, если нужно просто вывести Заголовок и строку тегов без возможности операций с тегами. Тогда будет быстро и удобно. Скажем, просто, как реквизиты к статье (дата, автор, список тегов). Но если надо выполнять с ними какие-то действия, например, по клику на тег получать вывод всех заметок по тегу, то тогда такая строка бесполезна.
    Так?

    В общем, в который раз, огромное спасибо!
     
  4. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    Это полезно для вывода без лишних запросов. Годится ли для пользовательских действий — это уже от тебя зависит. Можно искать по имени тега? Тогда не вижу проблем с тем чтобы сформировать список ссылок.

    Добавлено спустя 18 минут 49 секунд:
    Код (PHP):
    1. $str = 'ритейл, авто, авиа';
    2. $parts = explode(',', $str);
    3. $links = array_map(
    4.   function ($str) {
    5.     $str = trim($str);
    6.     return '[url="/tags/'.$str.'"]'.$str.'[/url]';
    7.   },
    8.   $parts
    9. ); 
     
  5. bocharsky

    bocharsky Новичок

    С нами с:
    18 апр 2014
    Сообщения:
    77
    Симпатии:
    1
    Задам как мне кажется крамольный вопрос.
    Если в таблице Articles есть поле categories, в котором записаны строки в виде набора тегов, например "авто, авиа, финансы", то можно обойтись без отдельной таблицы с тегами и объединений, а делать выборки по одной таблице с помощью LIKE?

    Скажем, если у нас есть
    Статья1 | авто, авиа
    Статья2 | авиа, финансы
    И пользователь хочет выбрать статьи по тегу "авиа", то можно сделать SELECT * WHERE catgories LIKE '%авиа%'

    Это ужасно?

    Я слышал, что LIKE очень медленный и все такое.

    Поэтому позвольте вдогон еще вопрос:
    Представим, что у меня в таблице 100 тыс строк (статей). Но отбор я провожу среди малой группы. Типичный случай - ограничение LIMIT (10, или 20 статей на страницу). Мой частный случай - статьи с датой не старше Х (новые материалы), получается менее 100 строк.
    Можно Базе данных как-то сказать, чтобы она сначала отобрала нужные мне 10 - 100 строк (те, у которых дата не старше Х), а затем уже для них выполняла сложные объединения и прочие операции?
    В результате будет в принципе быстрее, ну и если говорить про LIKE, то поиск по 20 - 30 строкам даже LIKE сделает быстро.

    Можно ли и как?

    Спасибо!
     
  6. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    Еще раз: значения в текстовом поле хранятся только для быстрого вывода, чтобы не создавать джойны ради показа списка тегов.
    Конечно LIKE '%строка%' медленный, он гарантированно приведет к полному перебору без индекса.

    Можно ли сначала ограничить набор строк, а потом произвести над ними дополнительныую работу? Да, конечно. НО, почему ты уверен, что все твои будущие запросы про категории будут работать с малыми наборами? Попробуй, потом расскажешь.
     
  7. bocharsky

    bocharsky Новичок

    С нами с:
    18 апр 2014
    Сообщения:
    77
    Симпатии:
    1
    Принял подсказанную вами концепцию без фантазий)))
    Только вот какая поправка (которая немного испортило впечатление "простоты"):

    В таблице Articles не получается хранить готовую строку тегов, например, "авто, финансы", как хотелось бы. Там приходится хранить строку из id тегов. Условно "4, 12".
    Потому что, если записывать туда названия тегов, то при коррекции названия, будет разнобой. Условно, поменяли сегодня "ритейл" на "торговлю", получили, что у статей, записанных до сегодня будет выводится "ритейл", а у новых - "торговля".

    Если же в Article хранить id тегов, то требуется ряд дополнительных телодвижений.
    У меня они выглядят так:
    1. Заранее составил массив в качестве "таблицы соответствий" вида id['industry'], например,
    4['авто'],
    5[ритейл]
    и пр.
    К счастью мне все равно приходится извлекать отрасли для построения фильтра, так что SELECT все равно уже есть. Так что с точки зрения БД выходит без доп.затрат.
    Просто формирую из результата Селекта еще один массив нужным мне образом.
    2. Извлекая данные из Article получаю ту самую строку из айдишников тегов, например "4, 5"
    3. Превращаю с помощью explode в массив
    4. При выводе на вебе, с помощью foreach извлекаю по очереди из массива
    5. С помощью search_array получаю название отрасли по id из "массива соответствий".
    6. Печатаю

    Все это заставило задуматься: может не городить огород, а все-таки втулить в Select блок с group_concat и сразу получать готовый набор названий.

    Понятно, что ответ на этот вопрос должен получить я сам, просто замерив время работы и так, и так.

    Пишу же это на всякий случай: вдруг я излишне усложнил получение название тегов, или в принципе не догнал какой-то концепт, который упрощает эту задачу. Если так, буду признателен подсказке.

    Спасибо
     
  8. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    мне кажется тебе подсказки уже не нужны ;)