I have a MySQL database which contains ranges of numbers. It has the following structure:
range_id, start, end
All columns are INT(10). Furthermore there is a Polygon field called range_poly used for indexing.
I want to flag all the ‘outer’ ranges: all ranges that are not contained within another range in the database. For example:
range_id | start | end
1 | 1 | 2
2 | 4 | 5
3 | 1 | 10
In this case the third record is an ‘outer range’ because it is not contained within another range, but the first and second records are not because they are completely contained within record 3. In order to achieve this I added a column called is_outer which is a simple INT(1) to indicate whether or not the range is contained within another one. I am using the following php script:
$result = mysql_query(mysql_real_escape_string("SELECT range_id, start FROM table;"), $db);
while($row = mysql_fetch_array($result))
{
$result2 = mysql_query(mysql_real_escape_string("SELECT range_id FROM table WHERE MBRCONTAINS( range_poly, POINTFROMWKB( POINT( ". $row['start'] ." , 0 ) ) ) ORDER BY (`end` - `start`) DESC LIMIT 1;"), $db);
$row2 = mysql_fetch_array($result2);
mysql_query(mysql_real_escape_string("UPDATE table SET is_outer = 1 WHERE range_id = ". $row2['range_id'] . ";"), $db);
}
This works fine but I can’t help feeling that there should be an easier way to achieve this. I can’t seem to wrap my brain around a way to do this is pure SET based queries. Alternatively I can code this using a CURSOR but I wonder if the performance will be much better compared to the PHP version. My database has about 3.7M records which explains why performance is quite important.
I have tried to use a subquery but LIMIT is not allowed in a subquery. Alternatively I am thinking of joining the table on itself but I can’t wrap my head around the right conditions.
If I’m understanding your question correctly, you want to return all the parents (outer ranges).
If so, try something like this:
And here is the SQL Fiddle.
If you have duplicated data (7, 33,36) and (8, 33,36), and you want both to return, you can add this to your WHERE clause:
Good luck.