SQL连接带有/不带数据的多个表

时间:2016-01-29 15:08:26

标签: sql sql-server join exists outer-join

我不知道如何创建一个SQL语句来连接4个表 1)供应商表将始终匹配供应商#
上每个表的条目 2)其余3个中的每一个将由供应商#& amp; Seq#
3)3的任何组合都可以有数据(或没有)
4)我不想从供应商表中选择,除非我至少得到3个中的一个

供应商

Vendor #      Name   
--------      ----  
   1        Tom Smith   
   2        Bruce Lee   
   3        Seamus O’Leary   
   4        Jonathan Stewart   
   5        Benjamin Franklin

选择月份范围

Vendor #   Seq #   MonthFrom   MonthTo
--------   -----   ---------   -------
    1        1         3          6
    1        2         7          9
    3        2         5          6 

选择周

Vendor #    Seq #     Week #
--------    -----     ------
    1         1         3
    3         1         4
    4         1         1

选择日期

Vendor #    Seq #    Day #
   1          1        15
   1          2        25
   2          1        12
   4          1        05
   5          1        19

所需表格(已加入)

Vendor#   Name          Seq#   MonthFrom   MonthTo   Week#   Day#
   1    Tom Smith         1        3          6        3      15
   1    Tom Smith         2        7          9      NULL     25
   2    Bruce Lee         1       NULL       NULL    NULL     12
   3    Seamus O’Leary    1       NULL       NULL      4     NULL
   3    Seamus O’Leary    2        5          6      NULL    NULL
   4    Jonathan Stewart  1       NULL       NULL      1      05
   5    Benjamin Franklin 1       NULL       NULL    NULL     19

诀窍是3中的任何一个(不包括'供应商')可以或不可以拥有数据,我只想要返回一行,如果有来自3个中的一个或多个的东西。

任何建议?

5 个答案:

答案 0 :(得分:1)

要在Vendor和Seq上加入,我们首先需要拥有所有可能的组合。然后我们可以根据这些组合过滤表格。我在SQL服务器上运行了以下命令:

<强>设置

.n2-font-d7df9860b9d2e958d0b5b8dadd6b6bd5-paragraph{
    font-family:'HelveticaNeue', 'Helvetica Neue', serif !important;
}

<强>查询

declare @Vendors table(id int, name varchar(20));
declare @MonthRangeSelected table (vendor int, seq int null, monthFrom int null, monthTo int null);
declare @WeekSelected table (vendor int, seq int null, week int null);
declare @DaySelected table (vendor int, seq int null, day int null);

insert into @Vendors
select 1, 'Tom Smith'
union all
select 2, 'Bruce Lee'
union all
select 3, 'Seamus O’Leary'
union all
select 4, 'Jonathan Stewart'
union all
select 5, 'Benjamin Franklin';

insert into @MonthRangeSelected
select 1, 1, 3, 6
union all
select 1, 2, 7, 9
union all
select 3, 2, 5, 6;

insert into @WeekSelected
select 1, 1, 3
union all
select 3, 1, 4
union all
select 4, 1, 1;

insert into @DaySelected
select 1, 1, 15
union all
select 1, 2, 25
union all
select 2, 1, 12
union all
select 4, 1, 05
union all
select 5, 1, 19;

这就是结果:

   select v.Id, v.name, combinations.seq, MonthFrom, MonthTo, Week, Day
     from @Vendors v
inner join (select m.vendor, m.seq
              from @MonthRangeSelected m
             union
            select w.vendor, w.seq
              from @WeekSelected w
             union
            select d.vendor, d.seq
              from @DaySelected d) combinations
       on combinations.vendor = v.id
left join @MonthRangeSelected m
       on m.Vendor = combinations.vendor
      and m.seq = combinations.seq
left join @WeekSelected w
       on w.Vendor = combinations.vendor
      and w.seq = combinations.seq
left join @DaySelected d
       on d.Vendor = combinations.vendor
      and d.seq = combinations.seq
    where (MonthFrom is not null
       or MonthTo is not null
       or Week is not null
       or Day is not null)

答案 1 :(得分:1)

这比听起来更复杂。根据结果​​,当表中有多个匹配项时,您不需要笛卡尔积。所以,你需要考虑seqnum。

select v.Vendor, v.name, coalesce(m.seq, w.seq, d.seq) as Seq,
       m.MonthFrom, m.MonthTo, w.Week, d.Day
from Vendors v left join
     SMonthRangeSelected m
     on v.Vendor = m.Vendor full join
     WeekSelected w
     on v.Vendor = w.Vendor and m.seq = w.seq full join
     DaySelected d
     on v.Vendor = d.Vendor and d.seq in (w.seq, m.seq)
where m.Vendor is not null or
      w.Vendor is not null or
      d.Vendor is not null;

使用full join时会发生奇怪的事情,特别是如果您想要进行任何过滤。另一种方法使用union allgroup by

select mwd.Vendor, v.name, mwd.seq,
       max(MonthFrom) as MonthFrom, max(MonthTo) as monthTo,
       max(Week) as week, max(Day) as day
from ((select m.Vendor, m.seq, m.MonthFrom, m.MonthTo, NULL as week, NULL as day
       from month m
      ) union all
      (select w.Vendor, w.seq, NULL as MonthFrom, NULL as MonthTo, w.week, NULL as day
       from week
      ) union all
      (select d.Vendor, d.seq, NULL as MonthFrom, NULL as MonthTo, NULL as week, d.day
       from day d
      )
     ) mwd join
     Vendor v
     on v.vendor = vmwd.vendor
group by mwd.Vendor, v.vname, mwd.seq;

请注意,此版本不需要Vendor表。

答案 2 :(得分:0)

您应该将外部联接留给3个表中的每个表,然后在where子句中包含以下内容:

(MonthFrom is not null or Week# is not null or Day# is not null)

听起来你可以内部加入供应商表

答案 3 :(得分:0)

您需要在三个表上进行完全外连接,以便获得所有供应商和序列号组合。加入这些与供应商,你就完成了:

select vendorno, v.name, x.seqno, x.monthfrom, x.monthto, x.weekno, x.dayno
from vendor v
join
(
  select vendorno, seqno, m.monthfrom, m.monthto, w.weekno, d.dayno
  from monthsel m
  full outer join weeksel w using (vendorno, seqno)
  full outer join daysel d using (vendorno, seqno)
) x using(vendorno)
order by vendorno, x.seqno;

UPDATE:如果没有USING子句,相同的查询会变得非常笨拙且可读性更低:

select v.vendorno, v.name, x.seqno, x.monthfrom, x.monthto, x.weekno, x.dayno
from vendor v
join
(
  select 
    coalesce(m.vendorno, w.vendorno, d.vendorno) as vendorno,
    coalesce(m.seqno, w.seqno, d.seqno) as seqno,
    m.monthfrom, m.monthto, w.weekno, d.dayno
  from monthsel m
  full outer join weeksel w 
      on  (w.vendorno = m.vendorno or w.vendorno is null or m.vendorno is null) 
      and (w.seqno = m.seqno or w.seqno is null or m.seqno is null) 
  full outer join daysel d 
      on  (d.vendorno = m.vendorno or d.vendorno is null or m.vendorno is null) 
      and (d.vendorno = w.vendorno or d.vendorno is null or w.vendorno is null) 
      and (d.seqno = m.seqno or d.seqno is null or m.seqno is null) 
      and (d.seqno = w.seqno or d.seqno is null or w.seqno is null) 
) x on x.vendorno = v.vendorno
order by v.vendorno, x.seqno;

(希望我没有在这里混淆。通过这样的查询很容易复制和粘贴错误。所以如果它不能正常工作,请注意拼写错误。)

答案 4 :(得分:0)

我相信这会做你需要的:

SELECT
    V.[Vendor#],  -- I'll never understand why people insist on using names that require brackets
    V.Name,
    COALESCE(M.[Seq#], W.[Seq#], D.[Seq#]) AS [Seq#],
    M.MonthFrom,
    M.MonthTo,
    W.[Week#],
    D.[Day#]
FROM
    Vendor V
LEFT OUTER JOIN MonthRange M ON M.[Vendor#] = V.[Vendor#]
LEFT OUTER JOIN Week W ON W.[Vendor#] = V.[Vendor#]
LEFT OUTER JOIN Day D ON D.[Vendor#] = V.[Vendor#]
WHERE
    (
        M.[Vendor#] IS NOT NULL OR
        W.[Vendor#] IS NOT NULL OR
        D.[Vendor#] IS NOT NULL
    ) AND
    (M.[Seq#] = W.[Seq#] OR M.[Seq#] IS NULL OR W.[Seq#] IS NULL) AND
    (M.[Seq#] = D.[Seq#] OR M.[Seq#] IS NULL OR D.[Seq#] IS NULL) AND
    (D.[Seq#] = W.[Seq#] OR D.[Seq#] IS NULL OR W.[Seq#] IS NULL)