C'est, en quelque sorte, une extension de la solution de Lennart , mais c'est si moche que je n'ose pas le suggérer comme un montage. Le but ici est d'obtenir les résultats sans tableau dérivé. Il n'y aura peut-être jamais besoin de cela, et combiné avec la laideur de la requête, l'effort dans son ensemble peut sembler un effort inutile. Je voulais quand même le faire comme un exercice et je voudrais maintenant partager mon résultat:
SELECT
Col_A,
Col_B,
DistinctCount = DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
+ DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
- 1
- CASE COUNT(Col_B) OVER (PARTITION BY Col_A)
WHEN COUNT( * ) OVER (PARTITION BY Col_A)
THEN 0
ELSE 1
END
FROM
dbo.MyTable
;
La partie centrale du calcul est la suivante (et je voudrais tout d'abord noter que l'idée n'est pas la mienne, j'ai appris cette astuce ailleurs):
DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
+ DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
- 1
Cette expression peut être utilisée sans aucune modification si les valeurs de Col_B
sont garanties de ne jamais avoir de valeurs nulles. Si la colonne peut avoir des valeurs nulles, cependant, vous devez en tenir compte, et c'est exactement à cela que sert l' CASE
expression. Il compare le nombre de lignes par partition avec le nombre de Col_B
valeurs par partition. Si les nombres diffèrent, cela signifie que certaines lignes ont un zéro dans Col_B
et, par conséquent, le calcul initial ( DENSE_RANK() ... + DENSE_RANK() - 1
) doit être réduit de 1.
Notez que parce que cela fait - 1
partie de la formule principale, j'ai choisi de le laisser comme ça. Cependant, il peut en fait être incorporé dans l' CASE
expression, dans la tentative futile de rendre la solution entière moins moche:
SELECT
Col_A,
Col_B,
DistinctCount = DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
+ DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
- CASE COUNT(Col_B) OVER (PARTITION BY Col_A)
WHEN COUNT( * ) OVER (PARTITION BY Col_A)
THEN 1
ELSE 2
END
FROM
dbo.MyTable
;
Cette démonstration en direct sur db <> fiddle.uk peut être utilisée pour tester les deux variantes de la solution.