优化复杂的SQL表达式

时间:2014-10-05 11:15:03

标签: mysql

这个超出了我的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 = userid或b.catch_held = userid或[对象在某个点的某个半径范围内(如上所述)]

它还需要高效。也就是说,在b.created = userid或b.catch_held = userid的情况下,我不想计算DISTANCE或进行任何边界框计算,我不知道如何重构查询实现这一目标。

请注意,无论create或catct_held如何,无距离相关的条件都需要保留。那是

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

有人可以帮忙吗?

1 个答案:

答案 0 :(得分: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;

如果它不是你想要的,那么它应该足够接近。