我有以下MySQL查询:
SELECT from_unixtime( `lastlogin` ) AS `last`
FROM `players` WHERE `lastlogin` <> 0 AND `last` < '2012-10-01 00:00:00';
Run Code Online (Sandbox Code Playgroud)
我得到以下结果:
last不能在WHERE语句中使用,因为这是一个虚拟列并且不存在."lastlogin"包含UNIX时间戳.
但是有没有一种在where语句中使用虚拟列的正确方法?
谢谢你的帮助.
长话短说
所选答案中的查询“应该有效”;对于简单的集合,性能很好。但对于大型场景来说,这将是一场糟糕的表演。
为了获得最佳性能,我们希望使用引用范围条件中的裸列的查询。像这样的东西:
SELECT from_unixtime( `lastlogin` ) AS `last`
FROM `players`
WHERE `lastlogin` > 0
AND `lastlogin` < UNIX_TIMESTAMP('2012-10-01 00:00:00')
Run Code Online (Sandbox Code Playgroud)
原答案
列别名last(在查询中)不能在(查询的)WHERE 子句中引用。这是MySQL 的语法规则。(该规则的原因基本上是,在处理 WHERE 子句中的谓词时,该别名引用的表达式不可用。)
但是该列别名可以在子句的谓词中引用HAVING。该HAVING子句几乎在执行计划结束时得到处理。这是可行的,但从性能的角度来看是有问题的:
SELECT from_unixtime( `lastlogin` ) AS `last`
FROM `players` WHERE `lastlogin` <> 0
HAVING `last` < '2012-10-01 00:00:00';
Run Code Online (Sandbox Code Playgroud)
其执行计划几乎等同于使用内联视图(创建派生表)并针对派生表运行 SELECT(从性能角度来看这也是有问题的):
SELECT d.*
FROM (
SELECT from_unixtime( `lastlogin` ) AS `last`
FROM `players` WHERE `lastlogin` <> 0
) d
WHERE `last` < '2012-10-01 00:00:00';
Run Code Online (Sandbox Code Playgroud)
last此处可以引用列别名,因为内部查询(别名为 d 的内联视图)首先运行,并且结果集存储为 MyISAM 表。然后外部查询针对 MyISAM 表运行。在此查询中, 正在last引用表中的列名,因此可以在 WHERE 子句中引用它。
重要的提示:
这样就回答了您提出的问题。但,
你其实并不想这么做!
对于大型集,此方法将表现出性能问题,因为此查询本质上是创建表的副本,然后在该副本上运行查询。该 from_unixtime 函数将对表中的每一行执行(lastlogin 为 NULL 或等于 0 的行除外)。
从性能的角度来看,最好使用单个查询(无内联视图)并在 WHERE 子句中的裸列上使用谓词。
理想情况下,lastlogin将存储为 DATETIME 或 TIMESTAMP 数据类型。但是,如果该列是整数,那么将谓词中的文字常量转换为整数,然后将其与裸列进行比较会更好(性能方面)。这将允许 MySQL 至少考虑对作为lastlogin前导列的索引进行范围扫描,而不是对from_unixtime每一行执行该函数,以与文字进行比较。
MySQL 提供了一个方便的UNIX_TIMESTAMP()函数来将 DATETIME 转换为整数值(它基本上是该函数的逆FROM_UNIXTIME()函数,但有一些注意事项。
为了获得最佳性能,我们需要以下形式的查询:
SELECT from_unixtime( `lastlogin` ) AS `last`
FROM `players`
WHERE `lastlogin` <> 0
AND `lastlogin` < UNIX_TIMESTAMP('2012-10-01 00:00:00')
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5268 次 |
| 最近记录: |