Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

SQL Server ENTRE


Encontrar uma maneira de usar BETWEEN com a tabela como está funcionará, mas terá um desempenho pior em todos os casos:
  • Na melhor das hipóteses, consumirá mais CPU para fazer algum tipo de cálculo nas linhas em vez de trabalhar com elas como datas.
  • Na pior das hipóteses, forçará uma varredura de tabela em todas as linhas da tabela, mas se suas colunas tiverem índices, então, com a consulta correta, uma busca será possível. Isso pode ser uma ENORME diferença de desempenho, porque forçar as restrições em uma cláusula BETWEEN desabilitará o uso do índice.

Sugiro o seguinte se você tiver um índice em suas colunas de data e se preocupar com o desempenho:
DECLARE
   @FromDate date = '20111101',
   @ToDate date = '20120201';

SELECT *
FROM dbo.YourTable T
WHERE
   (
      T.[Year] > Year(@FromDate)
      OR (    
         T.[Year] = Year(@FromDate)
         AND T.[Month] >= Month(@FromDate)
      )
   ) AND (
      T.[Year] < Year(@ToDate)
      OR (
         T.[Year] = Year(@ToDate)
         AND T.[Month] <= Month(@ToDate)
      )
   );

No entanto, é compreensível que você não queira usar essa construção, pois é muito estranha. Então, aqui está uma consulta de compromisso, que pelo menos usa computação numérica e usará menos CPU do que a computação de conversão de data para string (embora não o suficiente para compensar a varredura forçada, que é o problema real de desempenho).
SELECT *
FROM dbo.YourTable T
WHERE
   T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202;

Se você tiver um índice em Year , você pode obter um grande impulso enviando a consulta da seguinte forma, que tem a oportunidade de buscar:
SELECT *
FROM dbo.YourTable T
WHERE
   T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202
   AND T.[Year] BETWEEN 2011 AND 2012; -- allows use of an index on [Year]

Embora isso quebre seu requisito de usar um único BETWEEN expressão, não é muito mais doloroso e funcionará muito bem com o Year índice.

Você também pode alterar sua tabela. Francamente, usar números separados para suas partes de data em vez de uma única coluna com um tipo de dados de data não é bom. A razão pela qual não é bom é por causa do problema exato que você está enfrentando agora - é muito difícil de consultar.

Em alguns cenários de armazenamento de dados em que salvar bytes é muito importante, eu poderia imaginar situações em que você poderia armazenar a data como um número (como 201111 ), mas isso não é recomendado. O melhor A solução é alterar sua tabela para usar datas em vez de dividir o valor numérico do mês e do ano. Simplesmente armazene o primeiro dia do mês, reconhecendo que ele representa o mês inteiro.

Se alterar a maneira como você usa essas colunas não for uma opção, mas você ainda puder alterar sua tabela, poderá adicionar uma coluna computada persistente:
ALTER Table dbo.YourTable
   ADD ActualDate AS (DateAdd(year, [Year] - 1900, DateAdd(month, [Month], '18991201')))
   PERSISTED;

Com isso você pode apenas fazer:
SELECT *
FROM dbo.YourTable
WHERE
   ActualDate BETWEEN '20111101' AND '20120201';

O PERSISTED palavra-chave significa que, embora você ainda obtenha uma varredura, ela não precisará fazer nenhum cálculo em cada linha, pois a expressão é calculada em cada INSERT ou UPDATE e armazenada na linha. Mas você pode obtenha uma busca se você adicionar um índice nesta coluna, o que fará com que ela tenha um desempenho muito bom (embora, em suma, isso ainda não seja tão ideal quanto mudar para usar uma coluna de data real, porque ocupará mais espaço e afetará os INSERTs e ATUALIZAÇÕES):
CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);

Resumo:se você realmente não pode mudar a mesa de forma alguma, então você terá que fazer um acordo de alguma forma. Não será possível obter a sintaxe simples que você deseja que também tenha um bom desempenho, quando suas datas forem armazenadas divididas em colunas separadas.