2014-10-05 83 views
1

這個超出了我在SQL中的能力。我有一個查詢選擇具有特定半徑的對象的所有對象,完美地工作。它首先創建一個邊界框以獲取所有潛在候選項,然後從該邊界框內計算半徑以選擇結果。優化複雜的SQL表達式

 SELECT * 
    FROM (
    SELECT b.*, pr.postcode, pr.prize, pr.title, pr.collection, pr.redeemed, pr.delivery, pr.archived, bt.category, b.id as objectid, b.updated as changed, 
      p.radius, 
      p.distance_unit 
        * DEGREES(ACOS(COS(RADIANS(p.latpoint)) 
        * COS(RADIANS(b.lat)) 
        * COS(RADIANS(p.longpoint - b.lng)) 
        + SIN(RADIANS(p.latpoint)) 
        * SIN(RADIANS(b.lat)))) 
      AS distance 
     FROM bubbles AS b, bubble_prizes AS pr, bubble_types AS bt 
     JOIN ( 
      SELECT ? AS latpoint, ? AS longpoint, 
        ? AS radius,  ? AS distance_unit 
     ) AS p 
     WHERE pr.bubble = b.id 
     AND b.deleted = 0 
     AND b.type IN ($placeholders) 
     AND b.type = bt.type 
     AND b.updated > $since 
     AND b.lat 
     BETWEEN p.latpoint - (p.radius/p.distance_unit) 
      AND p.latpoint + (p.radius/p.distance_unit) 
     AND b.lng 
     BETWEEN p.longpoint - (p.radius/(p.distance_unit * COS(RADIANS(p.latpoint)))) 
      AND p.longpoint + (p.radius/(p.distance_unit * COS(RADIANS(p.latpoint)))) 

    ) AS d 
    WHERE distance <= radius 
    ORDER BY distance"; 

我的問題是,現在我想修改這個(這個查詢是從這篇文章http://www.movable-type.co.uk/scripts/latlong-db.html派生),因此它選擇的所有對象,其中

b.created =用戶ID或者b .catch_held = userid OR [物體在某點的某個半徑內(如上)]

它也需要高效。也就是說,在b.created = userid或b.catch_held = userid的情況下,我不想計算DISTANCE或執行任何邊界框計算,並且我不確定如何重構查詢來實現此目的。

請注意,無論創建還是catct_held,無距離相關條件都需要保留在原位。那是

 pr.bubble = b.id 
     AND b.deleted = 0 
     AND b.type IN ($placeholders) 
     AND b.type = bt.type 
     AND b.updated > $since 

有人可以幫忙嗎?

回答

2

首先,您應該只使用明確的join語法重寫您的查詢。一個簡單的規則:從不在from子句中使用逗號。

您正在尋找效率,所以基本上有兩種方法。一種是保持查詢原樣並使用union來引入其他行。另一種是修改查詢。爲此,只需在內部查詢和外部查詢中添加條件即可。這些都是簡單的條件,所以它不那麼難。

我對「距離」相關條件的含義有點模糊。以下假定所有的非加盟條件是距離相關:

SELECT * 
FROM (SELECT b.*, pr.postcode, pr.prize, pr.title, pr.collection, pr.redeemed, pr.delivery, 
      pr.archived, bt.category, b.id as objectid, b.updated as changed, 
      p.radius, 
      (p.distance_unit 
         * DEGREES(ACOS(COS(RADIANS(p.latpoint)) 
         * COS(RADIANS(b.lat)) 
         * COS(RADIANS(p.longpoint - b.lng)) 
         + SIN(RADIANS(p.latpoint)) 
         * SIN(RADIANS(b.lat)))) 
      ) AS distance 
     FROM bubbles b JOIN 
      bubble_prizes pr 
      ON pr.bubble = b.id JOIN 
      bubble_types bt 
      ON b.type = bt.type CROSS JOIN 
      (SELECT ? AS latpoint, ? AS longpoint, ? AS radius, ? AS distance_unit 
      ) p 
     WHERE (b.deleted = 0 AND 
      b.type IN ($placeholders) AND 
      b.updated > $since AND 
      b.lat BETWEEN p.latpoint - (p.radius/p.distance_unit) AND p.latpoint + (p.radius/p.distance_unit) AND 
      b.lng BETWEEN p.longpoint - (p.radius/(p.distance_unit * COS(RADIANS(p.latpoint)))) AND p.longpoint + (p.radius/(p.distance_unit * COS(RADIANS(p.latpoint)))) 
      ) or 
      (b.created = userid OR b.catch_held = userid) 
    ) b 
WHERE distance <= radius or b.created = userid OR b.catch_held = userid 
ORDER BY distance; 

如果這不是你想要的東西,那麼它應該是足夠接近。

+0

感謝您輸入Gordon。這與我嘗試過的各種事情有同樣的問題,那就是b似乎不存在,你會得到: 'where子句'中的未知列'b.creator'。 如果我刪除過去距離<=半徑的OR語句,它解析但不起作用。 – mark 2014-10-05 14:04:07

+0

對不起,我的意思是b.created沒有b.creator在上面的錯誤... – mark 2014-10-05 14:11:55

+0

@mark。 。 。外部查詢的別名是'd'而不是'b',因此應該用於外部引用。 – 2014-10-05 14:48:49