Объединение таблиц с запятыми
У меня есть твердая гайка, чтобы взломать три столы.
У меня есть newsletter_items, newsletter_fields и newsletter_mailgroups, к которым я хочу присоединиться, чтобы получить список информационных бюллетеней.
Newsletter_items содержит поля:
letter_id, letter_date, receivers, template, status
That can look like
1, 1234567899, 1,2 (comma separated), standard.html, 1
newsletter_fields содержит поля:
field_uid, field_name, field_content, field_letter_uid
That can look like
1, letter_headline, A great headline, 1
где field_letter_uid - это информационный бюллетень, для которого принадлежит поле.
и newsletter_mailgroups содержит поля:
mailgroup_id, mailgroup_name, number_of_members
That can look like
1, Group1, 233
2, Group2, 124
3, Group3, 54
То, что я хочу, - это объединить эти 3 таблицы, чтобы я мог получить список всего информационного бюллетеня следующим образом:
Letter date | Letter headline | Receivers | Status
2008-01-01 12:00:00 | A great headline | Group1, Group 2 | 1
Короче говоря, я хочу, чтобы мой SQL-запрос присоединился к трем таблицам, и в этом процессе выберите получателей из таблицы mailgroup и покажите их запятыми, как Group1, Group 2
Это то, что я получил сейчас
SELECT A.*, B.* FROM newsletter_items A, newsletter_fields B, WHERE B.field_letter_uid = A.letter_id AND field_name = 'letter_headline' AND A.template = '". $template ."';
Но я не могу понять, как получить в нем почтовые группы.
Ответы
Ответ 1
Я рекомендую вам сделать ваши соединения явными.
Это облегчает отладку вашего запроса и изменение внутреннего с помощью левого соединения.
Нет абсолютно никакой причины использовать синтаксис неявного синтаксиса SQL89.
SELECT ni.*
, nf.*
, group_concat(nm.mailgroup_name) as mailgroups
FROM newsletter_items ni
INNER JOIN newsletter_fields nf
ON (nf.field_letter_uid = ni.letter_id)
INNER JOIN newsletter_mailgroups nm
ON (find_in_set(nm.mailgroup_id, ni.receivers))
WHERE
nf.field_name = 'letter_headline'
ni.template = '". $template ."'
GROUP BY ni.letter_id;
Что касается дизайна вашей базы данных.
Я рекомендую вам нормализовать вашу базу данных, это означает, что вы перемещаете поля, разделенные запятыми, в другую таблицу.
Итак, вы делаете приемники таблицы
Receivers
----------
id integer auto_increment primary key
letter_id integer not null foreign key references newsletter_items(letter_id)
value integer not null
Затем вы удалите приемник поля из таблицы newsletter_items
Затем ваш запрос изменяется на:
SELECT ni.*
, group_concat(r.value) as receivers
, nf.*
, group_concat(nm.mailgroup_name) as mailgroups
FROM newsletter_items ni
INNER JOIN newsletter_fields nf
ON (nf.field_letter_uid = ni.letter_id)
INNER JOIN newsletter_mailgroups nm
ON (find_in_set(nm.mailgroup_id, ni.receivers))
LEFT JOIN receiver r ON (r.letter_id = ni.letter_id)
WHERE
nf.field_name = 'letter_headline'
ni.template = '". $template ."'
GROUP BY ni.letter_id;
Это изменение также должно значительно ускорить ваш запрос.
Ответ 2
Если это разрешено, почему бы вам не создать новую таблицу, называемую newsletter_item_receivers, где вы могли бы хранить буквы_ид, поля receiver_id?
Если значения, разделенные запятыми, в таком поле обычно означают, что вам не хватает таблицы:)
Edit:
Используя CSV, вы делаете свою жизнь несчастной, когда хотите получить ответ, чтобы "дать мне все информационные бюллетени, которые получает receiver_id = 5":)
Вот хороший ответ на аналогичный вопрос о SO: Значения, разделенные запятой, в поле базы данных
Edit2:
Если я правильно понимаю ваши отношения с таблицами, это будет примерно так:
SELECT
a.letter_date,
b.receiver_id,
a.status
FROM newsletter_items_receivers b
LEFT OUTER JOIN newsletter_items a ON (a.letter_id = b.letter_id)
LEFT OUTER JOIN newsletter_mailgroups m ON (m.mailgroup_id = b.receiver_id)
ВНИМАНИЕ! Этот запрос НЕ вернет информационный бюллетень, если нет получателей этого бюллетеня.
Если вам нужна эта функциональность, вы можете попробовать что-то вроде этого:
SELECT
x.letter_date,
y.mailgroup_name,
x.status
FROM (
SELECT
a.letter_date,
b.receiver_id,
a.status
FROM newsletter_items a
LEFT OUTER JOIN newsletter_items_rec b ON (b.letter_id = a.letter_id)) x
LEFT OUTER JOIN newsletter_mailgroups y ON (y.mailgroup_id = x.receiver_id)
У меня нет доступа к SQL прямо сейчас, поэтому я мог бы сделать некоторые синтаксические ошибки (надеюсь, не логические:)).
Что касается того, почему мы это делаем, как отметил @Konerak, вам будет полезно прочитать нормализацию базы данных и почему это важно.
Вы можете начать с этой статьи с сайта .com, просто взглянув на нее, кажется, что это нормально читать
http://databases.about.com/od/specificproducts/a/normalization.htm
Кроме того, было бы хорошо, если бы имена полей были одинаковыми для нескольких таблиц.
Например, у вас есть letter_id в newsletter_items, но у вас есть field_letter_uid в newsletter_fields. Просто мысль:)
Ответ 3
Попробуйте использовать
SELECT A.*, B.*, group_concat(C.mailgroup_name SEPARATOR ',')
FROM newsletter_items A, newsletter_fields B, newsletter_mailgroups C
WHERE B.field_letter_uid = A.letter_id
AND field_name = 'letter_headline'
AND A.template = '". $template ."'
and find_in_set(c.mailgroup_id, A.receivers)
group by A.letter_id;