Référence à un alias de colonne dans une clause WHERE

166
SELECT logcount, logUserID, maxlogtm
   , DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
FROM statslogsummary
WHERE daysdiff > 120

Je reçois

"nom de colonne non valide daysdiff".

Maxlogtm est un champ datetime. Ce sont les petites choses qui me rendent fou.

user990016
la source
pas sûr pour mysql, mais peut-être que l'alias doit être entouré de graduations `daysdiff`.
Ash Burlaczenko

Réponses:

194
SELECT
   logcount, logUserID, maxlogtm,
   DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
FROM statslogsummary
WHERE ( DATEDIFF(day, maxlogtm, GETDATE() > 120)

Normalement, vous ne pouvez pas faire référence aux alias de champ dans la WHEREclause. (Considérez-le comme l'ensemble des SELECTalias, y compris, est appliqué après la WHEREclause.)

Mais, comme mentionné dans d'autres réponses, vous pouvez forcer SQL à traiter SELECTpour être traité avant la WHEREclause. Cela se fait généralement avec des parenthèses pour forcer l'ordre logique de fonctionnement ou avec une expression de table commune (CTE):

Parenthèse / Sous-sélection:

SELECT
   *
FROM
(
   SELECT
      logcount, logUserID, maxlogtm,
      DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
   FROM statslogsummary   
) as innerTable
WHERE daysdiff > 120

Ou voir la réponse d'Adam pour une version CTE de la même chose.

Jamie F
la source
16
Ce n'est pas possible directement, car chronologiquement, WHERE se produit avant SELECT, qui est toujours la dernière étape de la chaîne d'exécution. REFER - stackoverflow.com/questions/356675/…
david blaine
afaik si l'alias dans la sélection est une sous-requête corrélée, cela fonctionnera alors que la solution CTE ne le fera pas.
Răzvan Flavius ​​Panda
Comme Pascal l'a mentionné dans sa réponse ici stackoverflow.com/a/38822328/282887 , vous pouvez utiliser la clause HAVING qui semble fonctionner plus rapidement que les sous-requêtes.
Bakhtiyor
@Bakhtiyor La HAVINGréponse ne fonctionne pas dans la plupart des environnements SQL, y compris MS-SQL sur lequel porte cette question. (Dans T-SQL, HAVINGnécessite une fonction d'agrégation.)
Jamie F
72

Si vous souhaitez utiliser l'alias dans votre WHEREclause, vous devez l'envelopper dans un sous-select, ou CTE :

WITH LogDateDiff AS
(
   SELECT logcount, logUserID, maxlogtm
      , DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
   FROM statslogsummary
)
SELECT logCount, logUserId, maxlogtm, daysdiff
FROM LogDateDiff
WHERE daysdiff > 120
Adam Wenger
la source
2
Savez-vous comment cela fonctionne en termes d'efficacité? Y a-t-il des frais généraux supplémentaires en utilisant un CTE?
James le
5
Un CTE est juste une plus jolie syntaxe pour une sous-requête, donc les performances seraient similaires à cela. D'après mon expérience, la différence de performances n'a pas été quelque chose qui m'a préoccupé pour des opérations comme celle-ci, mais il devrait être assez simple de la tester dans votre environnement pour voir si votre table / requête spécifique est affectée par cela par rapport à l'appel du formule spécifiquement dans la clause where. Je suppose que vous ne remarquerez aucune différence.
Adam Wenger le
Les CTE sont super sympas jusqu'à ce que vous essayiez d'en utiliser un comme sous-requête. J'ai dû recourir à leur création sous forme de vues pour les imbriquer. Je considère cela comme une grave lacune SQL
symbiont
10

Le moyen le plus efficace de le faire sans répéter votre code est d'utiliser HAVING au lieu de WHERE

SELECT logcount, logUserID, maxlogtm
   , DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
FROM statslogsummary
HAVING daysdiff > 120
Pascal
la source
1
Je pense que l'utilisation HAVINGd'alias n'est pas standard (cela fonctionne sur MySQL, cependant). Plus précisément, je pense que cela ne fonctionne pas avec SQL Server.
tokland
2
SQL Server:[S0001][207] Invalid column name 'daysdiff'
Vadzim
3
SQL Server:[S0001][8121] Column 'day' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.
Vadzim
9

Si vous ne souhaitez pas lister toutes vos colonnes dans CTE, une autre façon de le faire serait d'utiliser outer apply:

select
    s.logcount, s.logUserID, s.maxlogtm,
    a.daysdiff
from statslogsummary as s
    outer apply (select datediff(day, s.maxlogtm, getdate()) as daysdiff) as a
where a.daysdiff > 120
Roman Pekar
la source
6

Que diriez-vous d'utiliser une sous-requête (cela a fonctionné pour moi dans Mysql)?

SELECT * from (SELECT logcount, logUserID, maxlogtm
   , DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
FROM statslogsummary) as 'your_alias'
WHERE daysdiff > 120
Shekhar Joshi
la source
4

HAVING fonctionne dans MySQL selon la documentation:

La clause HAVING a été ajoutée à SQL car le mot clé WHERE n'a pas pu être utilisé avec les fonctions d'agrégation.

roier.rdz
la source
4

Vous pouvez faire référence à l'alias de colonne, mais vous devez le définir en utilisant CROSS/OUTER APPLY:

SELECT s.logcount, s.logUserID, s.maxlogtm, c.daysdiff
FROM statslogsummary s
CROSS APPLY (SELECT DATEDIFF(day, s.maxlogtm, GETDATE()) AS daysdiff) c
WHERE c.daysdiff > 120;

Démo DBFiddle

Avantages:

  • définition unique de l'expression (plus facile à maintenir / pas besoin de copier-coller)
  • pas besoin d'encapsuler la requête entière avec CTE / externalquery
  • possibilité de se référer WHERE/GROUP BY/ORDER BY
  • meilleures performances possibles (exécution unique)
Lukasz Szozda
la source
1
il vaut la peine de mentionner qu'il ne fonctionne que dans SQL Server
Martin Zinovsky
1
@MartinZinovsky Question est tagué avec sql-serveret t-sql:)
Lukasz Szozda
0

Je suis venu ici à la recherche quelque chose de semblable, mais avec un cas où, et a fini en utilisant l'où comme ceci: WHERE (CASE WHEN COLUMN1=COLUMN2 THEN '1' ELSE '0' END) = 0vous pourriez peut - être utiliser DATEDIFFdans le WHEREdirectement. Quelque chose comme:

SELECT logcount, logUserID, maxlogtm
FROM statslogsummary
WHERE (DATEDIFF(day, maxlogtm, GETDATE())) > 120
Scy
la source