ใช้ 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 clause ร้องขอการปรับปรุงข้อ - DISTINCT clause สำหรับฟังก์ชันการรวม

ตัวแปรที่เป็นไปได้อีกอย่างก็คือ

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 สามารถมีค่า Null ได้คุณสามารถใช้first_valueเพื่อพิจารณาว่ามีค่า Null อยู่ในพาร์ติชั่นหรือไม่จากนั้นลบ 1 หากเป็นไปตามที่ Martin Smith แนะนำไว้ในความคิดเห็น

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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.