Question

J'utilise DATEDIFF dans une instruction SQL. Je choisissais, et je dois l'utiliser dans la clause WHERE ainsi. Cette déclaration ne fonctionne pas ...

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE InitialSave <= 10

Il donne le message: nom de colonne non valide "InitialSave"

Mais cette déclaration fonctionne très bien ...

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10

Le programmeur me dit que c'est inefficace (semble que j'appelle la fonction deux fois).

Alors deux questions. Pourquoi ne fonctionne pas la première déclaration? Est-il inefficace de le faire en utilisant la deuxième déclaration?

Était-ce utile?

La solution

Vous ne pouvez pas accéder aux colonnes définies dans l'instruction select dans la déclaration où, parce qu'ils ne sont pas générés qu'après l'où a exécuté.

Vous pouvez le faire cependant

select InitialSave from 
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable) aTable
WHERE InitialSave <= 10

En tant que sidenote - ce déplace essentiellement la DATEDIFF dans la déclaration où en termes de l'endroit où il est d'abord défini. Utilisation des fonctions sur des colonnes dans les états où les causes index de ne pas être utilisés de manière aussi efficace et devrait être évitée si possible, si vous avez à utiliser datediff alors vous devez le faire!

Autres conseils

Remarque: Quand j'ai écrit cette réponse je l'ai dit qu'un index sur l'une des colonnes pourrait créer une requête qui fonctionne mieux que d'autres réponses (et mentionné Dan Fuller). Cependant, je ne pensais pas à 100% correctement. Le fait est, sans colonne calculée ou indexée (matérialisée) vue, une analyse complète de la table va être requis , parce que les deux colonnes de date comparées sont de même table!

Je crois qu'il ya encore de la valeur dans les informations ci-dessous, à savoir 1) la possibilité d'une amélioration des performances dans la bonne situation, comme lorsque la comparaison est entre les colonnes de différentes tables, et 2) la promotion de l'habitude dans les développeurs SQL de suivre le meilleur pratique et remodeler leur pensée dans la bonne direction.

Faire Conditions sargable

La meilleure pratique, je fais référence à un de se déplacer d'une colonne à être seul sur un côté de l'opérateur de comparaison, comme suit:

SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM dbo.MyTable T
WHERE T.EndTime <= T.BegTime + '00:00:10'

Comme je l'ai dit, ce ne sera pas éviter une analyse sur une seule table, cependant, dans une telle situation, il pourrait faire une énorme différence:

SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM
   dbo.BeginTime B
   INNER JOIN dbo.EndTime E
      ON B.BeginTime <= E.EndTime
      AND B.BeginTime + '00:00:10' > E.EndTime

EndTime est dans les deux conditions maintenant seul sur un côté de la comparaison. En supposant que la table a beaucoup moins BeginTime lignes, et la table a un index DateDiff(second, B.BeginTime, E.EndTime) sur la colonne AND B.BeginTime > E.EndTime - '00:00:10', ce interprétera bien, beaucoup mieux que tout ce qui utilise DateDiff. Il est maintenant sargable , ce qui signifie qu'il est valide "argument de recherche" - alors que le moteur scans la table 1, il peut chercher dans la table 3 ms. Une sélection rigoureuse dont la colonne est par lui-même d'un côté de l'opérateur est nécessaire - il peut être utile d'expérimenter en mettant lui-même en 1997 ms faisant une algèbre pour passer à 0

précision de DateDiff

Je tiens également à souligner que ne retourne pas EndTime - BegTime écoulé temps, mais compte le nombre de limites traversèrent. Si un appel à l'aide de secondes pour revenir datetime date, cela pourrait signifier le temps écoulé time, ou cela pourrait signifier DateAdd! Ceci est essentiellement une précision de + - 1 unités de temps. Pour la meilleure précision de + - unité de temps 1/2, vous voulez la requête suivante comparant à 1 month <=>:

SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
FROM MyTable
WHERE EndTime <= BegTime + '00:00:10'

a maintenant une erreur d'arrondi maximum de seulement une seconde au total, pas deux (en effet, une opération plancher ()). Notez que vous ne pouvez soustraire le <=> type de données - pour soustraire un ou une valeur <=> de vous aurait <=> à convertir ou utiliser d'autres <=> méthodes pour obtenir la meilleure précision (un tas de < =>, et éventuellement d'autres <=> indésirable, ou peut-être en utilisant une unité de temps de plus grande précision et en divisant).

Ce principe est particulièrement important lors du comptage des unités plus grandes telles que les heures, jours ou mois. Un de <=> pourrait être 62 <=> jours d'intervalle (pensez 1 Juillet 2013 accompli - 31 août 2013)!

au-delà de ce qui en fait le "travail", vous devez utiliser un indice

utiliser une colonne calculée avec un index ou une vue avec un index, sinon vous balayera de table. quand vous obtenez des lignes assez, vous vous sentirez le PAIN du balayage lent!

colonne et index calculé:

ALTER TABLE MyTable ADD
    ComputedDate  AS DATEDIFF(ss,BegTime, EndTime)
GO
CREATE NONCLUSTERED INDEX IX_MyTable_ComputedDate  ON MyTable 
    (
    ComputedDate
    ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

créer une vue et index:

CREATE VIEW YourNewView
AS
SELECT
    KeyValues
        ,DATEDIFF(ss, BegTime, EndTime) AS InitialSave
    FROM MyTable
GO
CREATE CLUSTERED INDEX IX_YourNewView
    ON YourNewView(InitialSave)
GO

Vous devez utiliser la fonction au lieu de l'alias de colonne - il est le même avec le nombre (*), etc. PITA

.

Comme alternative, vous pouvez utiliser colonnes calculées .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top