Trouver un moyen d'utiliser BETWEEN avec la table telle quelle fonctionnera, mais les performances seront moindres dans tous les cas :
- Cela consommera au mieux plus de CPU pour effectuer une sorte de calcul sur les lignes au lieu de les utiliser comme des dates.
- Au pire, cela forcera un balayage de table sur chaque ligne de la table, mais si vos colonnes ont des index, alors avec la bonne requête, une recherche est possible. Cela pourrait être une ÉNORME différence de performances, car forcer les contraintes dans une clause BETWEEN désactivera l'utilisation de l'index.
Je suggère plutôt ce qui suit si vous avez un index sur vos colonnes de date et que vous vous souciez des performances :
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)
)
);
Cependant, il est compréhensible que vous ne souhaitiez pas utiliser une telle construction car elle est très gênante. Voici donc une requête de compromis, qui utilise au moins le calcul numérique et utilisera moins de CPU que le calcul de conversion date-chaîne (mais pas assez pour compenser l'analyse forcée qui est le vrai problème de performances).
SELECT *
FROM dbo.YourTable T
WHERE
T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202;
Si vous avez un index sur Year
, vous pouvez obtenir un gros coup de pouce en soumettant la requête comme suit, qui a la possibilité de rechercher :
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]
Bien que cela ne respecte pas votre exigence d'utiliser un seul BETWEEN
expression, ce n'est pas trop pénible et ça marchera très bien avec l'Year
indice.
Vous pouvez également changer de table. Franchement, utiliser des nombres séparés pour vos parties de date au lieu d'une seule colonne avec un type de données de date n'est pas bon. La raison pour laquelle ce n'est pas bon est à cause du problème exact auquel vous êtes confronté en ce moment :il est très difficile d'interroger.
Dans certains scénarios d'entreposage de données où la sauvegarde des octets est très importante, je pourrais imaginer des situations où vous pourriez stocker la date sous forme de nombre (comme 201111
) mais ce n'est pas recommandé. Le meilleur La solution consiste à modifier votre table pour utiliser des dates au lieu de diviser la valeur numérique du mois et de l'année. Stockez simplement le premier jour du mois, sachant qu'il représente le mois entier.
Si la modification de la façon dont vous utilisez ces colonnes n'est pas une option, mais que vous pouvez toujours modifier votre table, vous pouvez ajouter une colonne calculée persistante :
ALTER Table dbo.YourTable
ADD ActualDate AS (DateAdd(year, [Year] - 1900, DateAdd(month, [Month], '18991201')))
PERSISTED;
Avec cela, vous pouvez simplement faire :
SELECT *
FROM dbo.YourTable
WHERE
ActualDate BETWEEN '20111101' AND '20120201';
Le PERSISTED
mot-clé signifie que même si vous obtiendrez toujours une analyse, il n'aura pas à faire de calcul sur chaque ligne puisque l'expression est calculée sur chaque INSERT ou UPDATE et stockée dans la ligne. Mais vous pouvez obtenir une recherche si vous ajoutez un index sur cette colonne, ce qui la rendra très performante (bien que dans l'ensemble, ce ne soit toujours pas aussi idéal que de changer pour utiliser une colonne de date réelle, car cela prendra plus d'espace et affectera les INSERT et mises à jour):
CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);
Résumé :si vous ne pouvez vraiment pas changer la table de quelque manière que ce soit, vous devrez alors faire un compromis d'une manière ou d'une autre. Il ne sera pas possible d'obtenir la syntaxe simple que vous voulez qui fonctionnera également bien, lorsque vos dates sont stockées dans des colonnes séparées.