2

问题是下面描述的查询是否可以在不求助于过程逻辑的情况下完成,即是否可以单独由 SQL 和 CTE 和窗口函数处理?我正在使用 SQL Server 2012,但问题不仅限于该引擎。

假设我们有一个包含 250,000 行的全国音乐教师数据库:

  teacherName, address, city, state, zipcode, geolocation, primaryInstrument

其中geolocation列是geography::point具有最佳细分索引的数据类型。

用户想要五位离他最近的吉他老师。如果我们选择任意距离截断值,例如 50 英里,则使用窗口函数的查询性能足够好,这样我们就不会选择所有 250,000 行,然后按距离对它们进行排序并取最近的 5 行。

但是,如果用户选择来自不同文化的乐器,例如西塔琴或乌德琴或巴拉莱卡琴,那么任意 50 英里半径截断可能并不总是成功地包含 5 位教师;在她所在的 50 英里范围内可能没有五位此类乐器的老师。

另外,现在假设我们有一个查询,音乐学院向我们发送了 250 名歌手的名单,这些歌手是来年被学校录取的学生,他们希望我们向他们发送最接近的五位语音教练名单上的每个人,以便这些学生可以在到达校园之前安排接受一些辅导。我们必须扫描教师数据库 250 次(即扫描地理定位索引),因为这些学生都住在全国不同的地方。

所以,我想知道,对于后一个涉及 250 个学生位置列表的查询,是否有可能编写一个递归查询,其中半径开始很小,例如 10 英里,然后每次迭代增加 10 英里,直到是否已达到 100 英里的最大半径或已找到所需的五 (5) 名教师?能不能只针对还没有匹配到所需5名老师的学生?

我认为它不能单独使用 SQL 来完成,必须通过循环和临时表来完成——但也许那是因为我还没有弄清楚如何单独使用 SQL 来完成。

PS 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
4

1 回答 1

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
于 2013-07-02T07:43:36.537 回答