多个表上的左外连接

时间:2013-10-28 05:30:32

标签: sql sql-server tsql

我有以下sql语句:

    select  
    a.desc
   ,sum(bdd.amount)
   from t_main c 
   left outer join t_direct bds on (bds.repid=c.id) 
   left outer join tm_defination def a on (a.id =bds.sId)
   where c.repId=1000000134
   group by a.desc;

当我运行它时,我得到以下结果:

   desc       amount
   NW         12.00
   SW         10

当我尝试添加另一个左外连接以获取另一组值时:

   select  
    a.desc
   ,sum(bdd.amount)
   ,sum(i.amt)
   from t_main c 
   left outer join t_direct bds on (bds.repid=c.id) 
   left outer join tm_defination def a on (a.id =bdd.sId)
   left outer join t_ind i on (i.id=c.id)
   where c.repId=1000000134
   group by a.desc;

它基本上将金额字段加倍:

         desc    amount   amt
         NW       24.00   234.00
         SE       20.00   234.00

虽然结果应该是:

        desc   amount   amt
        NW      12.00   234.00
        SE      10.00   NULL 

我该如何解决这个问题?

3 个答案:

答案 0 :(得分:6)

由于最有可能出现多个关系,您的新左外连接会强制在结果集中返回几行。删除你的SUM并只查看返回的行,并确切地确定你需要哪些(如果适用,可以将它限制在特定类型的t_ind记录上),然后相应地调整你的查询。

答案 1 :(得分:6)

如果您确实需要接收上述数据,则可以使用子查询来执行所需的计算。在这种情况下,您的代码可能如下所示:

select x.[desc], x.amount, y.amt
from
(
    select
         c.[desc]
       , sum (bdd.amount) as amount
       , c.id
    from t_main c 
    left outer join t_direct bds on (bds.repid=c.id) 
    left outer join tm_defination_def bdd on (bdd.id = bds.sId)
    where c.repId=1000000134
    group by c.id, c.[desc]
) x
left join
(
    select t.id, sum (t.amt) as amt 
    from t_ind t
    inner join t_main c
      on t.id = c.id
    where c.repID = 1000000134
    group by t.id
) y 
 on x.id = y.id

在第一个子选择中,您将收到两个第一列的汇总数据:descamount,根据需要进行分组。 第二个选择将返回第一个集合的每个amt所需的id值。 这些结果之间的左连接将给出所需的结果。由于性能问题,将t_main表添加到第二个选择中。

另一种解决方案如下:

select
     c.[desc]
   , sum (bdd.amount) as amount
   , amt = (select sum (amt) from t_ind where id = c.id)
from #t_main c 
left outer join t_direct bds on (bds.repid=c.id) 
left outer join tm_defination_def bdd on (bdd.id = bds.sId)
where c.repId = 1000000134
group by c.id, c.[desc]

结果将是相同的。基本上,不是使用嵌套选择,amt和的计算是按结果连接的每一行内联执行的。在大型表格的情况下,第二种解决方案的性能将比第一种方式更差。

答案 2 :(得分:1)

左外连接 - 驱动表行计数

如果join子句中有多个匹配项,则左外连接可能返回的行数多于驱动表中的行数。

使用MS SQL-Server:

DECLARE @t1 TABLE ( id INT )
INSERT INTO @t1 VALUES ( 1 ),( 2 ),( 3 ),( 4 ),( 5 );

DECLARE @t2 TABLE ( id INT )
INSERT INTO @t2 VALUES ( 2 ),( 2 ),( 3 ),( 10 ),( 11 ),( 12 );

SELECT * FROM @t1 t1
LEFT OUTER JOIN @t2 t2 ON t2.id = t1.id

这给出了:

1   NULL
2   2
2   2
3   3
4   NULL
5   NULL

驱动表(t1)中有5行,但返回6行,因为id 2有多个匹配。

因此,如果使用聚合函数,例如SUM()等,按驱动表列分组,则会产生错误的结果。

要解决此问题,请使用派生表或子查询来计算聚合值,如前所述。

左外连接 - 多个表

如果在多个表上有左外连接,或者有任何连接,则查询会按连接顺序生成一系列派生表。

SELECT * FROM t1
LEFT OUTER JOIN t2 ON t2.col2 = <...>
LEFT OUTER JOIN t3 ON t3.col3 = <...>

这相当于:

SELECT * FROM
(
   SELECT * FROM t1
   LEFT OUTER JOIN t2 ON t2.col2 = <...>
) dt1
LEFT OUTER JOIN t3 ON t3.col3 = <...>

这里,对于两个查询,第一个左外连接的结果被放入一个派生表(dt1),然后将其外部连接到第三个表(t3)。

对于多个表上的左外连接,连接子句中表的顺序非常重要。