MySQL - Select all geometries inside a rectangle - mysql

I have a table AREAGEOMETRY with the following structure:
+-----------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+----------+------+-----+---------+-------+
| AREAGEOMETRY_ID | int(11) | NO | PRI | NULL | |
| AreaManagerId | int(11) | YES | | NULL | |
| AreaId | text | YES | | NULL | |
| EndDateArea | datetime | YES | | NULL | |
| StartDateArea | datetime | YES | | NULL | |
| AreaGeometryTxt | text | YES | | NULL | |
+-----------------+----------+------+-----+---------+-------+
It contains data from parking zones. Now what I am trying to do is selecting all rows within a bounding box.
A bounding box may be the following:
LatLngBounds{southwest=lat/lng: (52.35631327204287,4.881156384944916), northeast=lat/lng: (52.38006384519922,4.913054890930653)}
I came up with the following query for this:
SELECT * FROM AREAGEOMETRY WHERE ST_OVERLAPS(GeomFromText(AreaGeometryTxt), GeomFromText('LINESTRING(52.35631327204287 4.881156384944916, 52.38006384519922 4.881156384944916, 52.38006384519922 4.913054890930653, 52.35631327204287 4.913054890930653, 52.35631327204287 4.881156384944916)'))
However it seems to return all the rows in the table, I have no idea what is going wrong here.
Maybe someone could point me in the right direction.
Edit:
For example it returns this row:
# AREAGEOMETRY_ID, AreaManagerId, AreaId, EndDateArea, StartDateArea, AreaGeometryTxt
493, 299, 8721, 0000-00-00 00:00:00, 0000-00-00 00:00:00, POLYGON ((6.071624141 51.927465383, 6.071167939 51.927755315, 6.073816653 51.928513734, 6.07434586 51.928376592, 6.072239751 51.927748706, 6.072269225 51.927414931, 6.071624141 51.927465383))
Which it shouldn't.
Edit2:
Some possible viewports and their results:
Viewport 1:
LatLngBounds{southwest=lat/lng: (52.367693923958065,6.981273405253887), northeast=lat/lng: (52.3812037840295,6.99942022562027)}
Query:
SELECT * FROM AREAGEOMETRY WHERE ST_CONTAINS(GeomFromText('LINESTRING(52.367693923958065 6.981273405253887, 52.3812037840295 6.981273405253887, 52.3812037840295 6.99942022562027, 52.367693923958065 6.99942022562027, 52.367693923958065 6.981273405253887)'), GeomFromText(AreaGeometryTxt));
Expected Result: 0 rows returned
Actual Result: 0 rows returned
Viewport 2:
LatLngBounds{southwest=lat/lng: (52.20765248996001,6.881230026483536), northeast=lat/lng: (52.23028692988024,6.911527253687382)}
Query:
SELECT * FROM AREAGEOMETRY WHERE ST_CONTAINS(GeomFromText('LINESTRING(52.20765248996001 6.881230026483536, 52.23028692988024 6.881230026483536, 52.23028692988024 6.911527253687382, 52.20765248996001 6.911527253687382, 52.20765248996001 6.881230026483536)'), GeomFromText(AreaGeometryTxt));
Expected Result: About 25 rows returned
Actual Result: 0 rows returned
Another query same viewport:
SELECT * FROM AREAGEOMETRY WHERE ST_Overlaps(GeomFromText('LINESTRING(52.20765248996001 6.881230026483536, 52.23028692988024 6.881230026483536, 52.23028692988024 6.911527253687382, 52.20765248996001 6.911527253687382, 52.20765248996001 6.881230026483536)'), GeomFromText(AreaGeometryTxt));
Expected Result: About 25 rows returned
Actual Result: 1000 rows returned (limited by MySQL Workbench)
Edit 3:
The following query returns exactly what I want:
SELECT * FROM AREAGEOMETRY WHERE ST_Intersects(GeomFromText('Polygon((6.881230026483536 52.20765248996001, 6.881230026483536 52.23028692988024, 6.911527253687382 52.20765248996001, 6.881230026483536 52.20765248996001))'), GeomFromText(AreaGeometryTxt));
Seems like I mixed up Lat/Lng and had the parameters in the wrong order.

You should use Contains or Intersects, depending on whether you want objects on the border to be included, or whether you want full containment.
However, your main issue, is that you have the geometries the wrong way round, if you look at the Contains documentation you will see it is Contains(g1, g2) returns 1 if g1 contains g2, so you will want to put your bounding box first.
SELECT * FROM AREAGEOMETRY WHERE ST_CONTAINS(ST_GeomFromText('LINESTRING(52.35631327204287 4.881156384944916, 52.38006384519922 4.881156384944916, 52.38006384519922 4.913054890930653, 52.35631327204287 4.913054890930653, 52.35631327204287 4.881156384944916)'), ST_GeomFromText(AreaGeometryTxt));
You might also want to consider storing AreaGeometryTxt as a geometry, rather than text, as this will give you two advantages:
You can then put a spatial index on it, which will lead to much faster query times as table size grows.
You will avoid the overhead of the GeomFromText conversion on each query, which in conjunction with point 1, will prevent doing a full table scan each time.
EDIT: I ran the following query, using the row you say should not be returned, and your original lat/lon bounding box:
select ST_Overlaps(ST_GeomFromText('POLYGON ((6.071624141 51.927465383, 6.071167939 51.927755315, 6.073816653 51.928513734, 6.07434586 51.928376592, 6.072239751 51.927748706, 6.072269225 51.927414931, 6.071624141 51.927465383))'),
ST_GeomFromText('POLYGON ((4.881156384944916 52.35631327204287, 4.881156384944916 52.38006384519922, 4.913054890930653 52.38006384519922, 4.913054890930653 52.35631327204287, 4.881156384944916 52.35631327204287))'));
This query returned 0 (false) for overlaps, intersects and contains as it should.

Related

How to CAST a VARCHAR value to INT if the value is comma seperated

The MySQL database I am working with has a column with comma separated values similar to -
mysql> select * from performance;
+----+------------------+
| id | maximums |
+----+------------------+
| 1 | 10000RPM, 60KM/h |
| 2 | 5000RPM, 30KM/h |
| 3 | 25mph, 3000RPM |
| 4 | 200KM/h, 2000RPM |
+----+------------------+
4 rows in set (0.00 sec)
I am trying to cast the numbers found in to their own INT columns.
mysql> select maximums,
CASE WHEN maximums like "%mph%" THEN CAST(SUBSTRING_INDEX(maximums, 'mph', 1) AS UNSIGNED) END AS mph_int,
CASE WHEN maximums like "%KM/h%" THEN CAST(SUBSTRING_INDEX(maximums, 'KM/h', 1) AS UNSIGNED) END AS kmh_int,
CASE WHEN maximums like "%RPM%" THEN CAST(SUBSTRING_INDEX(maximums, 'RPM', 1) AS UNSIGNED) END AS rpm_int
from performance;
+------------------+---------+---------+---------+
| maximums | mph_int | kmh_int | rpm_int |
+------------------+---------+---------+---------+
| 10000RPM, 60KM/h | NULL | 10000 | 10000 |
| 5000RPM, 30KM/h | NULL | 5000 | 5000 |
| 25mph, 3000RPM | 25 | NULL | 25 |
| 200KM/h, 2000RPM | NULL | 200 | 200 |
+------------------+---------+---------+---------+
4 rows in set, 4 warnings (0.00 sec)
I expect the output to show me the values as INTs in new columns, however am unsure how to achieve this.
Let's give this a whirl, using the good ol'-fashioned blunt instrument approach. I am guessing that you only need this to work once, to convert an old, poorly-designed schema into something more workable. Given that, I have made no effort at elegance or performance.
(If you are not using this to fix your data schema, you should, because the pain you are experiencing now is only the beginning.)
First, we need to split the maximums value into two pieces and process them separately. The first half is:
SUBSTRING_INDEX(`maximum`, ',', 1)
The second half is similar, but there is a stray space:
TRIM(SUBSTRING_INDEX(`maximum`, ',', -1))
From here on, let's just always trim, in case there is variation in the data. Now we need to see if the first section has 'mph' in it, and if so capture the value as you did in your question (this is essentially like your example but operating on only the first part of the maximum value):
IF(TRIM(SUBSTRING_INDEX(`maximum`, ',', 1)) LIKE '%mph', SUBSTRING_INDEX(TRIM(SUBSTRING_INDEX(`maximum`, ',', 1)), 'mph', 1), NULL)
Let's name that chunk of code "mph test on first half". The mph test on the second half is almost identical, just using -1 as the index. Finally, we need to put the non-null value (if either) into the column using COALESCE. Once we create all six variations of the test, we end up with the following:
SELECT
...
COALESCE([mph test on first half], [mph test on second half]) AS mph_int,
COALESCE([kph test on first half], [kph test on second half]) AS kph_int,
COALESCE([rpm test on first half], [rpm test on second half]) AS rpm_int
WHERE
...
Chances are you don't actually need to formally cast the string of digits into an integer; if you are inserting into a table with columns of those types, MySQL will cast the value for you.

MS-Access: Defining Data Types while Compiling Queries

I'm having some problems with the data types of some tables.
For example, I have:
+-------+----------+-------------+----------+--------------+
| Code | Money1 | Money2 | Quantity | Total |
+-------+----------+-------------+----------+--------------+
| 10001 | 100.4334 | 200.3444332 | 100 | 50,432.74642 |
+-------+----------+-------------+----------+--------------+
But I only want 2 digits in each column. I know how to do it from the Table (choosing Number -> Standard -> 2 decimals), but when I compile the query that corresponds to that Table, it breaks again.
I think I have to put CLng(), CInt() and that stuff in the query. Where? In SELECT, FROM, TO?. Is that OK? How "Standard, 2 digits" is called in that nomenclature?
Use Round([Money1], 2) for your columns in the query or - if you are serious about rounding - the RoundMid function here:
GitHub VBA.Round
Then use:
Select
Code,
RoundMid([Money1], 2) As Money_1,
RoundMid([Money2], 2) As Money_2,
Quantity,
RoundMid([Total], 2) As Total2
From
YourTable

NOT IN subquery gives 0 results

i'm not an mysqlologist but i have to deal with the following problem:
given a following table:
+-------+-----------+-------------+------+
| id | articleID | img | main |
+-------+-----------+-------------+------+
| 48350 | 4325 | scr426872xa | 1 |
| 48351 | 4325 | scr426872ih | 2 |
| 48352 | 4325 | scr426872jk | 2 |
| 48353 | 4326 | scr426882vs | 1 |
| 48354 | 4326 | scr426882ss | 2 |
| 48355 | 4326 | scr426882nf | 2 |
+-------+-----------+-------------+------+
each set of images of one distinct articleID should have one image set as main=1 and an unspecified number of images with main value of 2
Due to processing issues it can happen that there is no main=1 set for an image and i need to find the articleID where images with main=2 exist, but not with main=1.
By explaining it backwards it is easier to fomulate what my thinking process for the query is. My idea was to create a result set (subquery) by querying the table for articleID where main is "1". Then use that result to check which distinct articleID of a query where main=2 is not in the results of aforementioned (sub-)query. Basically "substracting" all matching articleID lines.
This should give basically the leftover of all main=2 lines which have no line with the same articleID where main=1
SELECT DISTINCT articleID
FROM img_table WHERE main = 2
AND articleID
NOT IN (SELECT articleID FROM img_table WHERE main = 1 );
I get no result when I know for a fact that there are some. There is surely something I'm doing wrong. I hope my problem is explained in a way that not only me know what I want :)
Given your problem description, it looks like you're actually looking for NOT EXISTS to check for rows that don't have a matching row in the subselect. Note that you do have to add the article id to the where clause in the subselect:
SELECT DISTINCT articleID
FROM img_table t1
WHERE main = 2
AND NOT EXISTS
(SELECT articleID
FROM img_table t2
WHERE main = 1
AND t2.articleID = t1.articleID);
I think your current solution should work too, but maybe you didn't show all the data. For the data you specified, the query would indeed return 0 rows, because all articleIDs have at least one main=1 and a main=2 image.
One important thing to remember: the subquery must not return any NULL value, otherwise NOT IN won't work properly. So if articleID is nullable, make sure your subselect looks like this:
(SELECT articleID FROM img_table WHERE main = 1 and articleID IS NOT NULL)
I didn't find any issue in your query, Please add some data where article id having only main 2. Your query checking both article ID contains main 1,2. ie why you not getting any result.

SUM in access query

I have a Table (T_agents) of agents each has a number of call in a field called NCH I want to create another field call NCHpercent that is the percentage of calls taken by that agent. So the formula is NCH/Total NCH.
So in the query builder I have the following and formula but it dosent work :(
NCHpercent: [NCH.T_agents] / ( SUM(SELECT [NCH.T_agents] FROM [T_agents]) )
What am I doing wrong ?
This would be easier if we could see the table structure as that impacts everything. However I hope I follow this correctly, but I imagine your table (T_agents) as something like:
+-------+-------------+------+
| ID | Agents | NCH |
+-------+-------------+------+
| 1 | agent_1 | 1 |
| 1 | agent_1 | 1 |
| 1 | agent_2 | 2 |
| 1 | agent_3 | 1 |
+-------+-------------+------+
Now assuming that is correct (and NCH is not a unique ID but a total number of calls then we can use a query like this to calculate percentage - note this is not stored in a table, this is just to display the percentage value in a query- I've also added the sum of the total in for the sake of it:
SELECT SUM([T_Agents].NCH) AS total_SUM, [T_agents].Agents, ((SUM(T_agents.NCH))/(select SUM(t_agents.NCH )from T_agents)*100) AS NCHPercent
FROM T_agents
GROUP BY [t_agents].Agents;
In my test the results would be:
2, agent_1, 40
2, agent_2, 40
1, agent_3, 20
However if I got this wrong and the NCH column is in fact
Ok. I just found the answer soing some trial an error. The answer is this code:
NCHperc: [AHT_Tenure].[Calls Handled]/(SELECT Sum(AHT_Tenure.[Calls Handled]) AS [SumaDeCalls Handled]
FROM AHT_Tenure)
By the way thank you guys. And actually the agents name dosent matter for this query since all I wanted was the percentage on each row.

mysql query logic

I have an sql query which shows the delivery details of a vehicle. ( it uses greatest to fetch max value from a range of colums for each vehicle stop)
SELECT deliveryid AS deliverynumber, loadid1 AS loadnumberdate,
haulieraccepted AS haulier,
greatest(drop1arrivedatetime, drop2arrivedatetime, drop3arrivedatetime,
drop4arrivedatetime, drop5arrivedatetime) AS planneddate,
date(greatest(ActualDrop1Arrive, ActualDrop2Arrive, ActualDrop3Arrive,
ActualDrop4Arrive, ActualDrop5Arrive )) AS actualenddate,
mitigation
FROM deliverydetails
WHERE deliveryid=44
the output is
deliverynumber | loadnumberdate | haulier | planneddate | actualenddate | mitigation
44 | 484487 | stols transport | 2011-11-26 15:50:00 | 2011-11-26 | customerdelay
How can I add to the mysql query to compare columns 'planneddate' and 'actualenddate'? if the dates are the same then set the query field to 'ontime' else if actualenddate>planneddate then 'deliverylate'. So ideally I want the following output:
deliverynumber | loadnumberdate | haulier | planneddate | actualenddate | mitigation | Status
44 | 484487 | stols transport | 2011-11-26 15:50:00 | 2011-11-26 | customerdelay | ontime.
Thanks for the assistance.
You can use a CASE statement or IF function. Perhaps something like:
SELECT ...., IF(actualenddate>planneddate,'deliverylate','ontime') AS status FROM ....
use mysql if condition and date conversion function to check and display according to....
You can wrap your original query as a subquery. This will rename the columns. Then, use a case ... then clause to add the column.
Assuming your original query works just fine, it would look like this:
select
*,
case when (... some comparison on 'planneddate' and 'actualenddate' ...)
then <true output>
else <false output> end
from
(<your original query>) as myalias;
The trick is that the columns from the subquery are renamed, allowing you to use their new names (planneddate and actualenddate).