我有一个很难解决的问题是加入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
我想要的是将这三个表格合并到一起,我可以获得所有时事通讯的列表:
Letter date | Letter headline | Receivers | Status
2008-01-01 12:00:00 | A great headline | Group1, Group 2 | 1
所以简而言之,我希望我的SQL查询加入3个表,并在该过程中从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 ."';
但我似乎无法弄清楚如何将邮件组纳入其中。
答案 0 :(得分:2)
我建议您明确联接。
它使您可以更轻松地调试查询并使用左连接更改内部
绝对没有充分的理由使用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 :(得分:1)
如果允许,为什么不创建一个名为newsletter_item_receivers的新表,您可以在其中存储letter_id,receiver_id字段? 在这样的字段中使用逗号分隔值通常意味着您缺少一个表:)
修改强>
通过使用CSV,当您想要检索“给我所有收到receiver_id = 5收到的简报”的答案时,您的生活将变得悲惨:)
这是关于SO的类似问题的一个很好的答案:Comma separated values in a database field
<强> 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
此外,如果您在多个表中保持字段名称相同,那将是一件好事。 例如,你在newsletter_items中有letter_id,但是你在newsletter_fields中有field_letter_uid。只是一个想法:)
答案 2 :(得分:0)
尝试使用
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;