Використання 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?
Erwin Brandstetter

Відповіді:


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Я повинен бути, я вірю.
norcalli

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 і включаючи count. Тоді у вашому зовнішньому запиті ваш рахунок (виразний) стає звичайним рахунком, а ваш рахунок (*) стає сумою (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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.