我有以下MySQL查询:
SELECT from_unixtime( `lastlogin` ) AS `last`
FROM `players` WHERE `lastlogin` <> 0 AND `last` < '2012-10-01 00:00:00';
我得到以下结果:
last
不能在WHERE语句中使用,因为它是虚拟列并且不存在。 “lastlogin”包含UNIX时间戳。
但是有没有一种在where语句中使用虚拟列的正确方法?
感谢您的帮助。
答案 0 :(得分:5)
这应该有效:
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';
答案 1 :(得分: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()。感谢您的纠正,我已经编辑了上述内容。
答案 2 :(得分:2)
在您的查询的WHERE子句中无法引用列别名last
(在您的查询中)。这是一个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或等于零的行除外。)
从性能角度来看,使用单个查询(没有内联视图)并在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')