问题是下面描述的查询是否可以在不依赖于过程逻辑的情况下完成,也就是说,它是否可以由SQL和CTE以及单独的窗口函数来处理?我正在使用SQL Server 2012,但问题不仅限于该引擎。
假设我们有一个拥有250,000行音乐教师的国家数据库:
teacherName, address, city, state, zipcode, geolocation, primaryInstrument
其中geolocation
列是具有最佳细分索引的geography::point
数据类型。
用户希望五位最近的吉他老师到他的位置。使用窗口函数的查询执行得足够好如果我们选择一些任意距离截止,比如说50英里,那么我们就不会选择所有250,000行,然后按距离对它们进行排序并取最接近的5。 / p> 但是,如果用户选择来自不同文化的乐器,例如西塔琴或乌德或巴拉莱卡,那么任意50英里半径的截止可能并不总能成功地包含5位教师;在她所在地50英里范围内可能没有五名这类仪器的教师。
此外,现在想象一下,我们有一个查询,其中音乐学院向我们发送了250名歌手的名单,这些歌手是未来一年被学校录取的学生,他们希望我们向他们发送五个最接近的学生列表上每个人的语音教练,以便这些学生可以安排在他们到达校园之前接受一些指导。我们必须扫描教师数据库250次(即扫描地理位置索引),因为这些学生都住在全国各地。
所以,我想知道,对于涉及250个学生位置列表的后一个查询,是否有可能编写一个递归查询,其中半径开始很小,比如10英里,然后每个增加10英里迭代,直到达到最大半径100英里或找到所需的五(5)名教师?是否只能为那些尚未与所需的5位教师配对的学生进行?
我认为单靠SQL无法完成,必须使用循环和临时表来完成 - 但也许这是因为我还没有弄清楚如何单独使用SQL。
P.S。 primaryInstrument列也可以减少按距离排列的集合的大小,但为了这个问题,请忘记这一点。
编辑:这是一个示例查询。 SINGER(已提交)数据集包含一个具有任意半径的列,以将地理结果限制为较小的子集,但如上所述,该半径可以定义一个圆(其中心点是学生的地理位置),可能不包含所需的数字老师。有时,提供的数据集包含数千个地址,而不仅仅是几百个。
select TEACHERSRANKEDBYDISTANCE.* from
(
select STUDENTSANDTEACHERSINRADIUS.*,
rowpos = row_number()
over(partition by
STUDENTSANDTEACHERSINRADIUS.zipcode+STUDENTSANDTEACHERSINRADIUS.streetaddress
order by DistanceInMiles)
from
(
select
SINGER.name,
SINGER.streetaddress,
SINGER.city,
SINGER.state,
SINGER.zipcode,
TEACHERS.name as TEACHERname,
TEACHERS.streetaddress as TEACHERaddress,
TEACHERS.city as TEACHERcity,
TEACHERS.state as TEACHERstate,
TEACHERS.zipcode as TEACHERzip,
TEACHERS.teacherid,
geography::Point(SINGER.lat, SINGER.lon, 4326).STDistance(TEACHERS.geolocation)
/ (1.6 * 1000) as DistanceInMiles
from
SINGER left join TEACHERS
on
( TEACHERS.geolocation).STDistance( geography::Point(SINGER.lat, SINGER.lon, 4326))
< (SINGER.radius * (1.6 * 1000 ))
and TEACHERS.primaryInstrument='voice'
) as STUDENTSANDTEACHERSINRADIUS
) as TEACHERSRANKEDBYDISTANCE
where rowpos < 6 -- closest 5 is an abitrary requirement given to us
答案 0 :(得分:1)
我想如果你只需要得到最近的5位教师而不考虑半径,你可以写下这样的东西。学生将在此查询中复制5次,我不知道你想得到什么。
select
S.name,
S.streetaddress,
S.city,
S.state,
S.zipcode,
T.name as TEACHERname,
T.streetaddress as TEACHERaddress,
T.city as TEACHERcity,
T.state as TEACHERstate,
T.zipcode as TEACHERzip,
T.teacherid,
T.geolocation.STDistance(geography::Point(S.lat, S.lon, 4326))
/ (1.6 * 1000) as DistanceInMiles
from SINGER as S
outer apply (
select top 5 TT.*
from TEACHERS as TT
where TT.primaryInstrument='voice'
order by TT.geolocation.STDistance(geography::Point(S.lat, S.lon, 4326)) asc
) as T