2014-10-05 2 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 = идентификатор пользователя или б .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 

Может кто-то помочь в этом?

ответ

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

Спасибо за вход Гордон. Это страдает той же проблемой, что и у меня с различными вещами, которые я пробовал, а это значит, что b, похоже, не существует, и вы получаете: Неизвестный столбец «b.creator» в разделе «where». Если я удаляю операторы OR, прошедшие расстояние <= radius, он анализирует, но не работает. – mark

+0

Извините, я имею в виду b.created not b.creator в ошибке выше ... – mark

+0

@mark. , , Алиасом внешнего запроса является 'd' not' b', поэтому его следует использовать для внешней ссылки. –

Смежные вопросы