2012-04-13 32 views
15

Tôi có một mô hình cơ sở dữ liệu Position(lat,lon) nắm giữ latitudeslongitudes.Rails Nested SQL Queries

Tôi có một hành động điều khiển được gọi là show_close_by mà recieves một vị trí trong độ (my_lat, my_lon), dung sai (bằng km) và sẽ trả về danh sách các vị trí trong cơ sở dữ liệu nằm trong phạm vi dung sai.

Đối với điều đó, tôi sử dụng công thức haversine_distance tính toán khoảng cách tính bằng kilomet (trên bề mặt trái đất) giữa hai tọa độ (lat1, lon1, lat2, lon2).

Để thực hiện các truy vấn nhanh hơn, tôi đã viết toàn bộ haversine_distance thức trong truy vấn:

... WHERE 2*6371*asin(sqrt(power(sin((:lat2-latitude)*pi()/(180*2)) ,2) + cos(latitude*pi()/180)*cos(:lat2*pi()/180)*power(sin((:lon2-longitude)*pi()/(180*2)),2))) < tolerance 

Các chi tiết cụ thể của các truy vấn không thành vấn đề. Nghi ngờ của tôi là: có cần thiết để tính toán hàm rất lớn này cho mọi vị trí trong cơ sở dữ liệu không? Tôi có thể lọc ra một số vị trí rõ ràng quá xa với chức năng đơn giản hơn không? Vâng, tôi có thể: Với một truy vấn SQL lồng nhau, tôi có thể truy vấn cơ sở dữ liệu cho các vị trí nằm trong một "hình vuông" lớn (trong không gian lat/lon), và sau đó lọc chúng với hàm lượng giác tốn kém hơn. Một cái gì đó như sau:

SELECT * FROM (SELECT * FROM Positions WHERE lat-latitude < some_reasonable_upper_bound AND lon-longitude < same_upper_bound) WHERE costly_haversine_distance < tolerance 

Cuối cùng, câu hỏi của tôi: làm thế nào tôi có thể thực hiện điều này trong Rails (không phải viết toàn bộ truy vấn)? Positions.where(reasonable_upper_bound).where(costly_but_accurate_restriction) có tạo một truy vấn lồng nhau không? Nếu không, làm thế nào?

Cảm ơn rất nhiều!

+0

Bạn có biết về [Geocoder] (https://github.com/alexreisner/geocoder) không? Bạn cài đặt gem, thêm một dòng trong mô hình Position của bạn, và sau đó bạn có thể thực hiện các công cụ như 'Position.near ([40.71, 100.23], 20)'. Đá quý này khá phổ biến, vì vậy tôi muốn nói họ làm những gì bạn muốn làm khá tốt. (juts để bạn biết, liên kết 'where' điều khoản không làm những gì bạn muốn. Tôi nghĩ rằng nó chỉ ghi đè lên những cái trước đó). – Robin

+0

Vâng, tôi biết về Geocoder và tôi có thể chỉ cần sử dụng nó, nhưng nó có vẻ hơi lớn đối với những gì tôi muốn (tôi chỉ muốn khoảng cách giữa các đối tượng). Ứng dụng tải một đối tượng Geocoder toàn bộ chậm hơn bao nhiêu so với hàm haversine_distance barebones tôi đã lập trình? – gdiazc

+0

@Robin Nó không ghi đè lên trước đó, nhưng nó * không * phụ thêm các điều kiện bằng cách sử dụng 'AND' cho truy vấn hiện tại, mà không hoàn toàn là những gì người hỏi đang tìm kiếm. – nzifnab

Trả lời

25

Dưới đây là cách thực hiện các truy vấn lồng nhau:

LineItem.where(product_id: Product.where(price: 50)) 

Nó làm cho các yêu cầu sau đây:

SELECT "line_items".* FROM "line_items" 
WHERE "line_items"."product_id" IN 
(SELECT "products"."id" FROM "products" WHERE "products"."price" = 50) 

Lưu ý rằng chỉid s sẽ được lấy từ bảng products. Nếu bạn đang cố gắng thực hiện một cách khác để kết nối hai thực thể và ma thuật này không phù hợp, hãy sử dụng Product.select(:some_field).where(...).

+0

Cảm ơn bài đăng nhưng tôi cần chút trợ giúp khác .. Cách viết truy vấn nếu tôi cần chọn tất cả thư được liên kết giữa hai người dùng (giả sử id1 & id2) và thư có sender_id và receive_id. Vì vậy, tôi cần phải chọn "tất cả các thư có ((sender_id = id1 và reciver_id = id2) hoặc (sender_id = id2 và reciver_id = id1))" trong đường ray. Tôi cần phải sắp xếp chúng ra và phân trang mà không thể được thực hiện nếu tôi thực hiện hai truy vấn riêng biệt và sau đó thêm chúng (tôi sẽ có kết quả mong muốn nhưng phân trang trở nên khó khăn). Vì vậy, hãy giúp tôi bằng cách viết một dòng truy vấn cho việc này. Bất kỳ trợ giúp sẽ được đánh giá cao. – zeal

+0

@zeal Hãy thử https: // github.com/activerecord-hackery/squeel cho các truy vấn phức tạp. – jdoe