Sắp xếp theo khoảng cách


9

Nếu tôi có một truy vấn trả lại các quán cà phê gần đó:

SELECT * FROM cafes c WHERE (
   ST_DWithin(
   ST_GeographyFromText(
     'SRID=4326;POINT(' || c.longitude || ' ' || c.latitude || ')'
   ),
   ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)'),
     2000
   )
)

Làm cách nào để chọn khoảng cách và cũng sắp xếp theo khoảng cách?
Có cách nào hiệu quả hơn cách này không:

 SELECT id, 
 ST_Distance(ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)'),
             ST_GeographyFromText(
             'SRID=4326;POINT(' || c.longitude || ' ' || c.latitude || ')')      
             ) as distance 
 FROM cafes c
   WHERE (
   ST_DWithin(
     ST_GeographyFromText(
     'SRID=4326;POINT(' || c.longitude || ' ' || c.latitude || ')'
   ),
    ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)'),
   2000
 )
 ) order by distance

Bạn có thể sử dụng khoảng cách trong WHEREmệnh đề của bạn ? Giống như so sánh nó với một ngưỡng?
dezso

bạn có thể giải thích rõ hơn rằng tôi không chắc là tôi hiểu ý của bạn không? cảm ơn
Gandalf StormCrow 11/03/2016

Tôi đã nghĩ rằng một điểm nằm trong một hình học hoặc cũng không xa hơn một khoảng cách nhất định có thể ít nhiều tương đương trong một số trường hợp.
dezso

Câu trả lời:


11

Đầu tiên , sử dụng

ST_SetSRID(ST_MakePoint(c.longitude, c.latitude),4326)::geography

thay vì

ST_GeographyFromText('SRID=4326;POINT(' || c.longitude || ' ' || c.latitude || ')')

Mỗi tài liệu:

ST_MakePointtrong khi không tuân thủ OGC thường nhanh hơn và chính xác hơn ST_GeomFromTextST_PointFromText. Nó cũng dễ sử dụng hơn nếu bạn có tọa độ thô thay vì WKT.

Tiếp theo , để làm cho truy vấn ngắn hơn và chỉ nhập tham số tìm kiếm một lần (không ảnh hưởng nhiều đến hiệu suất), hãy sử dụng truy vấn con (hoặc CTE):

SELECT id
     , ST_Distance(t.x
                 , ST_SetSRID(ST_MakePoint(c.longitude, c.latitude),4326)::geography) AS dist
FROM   cafes c
    , (SELECT ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)')) AS t(x)
WHERE  ST_DWithin(t.x
                , ST_SetSRID(ST_MakePoint(c.longitude, c.latitude),4326)::geography, 2000)
ORDER  BY dist;

Cuối cùng , bạn cần một chỉ số GiST để thực hiện điều này nhanh chóng cho các bảng lớn. Mỗi tài liệu vềST_DWithin() :

Cuộc gọi chức năng này sẽ tự động bao gồm một so sánh hộp giới hạn sẽ sử dụng bất kỳ chỉ mục nào có sẵn trên hình học.

Bạn có thể làm điều này để làm việc với một chỉ mục chức năng trên biểu thức khi bắt đầu câu trả lời. Nhưng tôi sẽ lưu trữ một geographycột loại để bắt đầu (hãy đặt tên cho nó thegeog) và tạo một chỉ mục GiST đơn giản như:

CREATE INDEX cafes_thegeog_gist ON cafes USING gist(thegeog);

Đến với truy vấn đơn giản hơn và nhanh hơn nhiều này:

SELECT id, ST_Distance(t.x, thegeog) AS distance 
FROM   cafes c
    , (SELECT ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)')) AS t(x)
WHERE  ST_DWithin(t.x, thegeog, 2000)
ORDER  BY distance;

Cập nhật để phù hợp geographyvới geography, như được chỉ ra bởi @ LR1234567 trong bình luận. Thay thế, bạn có thể làm việc với geometry. Tất cả các chức năng được sử dụng ở đây đều hoạt động cho cả hai (ngoại trừ ST_MakePoint, do đó, dàn diễn viên được nối thêm). Có gì khác biệt?

Nếu bạn muốn có n quán cà phê gần nhất thay vì tất cả trong bán kính, hãy xem xét tìm kiếm "hàng xóm gần nhất" . Thường thuận tiện hơn.


2
Bạn không nên kết hợp hình học với địa lý. Bạn nên làm: ST_SetSRID (ST_MakePoint (c.longitude, c.latitude), 4326) :: địa lý - lưu ý rằng ST_MakePoint trả về hình học.
LR1234567

@ LR1234567: Cảm ơn bạn đã chỉ ra điều đó. Tôi cập nhật câu trả lời cho phù hợp.
Erwin Brandstetter

+1 + acc, câu trả lời khá tuyệt vời Tôi hy vọng điều đó cũng sẽ giúp được nhiều người
Gandalf StormCrow

Xin chào Erwin Tôi đã làm mọi thứ như bạn đã viết, tôi đã tạo một cột địa lý (thegeog), tạo chỉ mục và tôi đã đổ tất cả các latlong vào đó. UPDATE cafes SET thegeog = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326);Tôi vẫn cần sử dụng phần mà select ST_GeographyFromText .... AS t(x)bây giờ tôi có cột này được điền? Tôi có thể tận dụng cột đó và hỏi trực tiếp gần đó thay vì chỉ định lat / long không?
Gandalf StormCrow

@GandalfStormCrow: Có, như thể hiện trong truy vấn cuối cùng của tôi.
Erwin Brandstetter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.