Предложение Group by и Having, использующее несколько столбцов

#sql-server #tsql #having

#sql-сервер #tsql #наличие

Вопрос:

AND Условие в приведенном ниже Having предложении ведет себя так, как будто оно фильтрует строку, даже если выполняется одно из условий, когда оно должно быть логичным AND поведением.

 with cte as 
( select 'A' as name , 10 as Classes , 11 as Fees union all
 select 'A' as name , 10 as Classes , 10 as Fees union all
  select 'A' as name , 10 as Classes , 10 as Fees union all
   select 'A' as name , 10 as Classes , 10 as Fees union all
    select 'A' as name , 10 as Classes , 10 as Fees union all
     select 'B' as name , 0 as Classes , 10 as Fees union all
      select 'B' as name , 0 as Classes , 10 as Fees union all
       select 'B' as name , 1 as Classes , 10 as Fees union all
        select 'B' as name , -10 as Classes , 10 as Fees union all
         select 'B' as name , 10 as Classes , 11 as Fees 
       )
       -- A has 50 rooms and 51 $ -- so it should not be returned
       -- B has 1 room and 51 $ and should be fetched
        -- why would the AND in the having clause  behave like a row level filter on only the indivigual column 
        -- instead of the combination
        -- The weird thing is the logic behaves as expected when using = but fails when using <>
       select name , sum(Classes) , sUM(Fees)
       from cte 
       group by name 
       having ( SUM(Classes) !=50) AND (sum(Fees) !=51  )
  

Вот странная часть, если вы замените != на =, как показано ниже, тогда все будет работать нормально

 with cte as 
( select 'A' as name , 10 as Classes , 11 as Fees union all
 select 'A' as name , 10 as Classes , 10 as Fees union all
  select 'A' as name , 10 as Classes , 10 as Fees union all
   select 'A' as name , 10 as Classes , 10 as Fees union all
    select 'A' as name , 10 as Classes , 10 as Fees union all
     select 'B' as name , 0 as Classes , 10 as Fees union all
      select 'B' as name , 0 as Classes , 10 as Fees union all
       select 'B' as name , 1 as Classes , 10 as Fees union all
        select 'B' as name , -10 as Classes , 10 as Fees union all
         select 'B' as name , 10 as Classes , 11 as Fees 
       )
       -- A has 50 rooms and 51 $ -- so it should not be returned
       -- B has 1 room and 51 $ and should be fetched
        -- why would the AND in the having clause  behave like a row level filter on only the indivigual column 
        -- instead of the combination
        -- The weird thing is the logic behaves as expected when using = but fails when using <>
       select name , sum(Classes) , sUM(Fees)
       from cte 
       group by name 
       having ( SUM(Classes) =1) AND (sum(Fees) =51  )

  

Я что-то упускаю, когда дело доходит до способа Group by работы? Я знаю, что могу обойти проблему, но я не понимаю, почему она ведет себя таким образом.

Это то, что я получаю

введите описание изображения здесь

Это то, что я должен получить

введите описание изображения здесь

Комментарии:

1. предоставьте пример вывода из запросов и что следует ожидать, пожалуйста

2. пожалуйста, также укажите, что такое ваша БД и какая ошибка у вас возникает? это просто не возвращает нужные данные или у вас не был запущен запрос?

3. MS SQL 2019, ошибок нет, просто не получается ожидаемый результат, код выглядит правильно, поэтому мне просто нужно понять, почему он не работает так, как описано в документах

4. @user5785372 в любом случае having ( SUM(Classes) !=50) AND (sum(Fees) !=51 ) , если последняя часть блокирует sum(Fees) = 51 arn’t it, вы должны использовать or not and

5. ИЛИ будет означать, что если либо классы sum, либо сборы Sum не совпадают, возвращается строка. Мне нужно получить строку, когда оба не совпадают

Ответ №1:

после некоторого обсуждения попробуйте использовать это

  having not(SUM(Classes) =50 AND sum(Fees) =51)
  

и OP, вы должны дважды подумать, когда пишете логическое значение.

Ответ №2:

Похоже, вы не понимаете логическую логику.

без предложения having это ваши результаты

 A   50  51
B   1   51
  

С помощью

  having ( SUM(Classes) !=50) AND (sum(Fees) !=51  )
  

Вы спрашиваете: покажите мне все строки, для которых классы не равны 50, А сборы не 51.

Важно И .

Теперь посмотрите еще раз на результаты без предложения having и спросите себя, почему вы ожидаете, что что-то будет возвращено?

Поскольку обе строки имеют fee= 51, ничего не возвращается, потому что вы запрашиваете строки с fee, отличными от 51.

Это известная ловушка разума из логической логики, когда вы кумулируете NOT (или <> !=) с помощью ANDS . Вообще говоря, вам редко следует это делать. Если у вас есть несколько NOT, вы должны суммировать их с помощью OR , а не AND .