如何在 WHERE 子句中使用计算值?

Posted

技术标签:

【中文标题】如何在 WHERE 子句中使用计算值?【英文标题】:How can I use a calculated value in the WHERE clause? 【发布时间】:2019-10-25 10:17:06 【问题描述】:

我有以下 SQL:

SELECT members.id, FLOOR(DATEDIFF('2019-10-25', crew_cv.dob) / 365.25) as age
FROM members
JOIN crew_cv ON members.id=crew_cv.user_id 
WHERE members.active=1 AND age>20 AND age<30 
ORDER BY crew_cv.last_name, crew_cv.first_name 
LIMIT 0,30

我在 phpmysql 中得到以下错误:

#1054 - Unknown column 'age' in 'where clause'

如何在WHERE 子句中使用计算出的值age

【问题讨论】:

见:Why should I provide an MCRE for what seems to me to be a very simple SQL query? 【参考方案1】:

在sql中你不能在where子句中使用别名(你必须重复列代码)

  SELECT members.id, FLOOR(DATEDIFF('2019-10-25', crew_cv.dob) / 365.25) as age
  FROM members
  JOIN crew_cv ON members.id=crew_cv.user_id 
  WHERE members.active=1 AND FLOOR(DATEDIFF('2019-10-25', crew_cv.dob) / 365.25)>20 
      AND age<FLOOR(DATEDIFF('2019-10-25', crew_cv.dob) / 365.25) 
  ORDER BY crew_cv.last_name, crew_cv.first_name 
  LIMIT 0,30

但你可以创建一个视图

create view my_view as 
select   members.id, crew_cv.last_name, crew_cv.first_name , FLOOR(DATEDIFF('2019-10-25', crew_cv.dob) / 365.25) as age
 FROM members
  JOIN crew_cv ON members.id=crew_cv.user_id 
  WHERE members.active=1

然后

SELECT id,  age, last_name, first_name
from  my_view 
where  age>20 AND age<30 
      ORDER BY last_name, first_name 
      LIMIT 0,30

或将条件应用到有子句

在 sql 中,子句按特定顺序进行评估 在 select 子句之前评估 where 条件(因此在此阶段不知道列别名) 而是在 select 子句之后评估 have 子句

【讨论】:

【参考方案2】:
FROM clause
ON clause
OUTER clause
WHERE clause
GROUP BY clause
HAVING clause
SELECT clause
DISTINCT clause
ORDER BY clause
TOP clause

这是查询执行的顺序,这意味着在您的查询中(在 where 子句中)在定义(在 select 子句中)之前使用计算。

如果您仍想将所有内容放在一个查询中,这是我建议的查询:

SELECT members.id, temp.age
FROM members, 
     (SELECT user_id, crew_cv.last_name, crew_cv.first_name
             FLOOR(DATEDIFF('2019-10-25', crew_cv.dob) / 365.25) as age
      FROM crew_cv) as temp
WHERE members.id = temp.user_id  
      AND members.active=1
      AND temp.age > 20 AND temp.age < 30
ORDER BY temp.last_name, temp.first_name
LIMIT 0,30

【讨论】:

【参考方案3】:

Maxim 已经回答了问题,但删除了答案。

MySQL 扩展了HAVING 子句,因此即使在没有聚合的查询中也能正常工作。这允许它使用别名过滤查询——这很方便。所以:

SELECT m.id,
       FLOOR(DATEDIFF('2019-10-25', c.dob) / 365.25) as age
FROM members m JOIN
     crew_cv c
     ON m.id = c.user_id 
WHERE m.active = 1 
HAVING age > 20 AND age < 30 
ORDER BY c.last_name, c.first_name 
LIMIT 0, 30;

MySQL 这样做是因为它倾向于实现子查询。这增加了读取和写入数据的开销。在大多数其他数据库中,您只需使用子查询或 CTE 来表达这一点,而不会影响性能。 MySQL 重载 HAVING 作为替代方法。

而且,话虽如此,/ 365.25 是一个近似值。更准确的查询是:

SELECT m.id,
       FLOOR(DATEDIFF('2019-10-25', c.dob) / 365.25) as age
FROM members m JOIN
     crew_cv c
     ON m.id = c.user_id 
WHERE m.active = 1 AND
      c.dob >= DATE('2019-10-25') - INTERVAL 30 YEAR AND
      c.dob <= DATE('2019-10-25') - INTERVAL 21 YEAR
ORDER BY c.last_name, c.first_name 
LIMIT 0, 30;

如果 MySQL 认为合适,它还可以使用(dob) 上的索引。

【讨论】:

以上是关于如何在 WHERE 子句中使用计算值?的主要内容,如果未能解决你的问题,请参考以下文章

Drupal 8 和 PostgreSQL:如何在 WHERE 子句中使用表达式的结果?

如何在 MySQL 的 WHERE 子句中使用计算字段?

如何在光标的 select 语句 where 子句中传递逗号分隔值

如何在 SQL Server where 子句中使用表值函数

如何在子查询的 WHERE 子句中使用来自 UNNEST 的多个值?

ZF2如何在where子句中将值作为表达式传递