The following shortened query selects all rows (entrys) inside a given distance (calculated from user e.altloc = 0: location or e.altloc = 1: altlocation ).
I have indexes on e.uid, al.eid, e.country, e.tmstmp and the id's are primary keys.
The problem according to explain all rows are need to process the query, instead of 2 rows which I like with limit 2.
I have read this question, but I'm not able to do the Limit before using a join, because I need to join the location tables before I can do the limit 2 else the return would be wrong.
https://dba.stackexchange.com/questions/52079/does-using-limit-improve-the-performance-and-is-it-noticeable
The query:
SELECT 
        e.id, e.uid, e.title, e.description, l.place, l.placenonce, al.altplace, al.altplacenonce,
        IF(e.altloc=0,
            6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(lat, UNHEX('###'), latnonce) ) ) * cos( radians( AES_DECRYPT(lng, UNHEX('###'), lngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(lat, UNHEX('###'), latnonce))) ) ,
            6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(altlat, UNHEX('###'), altlatnonce) ) ) * cos( radians( AES_DECRYPT(altlng, UNHEX('###'), altlngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(altlat, UNHEX('###'), altlatnonce))) )
        ) AS distance
    FROM 
        entrys e 
    INNER JOIN 
        location l 
        ON l.id = e.uid 
    LEFT JOIN
        altlocation al
        ON al.eid = e.id
    WHERE 
        IF(:border = 0, e.country = :countryid, e.country != 0 )    
    HAVING 
        distance <= 50
    ORDER BY 
        e.tmstmp 
    DESC
    LIMIT 2
A second example with a fixed location:
SELECT 
    s.id, s.image, s.description, s.title,      
    ( 
        6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(l.lat, :key, l.latnonce) ) ) * cos( radians( AES_DECRYPT(l.lng, :key, l.lngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(l.lat, :key, l.latnonce))) ) 
    ) AS distance
FROM 
    sponsors s 
INNER JOIN 
    location l 
    ON l.id = s.id 
WHERE 
    s.comp = 1 OR s.comp = 3 AND s.active = 1
HAVING 
    distance <= 50
ORDER BY
    s.rotate
ASC
LIMIT 2
How to improve this location based querys, if there are million of rows in my database? I need to output only 2 rows of each query.
Create table for the first example:
  CREATE TABLE `entrys` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `uid` int(5) NOT NULL,
 `tmstmp` bigint(11) NOT NULL,
 `approx_lat` mediumint(9) NOT NULL,
 `approx_lng` mediumint(9) NOT NULL,
 `altloc` tinyint(4) NOT NULL,
 `title` varchar(70) COLLATE latin1_general_ci NOT NULL,
 `description` text COLLATE latin1_general_ci NOT NULL,
 `country` tinyint(4) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `uid` (`uid`),
 KEY `tmstmp` (`tmstmp`),
 KEY `country` (`country`),
) ENGINE=MyISAM CHARSET=latin1 COLLATE=latin1_general_ci
CREATE TABLE `location` (
 `id` int(5) NOT NULL,
 `lat` varbinary(50) NOT NULL,
 `latnonce` varbinary(25) NOT NULL,
 `lng` varbinary(50) NOT NULL,
 `lngnonce` varbinary(25) NOT NULL,
 `place` tinyblob NOT NULL,
 `placenonce` tinyblob NOT NULL,
 UNIQUE KEY `id` (`id`),
 KEY `lat` (`lat`),
 KEY `lng` (`lng`)
) 
CREATE TABLE `altlocation` (
 `id` int(5) NOT NULL,
 `eid` int(5) NOT NULL,
 `altlat` varbinary(50) NOT NULL,
 `altlatnonce` varbinary(25) NOT NULL,
 `altlng` varbinary(50) NOT NULL,
 `altlngnonce` varbinary(25) NOT NULL,
 `altplace` tinyblob NOT NULL,
 `altplacenonce` tinyblob NOT NULL,
 UNIQUE KEY `eid` (`eid`),
 KEY `altlat` (`altlat`),
 KEY `altlng` (`altlng`)
)
sidenote: the engine for entrys should probably be innodb, having ~70% read. The location tables both running with innodb.
EDIT question to Willem Renzema for his answer:
Would it be more efficient like that?
SELECT 
        e.id, e.uid, e.title, e.description, l.place, l.placenonce, al.altplace, al.altplacenonce,
        IF(e.altloc=0,
            6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(lat, UNHEX('###'), latnonce) ) ) * cos( radians( AES_DECRYPT(lng, UNHEX('###'), lngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(lat, UNHEX('###'), latnonce))) ) ,
            6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(altlat, UNHEX('###'), altlatnonce) ) ) * cos( radians( AES_DECRYPT(altlng, UNHEX('###'), altlngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(altlat, UNHEX('###'), altlatnonce))) )
        ) AS distance
    FROM 
        (
            SELECT id, uid, title, description
            FROM 
                entrys 
            WHERE 
                    approx_lat > :min_lat
                AND approx_lat < :max_lat
                AND approx_lng > :min_lng
                AND approx_lng < :min_lng   
            ORDER BY 
                e.tmstmp 
            DESC
            LIMIT 2
            
        ) AS e
    INNER JOIN 
        location l 
    ON l.id = uid 
    LEFT JOIN
        altlocation al
    ON al.eid = e.id
    HAVING 
        distance <= 50
If I would add approx_lat and approx_lng to the entry table.
The clue would be moving approx_lat and approx_lng to the entry table, that I could insert altlocation OR location only, so I could get rid off IF inside the query.
Is HAVING distance <= 50still necessary?
 
     
     
    