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.


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.


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

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

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

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

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.


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)

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.