简体   繁体   中英

Advanced mysql query with multiple joins, sub queries and radius

I have an application that uses php and mysql that allows users to add multiple prices in multiple currencies depending on location.

The current database looks something like this…'

Units can have multiple positions that can have multiple prices...

units
    -id

positions
    -id
    -unit_id
    -lat
    -lng

prices
    -id
    -position_id
    -currency_id
    -price
    -min_price
    -weight
    -cu_meters

currencies
    -id
    -iso
    -name

rates
    -id
    -from_currency_id
    -to_currency_id
    -rate

What I need is to get the cheapest price (converted to euros so I can compare them) grouped by position sorted by the closest positions grouped by unit.

What I do now is first get all the position id's from the price table.

SELECT id, position_id
FROM ( SELECT * FROM prices ORDER BY price, min_price ) AS p
WHERE weight >= ".self::connection()->escape($weight)."
AND cu_meters >= ".self::connection()->escape($cu_meters)."
GROUP BY position_id
ORDER BY price, min_price

Then, get the closest positions with the result from the previous query

SELECT id, unit_id, lat, lng
FROM
    (
        SELECT
            id,
            unit_id,
            lat,
            lng,
            return_position,
            return_lat,
            return_lng,
            ( 6371 * acos( cos( radians(".Position::connection()->escape($lat).") ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(".Position::connection()->escape($lng).") ) + sin( radians(".Position::connection()->escape($lat).") ) * sin( radians( lat ) ) ) ) AS distance
        FROM positions
        ORDER BY distance ASC
    ) AS pos
WHERE id IN(".implode(",", array_keys($position_ids)).")
GROUP BY unit_id
ORDER BY distance ASC
LIMIT 20;

I have to add a query to get the currency conversion rate and convert the price to euros so I really get the cheapest price in the first query whether they are in euros, pounds or dollars.

Is it possible to do all this in one query. What is the most efficient way to do it

Thanks! /Mattias

You could simply run that first query as a sub-select, eg

WHERE id IN (
    SELECT id FROM prices ORDER BY price, min_price ) AS p
    WHERE weight >= ".self::connection()->escape($weight)."
    AND cu_meters >= ".self::connection()->escape($cu_meters)."
    GROUP BY position_id
    ORDER BY price, min_price
)

The only time you'd want to run them as seperate queries is if you'd want to keep those IDs from the sub-query for other purposes. But if you don't need them again elsewhere in the script, then the subselect will be somewhat more efficient, but chopping out at least one SQL parsing sequence.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM