具有左连接的SQL Server /具有sum / group by

时间:2012-04-02 10:47:52

标签: sql-server group-by outer-join having

表B包含计划值。表M保持实际值。我需要找到表B中的所有行,其中表M中没有实际值(即已连接)行,或者连接行具有不同的总实际值行。我正在尝试外部联接和sum ... group by的组合来实现这一点,但它不起作用,因为表B中的“孤儿”没有被返回。

我的查询是: -

 SELECT B.Id, B.Date, b.Ref,SUM(M.Actual_Volume), SUM(B.Planned_Volume),
 SUM(M.Actual_Value),SUM(B.Planned_Value)
 FROM
 TableB B
 left JOIN TableM M on M.Id = B.Id 
 inner JOIN TableX on TableX.FieldX = B.FieldX 
 WHERE TableX.FieldY = (SELECT T.FieldY from TableX T where T.FieldX = 408344)
 AND TableX.FieldZ = (SELECT T1.FieldZ from TableX T1 where T1.FieldX = 408344)
 group by B.Id, B.Date, B.Ref
 having SUM(M.Actual_Volume) <> SUM(B.Planned_Volume)
 OR SUM(M.Actual_Value) <> SUM(B.Planned_Value)
 order by b.Id

如果我使用=而不是&lt;&gt;比较实际和计划我得到加入的行,但我需要实际不等于计划的行,或者有计划但不是实际的行。

谢谢!

 Table B
 Id planned_vol planned val
 19 2           350
 28 1           100
 53 3           650
 61 1           50

 Table M
 M.Id B.Id actual_vol actual_val
 58   19   2          350
 65   28   1          100
 66   53   1          150

所以查询应该返回,

 B.Id 
 53 (because planned_vol <> actual_vol and planned_val <> actual_val)
 61 (because B.Id 61 is not in table M)

HTH!

2 个答案:

答案 0 :(得分:1)

这是未经测试的,但我认为您需要将有要求转移到左外连接要求中。使用CTE(即您需要使用SQL Server 2005或更高版本才能实现此功能)是一种方法。

您的having子句强制SQL Server将B-M连接视为内连接。可能有一种替代方法不使用CTE来检查所有正确位置的NULL。但我更喜欢分而治之的方法。

WITH
[BAlt] AS
(
    SELECT
        [B].[Id],
        [B].[Date],
        [B].[Ref],
        SUM([B].[Planned_Volume]) AS [Planned_Volume],
        SUM([B].[Planned_Value]) AS [Planned_Value],
    FROM [TableB] AS [B]
        INNER JOIN [TableX] AS [X1] ON [X1].[FieldX] = [B].[FieldX]
            AND [X1].[FieldY] =
            (
                SELECT
                    [X2].[FieldY]
                FROM [TableX] AS [X2]
                WHERE [X2].[FieldX] = 408344
            )
            AND [X1].[FieldZ] =
            (
                SELECT
                    [X3].[FieldZ]
                FROM [TableX] AS [X2]
                WHERE [X3].[FieldX] = 408344
            )
    GROUP BY
        [B].[Id],
        [B].[Date],
        [B].[Ref]
),
[MAlt] AS
(
    SELECT
        [M].[Id],
        SUM([M].[Actual_Volume]) AS [Actual_Volume],
        SUM([M].[Actual_Value]) AS [Actual_Value]
    FROM [M]
    GROUP BY
        [M].[Id]
)
SELECT
    [BAlt].[Id],
    [BAlt].[Date],
    [BAlt].[Ref],
    [BAlt].[Planned_Volume],
    [BAlt].[Planned_Value],
    [MAlt].[Actual_Volume],
    [MAlt].[Actual_Value]
FROM [BAlt]
    LEFT OUTER JOIN [MAlt] ON [MAlt].[Id] = [BAlt].[Id]
        AND
        (
            [MAlt].[Actual_Volume] <> [BAlt].[Planned_Volume]
                OR [MAlt].[Actual_Value] <> [BAlt].[Planned_Value]
        )
ORDER BY
    [BAlt].[Id]

答案 1 :(得分:0)

我真的没有看到问题:

    create table b
    (    B_id int
        ,PlannedVolume int
        ,PlannedValue int
    )

    create table M
    (    M_id int
        ,B_id int
        ,ActualVolume int
        ,ActualValue int
    )

    insert b (b_id, PlannedVolume, PlannedValue)
    values  (19, 2, 350),
        (28, 1, 100),
        (53, 3, 650),
        (61, 1, 50)

    insert m (m_id, b_id, ActualVolume, ActualValue)
    values  (58, 19, 2, 350),
        (65, 28, 1, 100),
        (66, 53, 1, 150),
        (67, 53, 1, 100)

   select b.b_id
   from b
     left join
     (  select b_id
            ,sum(ActualVolume) as ActualVolume
            ,sum(ActualValue) as ActualValue
        from m 
        group by b_id
     )   m       
     on  m.b_id = b.b_id
   where 
     m.b_id is null
     or
     (m.ActualValue <> b.PlannedValue and m.ActualVolume <> b.PlannedVolume)