SQL连接在M:N表上

时间:2011-01-10 14:29:54

标签: sql join

现有五个表:

(MAIL)
id  senderFK  receiverFK  text

发件人和收件人在{​​{1}}表格中引用:

MN

(MN) id studentFK teacherFK guestFK 中的每个条目只能填充MN和三个外键列中的一个。 例如,如果某一行在id中有id 42和16,则它会引用下表中带有studentFK 16的条目:

id

发件人/收件人的另外两个可能的表是:

(STUD)
id  name  grade  hasStudCard

(TEACH)
id  name  age  telephone

学生,老师和客人可以是邮件的发件人或收件人。

现在我想创建一个视图,用发件人和收件人可以拥有的所有数据填充邮件表。当然,我可以在(GUEST) id department Mail以及其他三个关于ID的全外联接。但是有更有效的方法吗?

3 个答案:

答案 0 :(得分:1)

select
      m.id              as MailID
    , m.text            as MailText

    , snd.PersonType    as SenderType
    , snd.PersonID      as SenderID
    , snd.Name          as SenderName
    , snd.grade         as SenderGrade
    , snd.hasStudCard   as SenderHasStudCard
    , snd.age           as SenderAge
    , snd.telephone     as SenderTelephone
    , snd.department    as SenderDepartment

    , rec.PersonType    as ReceiverType
    , rec.PersonID      as ReceiverID
    , rec.Name          as ReceiverName
    , rec.grade         as ReceiverGrade
    , rec.hasStudCard   as ReceiverHasStudCard
    , rec.age           as ReceiverAge
    , rec.telephone     as ReceiverTelephone
    , rec.department    as ReceiverDepartment

from MAIL as m
join
(
    select
          p.id
        , case 
             when s.id is not null then 'Student'
             when t.id is not null then 'Teacher'
             when g.id is not null then 'Guest'
          end        as PersonType
        , coalesce(s.id, t.id, g.ID)   as PersonID
        , coalesce(s.Name, t.name, '') as Name
        , grade
        , hasStudCard
        , age
        , telephone
        , department
    from MN           as p
    left join STUDENT as s on (s.id = p.studentFK and p.teacherFK is null and p.guestFK   is null)
    left join TEACH   as t on (t.id = p.teacherFK and p.studentFK is null and p.guestFK   is null)
    left join GUEST   as g on (g.id = p.guestFK   and p.studentFK is null and p.teacherFK is null)
) as snd on snd.id = m.senderFK
join
(
    select
          p.id
        , case 
             when s.id is not null then 'Student'
             when t.id is not null then 'Teacher'
             when g.id is not null then 'Guest'
          end        as PersonType
        , coalesce(s.id, t.id, g.ID)   as PersonID
        , coalesce(s.Name, t.name, '') as Name
        , grade
        , hasStudCard
        , age
        , telephone
        , department
    from MN           as p
    left join STUDENT as s on (s.id = p.studentFK and p.teacherFK is null and p.guestFK   is null)
    left join TEACH   as t on (t.id = p.teacherFK and p.studentFK is null and p.guestFK   is null)
    left join GUEST   as g on (g.id = p.guestFK   and p.studentFK is null and p.teacherFK is null)
) as rec on rec.id = m.receiverFK
;

答案 1 :(得分:0)

您的RDBMS可以使用'WITH'子句吗?

with
mnView as (
select
  id mnId,
  CASE
    WHEN MN.studentFK IS NOT NULL THEN 'S'
    WHEN MN.teacherFK IS NOT NULL THEN 'T'
    WHEN MN.guestFK IS NOT NULL THEN 'G'
    ELSE NULL
  END mnType,
  CASE
    WHEN MN.studentFK IS NOT NULL THEN MN.studentFK
    WHEN MN.teacherFK IS NOT NULL THEN MN.teacherFK
    WHEN MN.guestFK IS NOT NULL THEN MN.guestFK
    ELSE NULL
  END refId
  from MN
),
mnDetailView as (
select mnId, mnType, STUD.name, STUD.grade, STUD.hasStudCard,
  NULL age, NULL telephone, NULL department
from mnView
  join STUD on STUD.id = mnView.refId
where mnView.mnType = 'S'
union all
select mnId, mnType, TEACH.name, NULL grade, NULL hasStudCard,
  TEACH.age, TEACH.telephone, NULL department
from mnView
  join TEACH on TEACH.id = mnView.refId
where mnView.mnType = 'T'
union all
select mnId, mnType, NULL name, NULL grade, NULL hasStudCard, 
  NULL age, NULL telephone, GUEST.department
from mnView
  join GUEST on GUEST.id = mnView.refId
where mnView.mnType = 'G'
)
select
  MAIL.id,
  MAIL.text,
  sender.mnId senderMnId, sender.mnType senderMnType, sender.name senderName,
  /* sender.grade senderGrade, ... and so on */
  receiver.mnId receiverMnId, receiver.mnType receiverMnType, receiver.name
  /* receiver.grade receiverGrade, ... and so on */
receiverName
from MAIL
  join mnDetailView sender on sender.mnId = MAIL.senderFK
  join mnDetailView receiver on receiver.mnId = MAIL.receiverFK

答案 2 :(得分:0)

这非常不舒服,因为桌子(STUD)(TEACH)和(GUEST)的结构不同。你可以试试这个'野蛮人'全外连接:

SELECT (MAIL).id, (MAIL).text, (STUD).id, (STUD).name, (STUD).grade, (STUD).hasStudCard, (TEACH).id, (TEACH).name, (TEACH).age, (TEACH).telephone, (GUEST).id, (GUEST).department FROM (MAIL), (MN), (STUD), (TEACH), (GUEST) WHERE (MAIL).senderFK = (MN).id AND ((MN).studentFK = (STUD).id OR (MN).teacherFK = (TEACH).id OR (MN).guestFK = (GUEST).id)

为接收者提取发送者数据和类似的数据。

通常,如果其中一个表比其他表大得多,减小其大小并在缩减表上执行连接将提高性能。