Parece um cenário ideal para uma exibição indexada, que permite pagar cálculos e agregações no momento da gravação, em vez do tempo da consulta.
CREATE VIEW dbo.MyIndexedView
WITH SCHEMABINDING
AS
SELECT Enroll_Date, UserID, RawCount = COUNT_BIG(*)
FROM dbo.UserTable
GROUP BY Enroll_Date, UserID;
GO
CREATE UNIQUE CLUSTERED INDEX CIX_miv ON dbo.MyIndexedView(Enroll_Date, UserID);
Isso levará algum tempo para criar e, é claro, exigirá manutenção em todas as operações DML, como um índice na tabela base.
Agora, a consulta nessa visualização seria bastante semelhante - cada linha na visualização agora representa uma combinação de usuário / data distinta, para que o número possa ser calculado por uma única COUNT (*), enquanto o número total de linhas na tabela base é já agregado parcialmente para você, agora você só precisa adicioná-los usando SUM por data:
SELECT Enroll_Date,
[Record #] = SUM(RawCount),
[User #] = COUNT(*)
FROM dbo.MyIndexedView WITH (NOEXPAND)
GROUP BY Enroll_Date;
Adicionado NOEXPAND dica, depois de lembrar isso e isso .
Posso dizer sem dúvida que essa consulta será mais rápida que a atual (mas não em quanto), exceto nos casos raros em que você tenha exatamente um usuário para cada data (nesse caso, a mesma quantidade de dados terá para serem lidas) e as colunas que conhecemos são as únicas colunas no índice da tabela base. Se esse aumento de desempenho no momento da leitura vale o trabalho extra que afetará a parte de gravação da sua carga de trabalho é algo que não podemos dizer a você - você precisará testá-lo para medir o trade-off (nenhum índice é gratuito).
E se você costuma usar as mesmas cláusulas WHERE comuns em relação ao Enroll_Date para intervalos específicos e bem definidos (por exemplo, o trimestre ou ano atual), você pode adicionar índices filtrados correspondentes que reduzam ainda mais a E / S (mas sempre há um troca).
Você também pode considerar colocar um índice em cluster na tabela base. Esse não parece ser um daqueles casos de uso muito raros que se beneficiam de uma pilha.