Объединять таблицы со значениями запятой - PullRequest
0 голосов
/ 26 августа 2011

У меня крепкий орешек, когда я присоединяюсь к 3 столам.У меня есть 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-запрос присоединился к 3 таблицам, и в этом процессе выберите получателей из таблицы почтовой группы и отобразите их через запятуюкак 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 ."'; 

Но я не могу понять, как включить в это почтовые группы.

Ответы [ 3 ]

2 голосов
/ 26 августа 2011

Я рекомендую вам сделать ваши объединения явными.
Это облегчает отладку вашего запроса и изменение внутреннего с помощью левых объединений.
Абсолютно никогда не существует веской причины использовать синтаксис неявного объединения SQL '89.

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;

Это изменение также должно значительно ускорить ваш запрос.

1 голос
/ 26 августа 2011

Если это разрешено, почему бы вам не создать новую таблицу под названием newsletter_item_receivers, где вы можете хранить поля letter_id, receive_id? Наличие в поле значений, разделенных запятыми, обычно означает, что вам не хватает таблицы:)

Edit:

Используя CSV, вы делаете свою жизнь несчастной, когда хотите получить ответ на «дать мне все информационные бюллетени, которые получает receive_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, вам бы посоветовали ознакомиться с нормализацией базы данных и понять, почему это важно.

Вы можете начать с этой статьи с сайта about.com, просто посмотрите, кажется, что все в порядке. http://databases.about.com/od/specificproducts/a/normalization.htm

Кроме того, было бы хорошо, если бы вы сохранили одинаковые имена полей в нескольких таблицах. Например, у вас есть letter_id в newsletter_items, но у вас есть field_letter_uid в newsletter_fields. Просто мысль:)

0 голосов
/ 26 августа 2011

Попробуйте использовать

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;
...