Menggunakan fungsi DISTINCT in window dengan OVER

18

Saya mencoba memigrasikan kueri dari Oracle ke SQL Server 2014.

Inilah permintaan saya yang sangat bagus di Oracle:

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

Berikut adalah kesalahan yang saya dapatkan setelah mencoba menjalankan kueri ini di SQL Server 2014.

Use of DISTINCT is not allowed with the OVER clause

Adakah yang tahu apa masalahnya? Apakah mungkin ada semacam query di SQL Server? Mohon saran.

Omri
sumber
Apakah Anda benar-benar membutuhkan satu baris dalam hasil untuk setiap baris di MyTable? Atau cukupkah baris yang berbeda? Dan Anda tidak perlu mempertimbangkan pembagian dengan kesalahan nol jika tidak ada baris MyTable?
Erwin Brandstetter

Jawaban:

12

Adakah yang tahu apa masalahnya? Apakah mungkin ada semacam query di SQL Server?

Tidak, saat ini tidak diterapkan. Lihat permintaan item terhubung berikut.

OVER permintaan peningkatan klausa - DISTINCT klausa untuk fungsi agregat

Varian lain yang mungkin adalah

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) 

para pemain NUMERICada di sana untuk menghindari pembagian bilangan bulat. Alasan untuk klausa gabungan dijelaskan di sini .

Itu dapat diganti dengan ON M.B = T.B OR (M.B IS NULL AND T.B IS NULL)jika disukai (atau hanya ON M.B = T.Bjika Bkolom tidak dapat dibatalkan).

Martin Smith
sumber
14

Ini memberikan hitungan berbeda (*) untuk A yang dipartisi oleh B:

dense_rank() over (partition by B order by A) 
+ dense_rank() over (partition by B order by A desc) 
- 1
Ben
sumber
3
Solusi menarik. Saya kira itu harus memiliki disclaimer yang berfungsi ketika Ahanya non-nullable (karena saya pikir itu juga menghitung nulls).
ypercubeᵀᴹ
Seharusnya abs(dense_rank - dense_rank) + 1saya percaya.
norcalli
7

Anda dapat mengambil nilai maks dense_rank()untuk mendapatkan hitungan berbeda dari A yang dipartisi oleh B.

Untuk menangani kasus di mana A dapat memiliki nilai nol yang dapat Anda gunakan first_valueuntuk mencari tahu apakah nol ada di partisi atau tidak dan kemudian kurangi 1 jika seperti yang disarankan oleh Martin Smith dalam komentar.

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
Mikael Eriksson
sumber
5

Coba lakukan subquery, pengelompokan berdasarkan A, B, dan termasuk hitungan. Kemudian dalam kueri luar Anda, jumlah Anda (berbeda) menjadi jumlah reguler, dan jumlah Anda (*) menjadi jumlah (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;
Rob Farley
sumber