Saya mencoba untuk menulis yang berikut ini untuk mendapatkan total NumUsers yang berbeda, seperti:
NumUsers = COUNT(DISTINCT [UserAccountKey]) OVER (PARTITION BY [Mth])
Studio manajemen sepertinya tidak terlalu senang dengan ini. Kesalahan menghilang saat saya menghapus DISTINCT
kata kunci, tetapi kemudian tidak akan menjadi hitungan yang berbeda.
DISTINCT
tampaknya tidak dimungkinkan dalam fungsi partisi. Bagaimana cara saya mencari hitungan berbeda? Apakah saya menggunakan metode yang lebih tradisional seperti subkueri berkorelasi?
Melihat lebih jauh, mungkin OVER
fungsi - fungsi ini bekerja secara berbeda dengan Oracle sehingga tidak dapat digunakan SQL-Server
untuk menghitung total yang berjalan.
Saya telah menambahkan contoh langsung di sini di SQLfiddle di mana saya mencoba menggunakan fungsi partisi untuk menghitung total yang berjalan.
COUNT
denganORDER BY
bukannyaPARTITION BY
tidak jelas pada tahun 2008. Saya terkejut itu membiarkan Anda memilikinya sama sekali. Berdasarkan dokumentasi , Anda tidak diizinkanORDER BY
untuk fungsi agregat.Jawaban:
Ada solusi yang sangat sederhana dengan menggunakan
dense_rank()
Ini akan memberi Anda apa yang Anda minta: Jumlah UserAccountKey yang berbeda dalam setiap bulan.
sumber
dense_rank()
adalah bahwa ia akan menghitung NULL sedangkanCOUNT(field) OVER
tidak. Saya tidak dapat menerapkannya dalam solusi saya karena ini tetapi saya masih menganggapnya cukup pintar.NULL
nilai-nilai dalamUserAccountKey
, maka Anda perlu menambahkan istilah ini:-MAX(CASE WHEN UserAccountKey IS NULL THEN 1 ELSE 0 END) OVER (PARTITION BY Mth)
. Ide diambil dari jawaban LarsRönnbäck di bawah ini. Pada dasarnya, jikaUserAccountKey
memilikiNULL
nilai, Anda perlu mengurangi ekstra1
dari hasilnya, karenaDENSE_RANK
menghitung NULL.dense_rank
solusi ini ketika fungsi jendela memiliki bingkai. SQL Server tidak mengizinkandense_rank
penggunaan dengan bingkai jendela: stackoverflow.com/questions/63527035/…Necromancing:
Sangat mudah untuk meniru COUNT DISTINCT selama PARTITION BY dengan MAX melalui DENSE_RANK:
;WITH baseTable AS ( SELECT 'RM1' AS RM, 'ADR1' AS ADR UNION ALL SELECT 'RM1' AS RM, 'ADR1' AS ADR UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR UNION ALL SELECT 'RM2' AS RM, 'ADR3' AS ADR UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR UNION ALL SELECT 'RM3' AS RM, 'ADR2' AS ADR ) ,CTE AS ( SELECT RM, ADR, DENSE_RANK() OVER(PARTITION BY RM ORDER BY ADR) AS dr FROM baseTable ) SELECT RM ,ADR ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY ADR) AS cnt1 ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM) AS cnt2 -- Not supported --,COUNT(DISTINCT CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY CTE.ADR) AS cntDist ,MAX(CTE.dr) OVER (PARTITION BY CTE.RM ORDER BY CTE.RM) AS cntDistEmu FROM CTE
Catatan:
Ini mengasumsikan bidang yang dimaksud adalah bidang NON-nullable.
Jika ada satu atau lebih entri NULL di bidang, Anda perlu mengurangi 1.
sumber
Saya menggunakan solusi yang mirip dengan David di atas, tetapi dengan tambahan twist jika beberapa baris harus dikecualikan dari hitungan. Ini mengasumsikan bahwa [UserAccountKey] tidak pernah nol.
-- subtract an extra 1 if null was ranked within the partition, -- which only happens if there were rows where [Include] <> 'Y' dense_rank() over ( partition by [Mth] order by case when [Include] = 'Y' then [UserAccountKey] else null end asc ) + dense_rank() over ( partition by [Mth] order by case when [Include] = 'Y' then [UserAccountKey] else null end desc ) - max(case when [Include] = 'Y' then 0 else 1 end) over (partition by [Mth]) - 1
Sebuah SQL Fiddle dengan contoh tambahan dapat ditemukan di sini.
sumber
[Include]
yang Anda bicarakan dalam jawaban Anda) dengandense_rank()
bekerja jikaUserAccountKey
bisaNULL
. Menambahkan istilah ini ke rumus:-MAX(CASE WHEN UserAccountKey IS NULL THEN 1 ELSE 0 END) OVER (PARTITION BY Mth)
.Saya pikir satu-satunya cara untuk melakukan ini di SQL-Server 2008R2 adalah dengan menggunakan subkueri berkorelasi, atau penerapan luar:
SELECT datekey, COALESCE(RunningTotal, 0) AS RunningTotal, COALESCE(RunningCount, 0) AS RunningCount, COALESCE(RunningDistinctCount, 0) AS RunningDistinctCount FROM document OUTER APPLY ( SELECT SUM(Amount) AS RunningTotal, COUNT(1) AS RunningCount, COUNT(DISTINCT d2.dateKey) AS RunningDistinctCount FROM Document d2 WHERE d2.DateKey <= document.DateKey ) rt;
Ini dapat dilakukan di SQL-Server 2012 menggunakan sintaks yang Anda sarankan:
SELECT datekey, SUM(Amount) OVER(ORDER BY DateKey) AS RunningTotal FROM document
Namun, penggunaan
DISTINCT
masih tidak diperbolehkan, jadi jika DISTINCT diperlukan dan / atau jika peningkatan bukanlah suatu pilihan maka saya pikirOUTER APPLY
adalah pilihan terbaik Andasumber