Ces deux requêtes sont-elles logiquement équivalentes?

10

Ces deux requêtes sont-elles logiquement équivalentes?

DECLARE @DateTime DATETIME = GETDATE()

Requête 1

SELECT *
FROM   MyTable
WHERE  Datediff(DAY, LogInsertTime, @DateTime) > 7   

Requête 2

SELECT *
FROM   MyTable
WHERE  LogInsertTime < @DateTime - 7 

S'ils ne sont pas logiquement équivalents, pouvez-vous me donner l'équivalent logique de la première requête afin que la clause WHERE puisse utiliser efficacement un index (c'est-à-dire éliminer le wrapping de fonction)?

Alf47
la source
Quel LogInsertTimeest ce type ?
dezso
1
Jetez un œil à knowdotnet.com/articles/getdatereturn.html .
a1ex07
LogInsertTime est un DATETIME
Alf47

Réponses:

15

Que les deux requêtes que vous avez publiées soient logiquement équivalentes n'a pas d'importance; vous ne devez utiliser aucun d'eux. Je vais essayer de vous éloigner de deux ou trois choses:

  1. Dans la mesure du possible, essayez d'éviter d'appliquer des fonctions aux colonnes. Il est toujours aussi bon, et surtout meilleur, de conserver ces calculs par rapport aux constantes et non aux colonnes - cela peut détruire SARGability et rendre les index sur ces colonnes inutiles. Dans ce cas, je préfère de loin la requête 2, surtout si elle LogDateTimeest indexée (ou pourrait l'être).
  2. Je n'aime pas le calcul de la date abrégée et je le déconseille. Bien sûr, il est plus rapide de taper, mais essayez cela avec un DATEtype de données et vous obtiendrez une erreur laide. Beaucoup mieux pour le préciser, par exemple:

    WHERE LogInsertTime < DATEADD(DAY, -7, @DateTime);
Aaron Bertrand
la source
Je suis d'accord, mon objectif était de changer la requête 1 en quelque chose de similaire à la requête 2 afin que les index puissent être utilisés efficacement. Merci pour votre aide
Alf47
8

J'utiliserais la requête discutable suivante:

SELECT * FROM MyTable WHERE LogInsertTime < DATEADD(DAY, -7, @DateTime)

La raison: je crois que le résultat de @ DateTime-7 n'est pas documenté. Même s'il s'avère justement être équivalent à DATEADD (DAY, -7, @DateTime), il peut se casser dans une version ultérieure.

AK
la source
Génial c'est exactement ce que je cherchais, merci
Alf47
2
Il est, en fait, documenté et bien défini : - (Subtract): Subtracts two numbers (an arithmetic subtraction operator). Can also subtract a number, in days, from a date.. Néanmoins, je suis d'accord que l'utilisation de fonctions de date explicites rend la requête résultante plus lisible et maintenable que la "magie d'opérateur arithmétique".
Heinzi
6

Ils ne sont pas équivalents. Les enregistrements datant de 7 jours, mais antérieurs à l' heure actuelle , ne seront renvoyés que dans la requête n ° 2:

Lors de la comparaison des jours à l'aide de la DATEADDfonction , elle ne prend pas en compte la partie temps . La fonction renverra 1 lors de la comparaison dimanche et lundi, quelles que soient les heures.

Démo:

DECLARE @MyTable TABLE(pk INT, LogInsertTime DATETIME);

INSERT @MyTable
VALUES (1, DATEADD(HOUR, 1, CAST(DATEADD(DAY, -7, CAST (GETDATE() AS DATE))AS DATETIME))),
(2, DATEADD(HOUR, 23, CAST(DATEADD(DAY, -7, CAST (GETDATE() AS DATE)) AS DATETIME)));

DECLARE @DateTime DATETIME = GETDATE();

SELECT *
FROM @MyTable
WHERE DATEDIFF(DAY, LogInsertTime, @DateTime) > 7;

-- 0 records.

SELECT *
FROM @MyTable
WHERE LogInsertTime < @DateTime - 7;
-- 1 record.

L'équivalent logique de la première requête qui permettra l'utilisation potentielle de l'index est de supprimer la partie temporelle de @DateTimeou de définir l'heure sur 0:00:00:

SELECT *
FROM @MyTable
WHERE LogInsertTime < CAST(@DateTime - 7 AS DATE);

La raison pour laquelle la première requête ne peut pas utiliser un index LogInsertTimeest parce que la colonne est enterrée dans une fonction. La requête n ° 2 compare la colonne à une valeur constante qui permet à l'optimiseur de choisir un index LogInsertTime.

The Scrum Meister
la source