Использование DISTINCT в оконной функции с OVER

18

Я пытаюсь перенести запрос из Oracle в SQL Server 2014.

Вот мой запрос, который прекрасно работает в Oracle:

select
count(distinct A) over (partition by B) / count(*) over() as A_B
from MyTable 

Вот ошибка, которую я получил после того, как попытался выполнить этот запрос в SQL Server 2014.

Use of DISTINCT is not allowed with the OVER clause

Кто-нибудь знает в чем проблема? Возможен ли такой запрос в SQL Server? Пожалуйста, порекомендуйте.

Омри
источник
Вам действительно нужна одна строка в результате для каждой строки в MyTable? Или достаточно четких рядов? И вам не нужно учитывать ошибку деления на ноль, если в ней нет строк MyTable?
Эрвин Брандштеттер

Ответы:

12

Кто-нибудь знает в чем проблема? Возможен ли такой запрос в SQL Server?

Нет, в настоящее время это не реализовано. См. Следующий запрос элемента подключения.

Запрос расширения предложения OVER - предложение DISTINCT для агрегатных функций

Другой возможный вариант будет

SELECT M.A,
       M.B,
       T.A_B
FROM   MyTable M
       JOIN (SELECT CAST(COUNT(DISTINCT A) AS NUMERIC(18,8)) / SUM(COUNT(*)) OVER() AS A_B,
                    B
             FROM   MyTable
             GROUP  BY B) T
         ON EXISTS (SELECT M.B INTERSECT SELECT T.B) 

приведение к NUMERICтам, чтобы избежать целочисленного деления. Причина предложения объединения объясняется здесь .

Его можно заменить на, ON M.B = T.B OR (M.B IS NULL AND T.B IS NULL)если предпочтительнее (или просто, ON M.B = T.Bесли Bстолбец не обнуляем).

Мартин Смит
источник
14

Это дает отчетливый счет (*) для A, разделенного на B:

dense_rank() over (partition by B order by A) 
+ dense_rank() over (partition by B order by A desc) 
- 1
Бен
источник
3
Интересное решение. Я предполагаю, что у него должен быть отказ от ответственности, что он работает, только когда Aне обнуляется (поскольку я думаю, что это также учитывает нули).
ypercubeᵀᴹ
Должно быть, abs(dense_rank - dense_rank) + 1я верю.
Норкалли
7

Вы можете принять максимальное значение, dense_rank()чтобы получить четное число A, разделенное на B.

Чтобы позаботиться о случае, когда A может иметь нулевые значения, вы можете использовать, first_valueчтобы выяснить, присутствует ли нулевое значение в разделе или нет, а затем вычесть 1, если это соответствует предложению Мартина Смита в комментарии.

select (max(T.DenseRankA) over(partition by T.B) - 
          cast(iif(T.FirstA is null, 1, 0) as numeric(18, 8))) / T.TotalCount as A_B
from (
     select dense_rank() over(partition by T.B order by T.A) DenseRankA,
            first_value(T.A) over(partition by T.B order by T.A) as FirstA,
            count(*) over() as TotalCount,
            T.A,
            T.B
     from MyTable as T
     ) as T
Микаэль Эрикссон
источник
5

Попробуйте выполнить подзапрос, сгруппировав по A, B и включив количество. Затем в вашем внешнем запросе ваш счет (отличный) становится обычным, а ваш (*) становится суммой (cnt).

select
count(A) over (partition by B) * 1.0 / 
    sum(cnt) over() as A_B
from
(select A, B, count(*) as cnt
 from MyTable
 group by A, B) as partial;
Роб Фарли
источник