【问题标题】:SQL Server with left join/having sum/group by具有左联接/具有总和/分组依据的 SQL Server
【发布时间】:2012-04-16 00:42:54
【问题描述】:

表 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

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

谢谢!

 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!

【问题讨论】:

  • 能否请您显示一些您想要返回的行的示例数据(出于这两个原因)和至少一个您不想返回的行。
  • 快速解决方法是将 SUM(M.Actual_Volume) is null or SUM(M.Actual_Value) is null 添加到有子句中,但我认为您正在尝试对 n:m 关系的双方求和。这将导致数据重复。您能否发布更多关于您的架构以及 M 和 B 之间关系的信息?
  • 是的,tableB 到 TableM 是 m:m 因为定义表是 TableX
  • 我认为您的 WHERE 子句可以替换为此连接:INNER JOIN TableX T ON TableX.FieldY = T.FieldY AND TableX.FieldZ = T.FieldZ AND T.FieldX = 408344(或 T.FieldX = 408344 可以移动到 WHERE)。

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


【解决方案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]

【讨论】:

  • 我使用的是 SQL Server 2005 9.00.1399.06,所以我会试一试
  • Daniel,尝试这种方式,正确的集合被选择为 SELECT 子句,但由于某种原因,最终的 SELECT 没有正确连接,并为 Actual_Volume 和 Actual_Value 字段返回 NULL。关于如何解决的任何想法?
  • @epx Actual_VolumeActual_ValueB 中存在 ID 但在 M 中不存在的情况下将为空。如果你想要一个默认值,例如零,在最后的 select 子句中使用 ISNULL([MAlt].[Actual_Volume], 0)Actual_Value 的等价物。
【解决方案2】:

我真的不认为有问题:

    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)

【讨论】:

  • 问题是 b 和 m 之间不是 1:1,所以在 m 中你也可以有 67,53,1,100 行。所以现在对于 b_id 53,我们现在有一个未完成的 plan_volume 为 1 和一个未完成的 plan_value 为 400,这就是我尝试 GROUP BY ... HAVING SUM 的原因,但它还没有:-(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-24
  • 2020-06-09
  • 2019-02-18
  • 1970-01-01
  • 2017-08-19
相关资源
最近更新 更多