2012-11-27 68 views
1

我有一個查詢,我用它來查找按位置排序的結果。結果也必須考慮增值稅,所以這也在查詢中。不幸的是,查詢可能需要4+秒才能運行。任何人都可以發現任何顯而易見的明顯問題,或者提出任何我可以改進的問題嗎?基於緩慢位置的搜索結果查詢

只是爲了澄清什麼在查詢中發生的事情:

  • 的距離計算是利用歐氏距離拉/長
  • 的incvat字段用於顯示時的價格是含稅
  • 在WHEN/THEN語句用來放0價格最底部

查詢:

SELECT * , ROUND(SQRT(POW((69.1 * (company_branch_lat - 52.4862)) , 2) + POW((53 * (company_branch_lng - - 1.8905)) , 2)) , 1) AS distance, 
    hire_car_day + (hire_car_day * 0.2 * ! hire_car_incvat) AS hire_car_day_incvat, 
    hire_car_addday + (hire_car_addday * 0.2 * ! hire_car_incvat) AS hire_car_addday_incvat, 
    hire_car_week + (hire_car_week * 0.2 * ! hire_car_incvat) AS hire_car_week_incvat, 
    hire_car_weekend + (hire_car_weekend * 0.2 * ! hire_car_incvat) AS hire_car_weekend_incvat 
FROM hire_car 
LEFT JOIN company_branch ON company_branch_id = hire_car_branchid 
LEFT JOIN hire_cartypelink ON hire_cartypelink_carhireid = hire_car_id 
LEFT JOIN users ON company_branch_userid = user_id 
WHERE 1 
GROUP BY hire_car_id 
HAVING distance <=30 
ORDER BY CASE hire_car_day_incvat 
WHEN 0 
THEN 40000 
ELSE hire_car_day_incvat 
END , distance ASC 
LIMIT 0 , 30 
+0

可能查詢正在進行完整的表掃描,以計算所有行使用距離<= 30的過濾器的距離。如果你有太多的行,這可能需要時間。在選擇前添加EXPLAIN來檢查您的查詢,您將看到瓶頸。 –

+2

不要使用'SQRT()',將初始距離平方並改爲使用('HAVING sqDistance <900')。距離計算在球面上不完全正確(您可以在SO上找到公式,但它更昂貴)。添加盒子邊界檢查以消除大多數行,並使用索引(例如'WHERE company_branch_lat> 52 AND company_branch_lat <53')。優化器可能不喜歡'ORDER BY CASE ...',因爲它可能會混淆簡單的索引掃描;如果你擔心'0'的價格,把'CASE'放在'SELECT'中(或者使用兩列)。 '0.2'有什麼用? –

+0

@ Clockwork-Muse感謝關於放棄SQRT「根」的想法。任何想法如何在保持我需要的功能的同時改進'ORDER BY CASE'? 0.2在英國是20%的稅收 – Chris

回答

2

您可以使用MySQL的空間擴展和經度和緯度保存爲一個點的數據類型,並使其成爲空間索引。這樣,您可以沿曲線重新排列座標,並減少尺寸並保留空間信息。您可以使用空間索引作爲邊界框來過濾查詢,然後使用harvesine公式來選擇最佳結果。你的邊界框應該大於大圓的半徑。 Mysql使用帶有一些空間索引的rtree,我的例子是關於z曲線或希爾伯特曲線:https://softwareengineering.stackexchange.com/questions/113256/what-is-the-difference-between-btree-and-rtree-indexing。 然後,您可以將地理座標直接插入點列:http://dev.mysql.com/doc/refman/5.0/en/creating-spatial-values.html。或者,您可以使用幾何數據類型:http://markmaunder.com/2009/10/10/mysql-gis-extensions-quick-start/。然後,您可以使用MBRcontains功能:http://dev.mysql.com/doc/refman/4.1/en/relations-on-geometry-mbr.html或任何其他功能:http://dev.mysql.com/doc/refman/5.5/en/functions-for-testing-spatial-relations-between-geometric-objects.html。因此你需要一個邊界框。 下面是一些例子:

下面是一個簡單的例子與點數據類型:

CREATE SPATIAL INDEX sx_place_location ON place (location) 

    SELECT * FROM mytable 
    WHERE MBRContains 
      (
      LineString 
        (
        Point($x - $radius, $y - $radius), 
        Point($x + $radius, $y + $radius) 
        ) 
      location 
      ) 
    AND Distance(Point($x, $y), location) <= $radius 

我不確定它是否工作,因爲它使用帶邊界框函數的半徑變量。在我看來,MBRwithin更簡單一些,因爲它不需要任何參數:Mysql: Optimizing finding super node in nested set tree

+0

這聽起來很有趣,但我不知道該怎麼做!大聲笑 – Chris

0

您正在使用GROUP BY語句和HAVING一起使用,儘管在查詢中沒有看到任何聚合函數。我建議你重新寫這樣的查詢,看看它是否有什麼差別

SELECT * , ROUND(SQRT(POW((69.1 * (company_branch_lat - 52.4862)) , 2) + POW((53 * (company_branch_lng - - 1.8905)) , 2)) , 1) AS distance, 
hire_car_day + (hire_car_day * 0.2 * ! hire_car_incvat) AS hire_car_day_incvat, 
hire_car_addday + (hire_car_addday * 0.2 * ! hire_car_incvat) AS hire_car_addday_incvat, 
hire_car_week + (hire_car_week * 0.2 * ! hire_car_incvat) AS hire_car_week_incvat, 
hire_car_weekend + (hire_car_weekend * 0.2 * ! hire_car_incvat) AS hire_car_weekend_incvat 
FROM hire_car 
LEFT JOIN company_branch ON company_branch_id = hire_car_branchid 
LEFT JOIN hire_cartypelink ON hire_cartypelink_carhireid = hire_car_id 
LEFT JOIN users ON company_branch_userid = user_id 
WHERE ROUND(SQRT(POW((69.1 * (company_branch_lat - 52.4862)) , 2) + POW((53 * (company_branch_lng - - 1.8905)) , 2)) , 1) <= 30 
ORDER BY CASE hire_car_day_incvat 
WHEN 0 
THEN 40000 
ELSE hire_car_day_incvat 
END , distance ASC 
LIMIT 0 , 30