Sybase: HAVING работает на строках?

Я столкнулся с следующим SYBASE SQL:

-- Setup first
create table #t (id int, ts int)
go

insert into #t values (1, 2)
insert into #t values (1, 10)
insert into #t values (1, 20)
insert into #t values (1, 30)

insert into #t values (2, 5)
insert into #t values (2, 13) 
insert into #t values (2, 25)
go

declare @time int select @time=11
-- This is the SQL I am asking about
select * from (select * from #t where ts <= @time) t group by id having ts = max(ts)
go

Результатами этого SQL являются

 id          ts          
 ----------- ----------- 
           1          10 
           2           5 

Это выглядит как условие HAVING, применяемое к строкам, а не к группам. Может ли кто-нибудь указать мне место в документации Sybase 15.5, где описан этот случай? Все, что я вижу, это "HAVING работает на группы". Наиболее близким я вижу в документах:

Предложение having может включать столбцы или выражения, которые не входят в список выбора, а не в предложении group by.

(Цитата из здесь).

Однако они не точно объясняют, что происходит, когда вы это делаете.

Ответ 1

Мое понимание: Да, в принципе, HAVING работает на строках. Опуская GROUP BY, он работает со всеми результирующими строками внутри одной "супергруппы", а не по группам внутри групп. Прочтите раздел "Как группировать и иметь запросы с агрегатами" в исходной ссылке Sybase docco: -

Как работает группа и запросы с агрегатами

  • Предложение where исключает строки, которые не соответствуют условиям поиска; его функция остается неизменной для сгруппированных или негрупповых запросов.
  • Предложение group by собирает оставшиеся строки в одну группу для каждого уникального значения в выражении по выражению. Опускание группы с помощью создает отдельную группу для всей таблицы.
  • Совокупные функции, указанные в списке выбора, вычисляют суммарные значения для каждой группы. Для скалярных агрегатов для таблицы есть только одно значение. Векторный агрегат вычисляет значения для отдельных групп.
  • Предложение с исключает группы из результатов, которые не соответствуют условиям поиска. Даже если предложение с проверяет только строки, наличие или отсутствие предложения по может заставить его работать в группах:
    • Когда запрос включает группу, с исключает строки группы результатов. Вот почему с работает в группах.
    • Когда запрос не имеет группы, с исключает строки результатов из таблицы (одной группы). Вот почему с работает в строках (результаты аналогичны результатам where).

Во-вторых, краткое резюме появляется в разделе "Как взаимодействовать, группируя и где clauses" : -

Как взаимодействовать друг с другом, группировать и где

Когда вы включаете в запрос , группу и где, последовательность, в которой каждое предложение влияет на строки, определяет конечные результаты:

  • Предложение where исключает строки, которые не соответствуют условиям поиска.
  • Предложение group by собирает оставшиеся строки в одну группу для каждого уникального значения в выражении по выражению.
  • Совокупные функции, указанные в списке выбора, вычисляют суммарные значения для каждой группы.
  • Предложение с исключает строки из окончательных результатов, которые не соответствуют условиям поиска.

@SQLGuru поясняет это.

Изменить...

В связанной точке я был удивлен поведением не-ANSI-совместимых запросов, в которых используются расширенные столбцы TSQL. Sybase обрабатывает расширенные столбцы (i) после предложения WHERE (ii) путем создания дополнительных объединений к исходным таблицам и (iii) предложение WHERE не используется в соединении. Такие запросы могут возвращать больше строк, чем ожидалось, и предложение HAVING требует дополнительных условий для их фильтрации.

См. примеры b, c и d в разделе "Расширения Transact-SQL для группировки и наличия" на странице вашего исходного кода, связанных docco. Мне было полезно установить базу данных pubs2 из Sybase, чтобы играть вместе с примерами.

Ответ 2

Я не делал Sybase, так как он делил код с MS SQL Server.... 90-е, но моя интерпретация того, что вы делаете, такова:

Сначала список фильтруется в <= 11

id   ts
1    2
1    10
2    5

Все остальное отфильтровано.

Затем вы фильтруете список в строки, где TS = Max (TS) для этой группы.

id   ts
1    10
2    5

10 - Макс (TS) для группы 1, а 5 - Макс (TS) для группы 2. Эти две строки - это те, которые остаются. Какой результат вы ожидали бы иначе?

Ответ 3

Если вы читаете документацию здесь, кажется, что использование Sybase столбцов в предложении having, которое не отображается в group by отличается от MySQL.

Пример, который они приводят, имеет следующее объяснение:

Расширенный столбец Transact-SQL, цена (в списке выбора, но не агрегат, а не в группе по статье), вызывает все квалифицированные строки для отображения в каждой квалифицированной группе, хотя стандартная группа by создает отдельную строку для каждой группы. Группа по-прежнему влияет на векторный агрегат, который вычисляет среднюю цену за группу отображаются в каждой строке каждой группы (они являются теми же значениями, что были вычислены, например, a):

Итак, ts = max(ts) делает это:

select *
from (select t.*,
             max(ts) over (partition by id) as maxts
      from #t
      where ts <= @time
     ) t
where ts = maxts

Подзапрос важен, поскольку предложение where используется для вычисления max(), и все строки будут возвращены.

Я считаю это поведение довольно запутанным и нестандартным. Я бы заменил его более типичными конструкциями. Они примерно одинакового уровня сложности и кажутся более ясными для более широкой аудитории.