問題は、以下で説明するクエリが手続き型ロジックに頼らずに実行できるかどうか、つまり、SQL と CTE とウィンドウ関数だけで処理できるかどうかです。私は SQL Server 2012 を使用していますが、質問はそのエンジンに限定されません。
250,000 行の音楽教師の全国データベースがあるとします。
teacherName, address, city, state, zipcode, geolocation, primaryInstrument
ここで、geolocation
列はgeography::point
最適にテセレートされたインデックスを持つデータ型です。
ユーザーは、自分の場所に最も近い 5 人のギター教師を探しています。ウィンドウ関数を使用したクエリは、任意の距離カットオフ (たとえば 50 マイル) を選択すれば十分に機能します。これにより、250,000 行すべてを選択して距離でランク付けし、最も近い 5 を取得する必要がなくなります。
しかし、たとえば、ユーザーがシタール、ウード、バラライカなど、異なる文化の楽器を選んだ場合、その任意の半径 50 マイルのカットオフが常に 5 人の教師を網羅できるとは限りません。彼女のいる場所から 50 マイル以内に、そのような楽器の教師が 5 人もいないかもしれません。
また、音楽院から 250 人の歌手のリストが送られてきました。このリストは、次年度の入学が許可された学生で、最も近いボイス コーチ 5 人を送りたいと考えています。リストに載っている各人が、キャンパスに到着する前にコーチングを受けることができるようにします。教師のデータベースを 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