2

我有以下 MySQL 查询:

SELECT from_unixtime( `lastlogin` ) AS `last`
FROM `players` WHERE `lastlogin` <> 0 AND `last` < '2012-10-01 00:00:00';

我得到以下结果:

1054 - 'where 子句'中的未知列'last'

last不能在 WHERE 语句中使用,因为这当然是一个虚拟列并且不存在。“lastlogin”包含一个 UNIX 时间戳。

但是,有没有在 where 语句中使用虚拟列的正确方法?

谢谢你的帮助。

4

3 回答 3

4

TL;博士

所选答案中的查询“应该有效”;用平凡的集合表现很好。但对于大型集,这将是地狱般的表现。

为了获得最佳性能,我们希望使用在范围条件中引用裸列的查询。像这样的东西:

SELECT from_unixtime( `lastlogin` ) AS `last`
  FROM `players`
 WHERE `lastlogin` > 0
   AND `lastlogin` < UNIX_TIMESTAMP('2012-10-01 00:00:00')

原始答案

列别名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';

其执行计划几乎等同于使用内联视图(创建派生表)并针对派生表运行 SELECT(从性能角度来看这也是有问题的):

SELECT d.*
  FROM (
         SELECT from_unixtime( `lastlogin` ) AS `last`
         FROM `players` WHERE `lastlogin` <> 0 
       ) d
WHERE `last` < '2012-10-01 00:00:00';

列别名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')
于 2013-01-25T23:49:02.723 回答
4

这应该有效:

SELECT a.`last`
FROM   (SELECT From_unixtime(`lastlogin`) AS `last` 
        FROM   `players` 
        WHERE  `lastlogin` <> 0) a 
WHERE  a.`last` < '2012-10-01 00:00:00'; 
于 2013-01-25T23:10:42.750 回答
2

如果在子查询中定义列别名,则可以引用列别名,但这效率不高,因为子查询必须为所有行生成临时结果集,只是为了让它们被外部查询中的条件丢弃。尽早将结果集减少为匹配行会更有效。

如果您的lastlogin列是 unixtime 格式,您可以将其与 WHERE 子句中的常量表达式进行比较:

SELECT FROM_UNIXTIME( `lastlogin` ) AS `last`
FROM `players` WHERE `lastlogin` < UNIX_TIMESTAMP('2012-10-01 00:00:00');

这可以从该列上的索引中受益并减少结果集的大小。

我取出了,lastlogin <> 0因为无论如何你都不应该存储零日期。如果您使用零作为表示“无价值”的特殊值,那么您应该使用 NULL 来代替。


重新评论:是的,正确的函数名称是UNIX_TIMESTAMP()。感谢您的更正,我已经编辑了上面的内容。

于 2013-01-25T23:20:23.873 回答