SQL Server с левым соединением / с суммой / сгруппирован по - PullRequest
0 голосов
/ 02 апреля 2012

Таблица B содержит плановые значения. Таблица M содержит фактические значения. Мне нужно найти все строки в таблице B, где либо нет строки фактических значений (т. Е. Соединенных) в таблице M, либо где объединенные строки имеют разные строки общего фактического значения. Я пытаюсь использовать комбинацию внешнего соединения и суммирования ... 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)

НТН!

Ответы [ 2 ]

1 голос
/ 02 апреля 2012

Это не проверено, но я думаю, что вам нужно перенести необходимые требования в требования к левому внешнему соединению.Использование CTE (т.е. вам нужно использовать SQL Server 2005 или более позднюю версию, чтобы это работало) - это один из способов сделать это.

В предложении о том, что SQL Server вынуждает объединение BM как внутреннее соединение.Может быть альтернативный подход, который не использует 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]
0 голосов
/ 02 апреля 2012

Я действительно не вижу проблемы:

    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)
...