我有一个查询,我试图找到邮政编码的特定半径范围内的用户并在特定年龄范围内。指定的范围也存储在表中。基于birthdate字段在查询中计算年龄,并且是我所说的列别名'age'。做我的研究我发现我不能在WHERE子句中使用列别名,所以我不确定如何进行年龄比较。以下是我到目前为止的查询,关于如何比较'age'与'wantminage'和'wantmaxage'的任何指导?
SELECT
zipcodes.zip, zipcodes.city, zipcodes.state,
users.*, YEAR(CURRENT_TIMESTAMP) - YEAR(users.birthdate) - (RIGHT(CURRENT_TIMESTAMP, 5) < RIGHT(users.birthdate, 5)) as age, center.seekingdistance,
(3959 * acos(cos(radians(zipcodes.latitude)) *
cos(radians(center.latitude)) *
cos(radians(zipcodes.longitude ) -
radians(center.longitude)) +
sin(radians(zipcodes.latitude)) *
sin(radians(center.latitude)))) AS distance FROM
( ( SELECT users.username,
users.zip,
users.seekingdistance, users.seekingminage as wantminage, users.seekingmaxage as wantmaxage,
zipcodes.latitude,
zipcodes.longitude FROM
(users JOIN zipcodes ON users.zip = zipcodes.zip)
WHERE (username='tester55')
) center, zipcodes) INNER JOIN users ON zipcodes.zip = users.zip
WHERE (users.username <> 'tester55')
HAVING (distance < center.seekingdistance)
ORDER BY distance
答案 0 :(得分:1)
简单的答案是,您只需重复用于在结果中派生列的表达式。人们通常将此解释为重复工作,但实际上并不是......在执行查询期间,WHERE
在 SELECT
之前进行了逻辑处理,而不是 之后 - 服务器的任务是识别行 ,然后选择并返回col1,col2,col3 ...... so SELECT
正在检索WHERE
用于标识匹配行的值。所以复制表达式很好。
然而......你需要在正确的路径上有一些额外的方向。关系数据库的大部分神奇之处在于索引,它允许查询优化器按照预期的方式执行操作:以最少的工作量识别正确的行。你正在做什么,你做的方式,不会扩展。服务器需要为整个表中的每一行评估这些条件和表达式,这会降低性能随着行数的增加而降低到不可接受的性能。
一个表可以有多个索引,但是当您处理基于索引的优化时,通常必须假设每个查询只能使用一个索引。优化器根据启发式选择一个。多列可以是单个索引的一部分,但只要涉及范围条件,就只能使用一个范围。这种情况的常见例证是打印的电话簿。它是一个两列索引(last_name,first_name)。如果您知道姓氏,您可以快速找到所需的名字,因为这些名称也已排序......但是尝试找到具有特定名字的所有人是不可能的 - 如果您排序名字是无益的不知道姓氏。即使您知道姓氏以S开头(&#39;范围条件,last_name&gt; =&#39; S&#39;以及last_name&lt;&#39; T&#39;)它也不会如果您还希望所有以J开头的名字(第二个范围条件)以及以姓氏开头的名字开头,你可以帮助你。除了扫描每个S寻找所有的J之外别无选择。
这里......你有两个范围,年龄和距离。距离特别凌乱,所以我们首先要覆盖年龄。
出生日期是一个很好的专栏和索引,但我们不想使用函数和日期数学来得出年龄,然后找到年龄段内的所有人。当列是函数的参数时,索引不起作用。相反,我们希望将所需的年龄范围解决为一系列的出生日期。
WHERE users.birthdate >= DATE_SUB(NOW(), INTERVAL @max_age YEAR)
AND users.birthdate <= DATE_SUB(NOW(), INTERVAL @min_age YEAR)
获取两个静态值,此处显示为@max_age
和@min_age
,索引为(birthdate),我们有一个最佳查询,因为DATE_SUB()
在查询开始时解析计划成两个常量值 - 范围条件 - 我们可以通过索引直接转到那些行,甚至不用去检查任何其他行。他们甚至不会被检查,因为优化器知道他们已经超出范围,并且无趣。
至于距离,这个问题要复杂得多,但如果索引正确,可能会给你一个比年龄更大的优势。您需要的是空间索引。 MySQL为此使用了一个R-Tree,它将lat / long组织成一个基于最小边界矩形的二维索引,提供了一个有效的结构来识别一定距离内的两个点,尽管你可能会发现它更容易匹配有人更粗,更大的盒子,然后过滤掉一些异常值...但这可以在SQL中完成,结合现有的逻辑和空间查询功能 - 当优化器找到最佳查询计划时,它不会“关心它并不完美。在姓氏S *名字J *场景中,服务器将使用索引来查找S *,然后扫描匹配的行,丢弃与J *不匹配的所有内容 - 它通常不会将索引视为无用的简单因为它没有提供整体解决方案。它需要它认为可以找到的最佳路径并完成其余的工作。
希望这有帮助。