PHP MySQL How to calculate distance for between row? - mysql

I have a table like this:
stepID | UserID| Date | Lat | Lng
1 |1 | 2019-10-11 | -7.2905838 | 112.5655568
2 |1 | 2019-10-11 | -7.2349607 | 112.6106177
3 |1 | 2019-10-11 | -7.2345435 | 112.6112432
4 |1 | 2019-10-12 | -7.2529265 | 112.6542999
I need to calculate distance that user has been visited on the same day (for example 2019-10-11). So waht will be show on PHP page is (the KM amount below is an example) :
From step 1 to 2: 2 KM
From step 2 to 3: 3 KM
From step 3 to 4: 3 KM
TOTAL FOR TODAY: 8 KM
I've googling and also search in this stackoverflow's history but didn't found like what I face today. Need your suggestion how to query this.
Thank you before, GBU always.

This is not the best task for MySQL. It would be much better to perform it in any programming language by reading rows from DB 1 by 1.
However if you want to use exactly MySQL then something like that (assuming StepIDs are sequential numbers without gaps):
SELECT UserID, SUM(km) total
FROM (
SELECT t1.UserID,
DEGREES(ACOS(LEAST(1.0, COS(RADIANS(t1.Lat))
* COS(RADIANS(t2.Lat))
* COS(RADIANS(t1.Lon- t2.Lon))
+ SIN(RADIANS(t1.Lat))
* SIN(RADIANS(t2.Lat))))) km
FROM table t1
JOIN table t2 ON t1.UserID = t2.UserID and t1.StepID = t2.StepID - 1
) t
GROUP BY UserID
I've got the formula from: https://stackoverflow.com/a/24372831/2244262

Related

MySQL to Find the first occurrence where the time difference is bigger than 5 minutes?

I have a table:
id | created
1 | 1563220108
2 | 1563220408
3 | 1563220608
4 | 1563220808
5 | 1563220908
I want to find the lowest ID, where the difference between the current row and the other row is more than 5 minutes.
If you look at my sample data, the difs are as follows:
id | created | dif
1 | 1563220108 | null
2 | 1563220408 | 300
3 | 1563220608 | 500
4 | 1563220808 | 700
5 | 1563220908 | 800
5 x 60 = 300. So, in this case, I would like 2 to be returned.
If id = 2, was instead 301 seconds apart, then it would need to return id = 3.
I am struggling to get my brain to figure out how to write a query that looks at row 1, then row X + 1, then row X + 2 etc. It's not directly comparing the NEXT row. It must compare the first row with the next row, then the first row with the 3rd row, then first row with the 4th row and so forth. Is this even possible with mysql?
UPDATE
Version: 10.1.36-MariaDB
UPDATE 2
I have a database that stores points every couple of seconds. I am trying to write a script that deletes any points that are less than 5 minutes apart. The goal is to decrease the total size of the database, because I don't need to store so many points. I only need points once every 15 minutes or so.
So to do this, I need a script that can find the first id in the table, where the difference between this id and another following row, is bigger than 5 minutes. SO I take it back. It needs to not compare X + 2 and X + 3. It must ALWAYS just compare it with the next value!
So I need a table like this:
id | created | dif
1 | 1563220108 | null
2 | 1563220408 | 300
3 | 1563220608 | 200
4 | 1563220808 | 200
5 | 1563220908 | 100
From this, I should be able to do what I need to do. Going to fiddle and see if I can get this myself.
If the table is named time_data
SELECT AA.id, MIN(BB.id)
FROM time_data AA
INNER JOIN time_data BB
ON TIMESTAMPDIFF(MINUTE, AA.created, BB.created) > 5
GROUP BY AA.id
You can use a correlated subquery and ordering:
select t.*
from t
where not exists (select 1
from t t2
where t2.created < t.created and
t2.created >= t.created - 5 * 60
)
order by t.id asc
limit 1;

How to conditional order results in mySql?

I have this scenario:
A user enter my application and push a button to look for a pair on a game. When he click the button, I do a insert in a table, whit his ID, his level, his countryID and a datetime whith NOW().
Then, I need to look in this table, trying to find a pair to play with him.
my problem is, i need to order the results based on the user level.
So, imagine my table have this rows:
|ID |userID|userLv| date_add | country_ID |
----------------------------------------------------
| 1 | 1 |3 |2016-06-24 12:09:06 | 5 |
| 2 | 2 |2 |2016-06-24 04:26:24 | 8 |
| 3 | 3 |2 |2016-06-24 11:38:54 | 1 |
| 4 | 4 |1 |2016-06-24 11:10:06 | 9 |
| 5 | 5 |2 |2016-06-24 11:35:49 | 6 |
| 6 | 6 |1 |2016-06-24 20:09:13 | 10 |
If a user with level 1 click the button, he needs to be matched with another user of level 1, odered by date_add ASC. But if there are no users of level 1 available, he will be paired with a user of level 2, again order by date_add ASC. if there are no level 2 users available, the he will be paired with a user of level 3.
Basically, i need a script that can return the rows ordered first by the userLv = x, then the other levels. How to do this with only one request to the database? Does this even make sense?
You can simple use a correlated query with LIMIT to get the second user id :
SELECT t.*,
(SELECT s.userID FROM YourTable s
WHERE s.userLv >= t.userLv
ORDER BY s.userLv
LIMIT 1) as user2_id
FROM YourTable t
WHERE t.userID = <YourParamOrWhatEver>
You can use your sql commands 1 at a time. Check if a same level query returns something and repeat it for some seconds. Then if you had no result, check the next sql statement for the same time and so on.

mysql update with subquery 2 level deep

Thanks for taking a look at this question. I'm kind of lost and hope someone can help me. Below is a update query i would like to run.
This query now returns an error:
1054 - Unknown column 'spi.et_cross_rank' in 'where clause'
Some background:
from table: tmp_ranking_tbl
I would like to get the nth(spi.et_return_rank) record
for a group with value x (spi.et_cross_rank)
SET #rownum=0;
UPDATE STRToer_Poule_indeling spi
SET spi.team_id = (SELECT R.team_poule_id
FROM (SELECT #rownum:=#rownum+1 AS rownum, trt.team_poule_id
FROM tmp_ranking_tbl trt
WHERE trt.overal_rank = spi.et_cross_rank
ORDER BY trt.punten DESC, (trt.goals_voor - trt.goals_tegen) DESC, trt.goals_voor DESC) R
WHERE R.rownum = spi.et_return_rank)
WHERE spi.et_ronde = v_et_ronde
AND spi.poule_id IN (SELECT row_id FROM STRToer_Poules WHERE toernooi_onderdeel_id=v_onderdeel_id) ;
Data in tmp_ranking_tbl looks like:
team_poule_id | punten | goals_voor | goals_tegen | overal_rank
65 | 6 | 10 | 10 | 2
69 | 6 | 9 | 10 | 2
75 | 7 | 11 | 4 | 2
84 | 6 | 6 | 8 | 2
112 | 5 | 7 | 7 | 2
Thanks in advance for the help!
Update after question in comment about the goal, i'll try to keep it short. :-)
This query is used on a website to keep scores of a tournament. Sometimes you have an odd number of teams going to the next round. At that point I want to select the best number 3(spi.et_cross_rank) team across poules. This is setting saved in the STRToer_Poule_indeling with what rank per poule and the 1st, 2nd or nth team(spi.et_return_rank). The table tmp_ranking_tbl is filled with all rank 3 teams across the poules. When this if filled I would like the 1st or 2nd, depedining on the setting in STRToer_Poule_indeling, record to return.
Subset of structure the STRToer_Poule_indeling table
row_id | team_id | et_ronde | et_cross_rank | et_return_rank
1 | null | 1 | 3 | 1
Just check if you have a column named et_cross_rank on your table STRToer_Poule_indeling
The problem seems to be that SQL can't find that column on your table.
Hope it helps.

MySQL - subtracting row 2 from row 1 where column is the same

I am creating a temp table which currently pulls values in for today and yesterday. The temp table looks like this:
|DateTime |company 1 |company 2 |
+------------------------+----------------------+
|2013-10-03 14:40:00 | 182475 | 110271 |
|2013-10-02 14:10:00 | 182086 | 110261 |
What I need to do is select the values from today, as well as the difference between today and yesterday. Based on the above data, the output would look like this:
Company 1 | Company 1 difference | Company 2| Company 2 difference
+-----------+----------------------+----------+--------------------------
182475 | 389 | 110271 | 10
I can't figure out how to specify which row to subtract from which, since there is no auto-increment field and the DateTime field will be slightly different each day.
Thanks for the help!
SELECT today.DateTime as DateTime
, today.Company1, today.Company1 - COALESCE(yesterday.Company1,0) AS Company1Diff
, today.Company2, today.Company2 - COALESCE(yesterday.Company2,0) AS Company2Diff
FROM tbl AS today
LEFT JOIN tbl AS yesterday
ON DATEDIFF(today.DateTime, yesterday.DateTime) = 1
ORDER BY today.DateTime ASC

MySql subselects and counts based on counts

Here is model of my table structure. Three tables.
---------------- ---------------------------- -------------------------------
|possibilities | |realities | |measurements |
|--------------| |--------------------------| |-----------------------------|
|pid| category | |rid | pid | item | status | |mid | rid | meas | date |
|--------------| |--------------------------| |-----------------------------|
|1 | animal | |1 | 1 | dog | 1 (yes)| |1 | 1 | 3 | 2012-01-01|
|2 | vegetable| |2 | 1 | fox | 1 | |2 | 3 | 2 | 2012-01-05|
|3 | mineral | |3 | 1 | cat | 1 | |3 | 1 | 13 | 2012-02-02|
---------------- |4 | 2 | apple| 2 (no) | |4 | 3 | 24 | 2012-02-15|
|5 | 1 | mouse| 1 | |5 | 2 | 5 | 2012-02-16|
|7 | 1 | bat | 2 | |6 | 6 | 4 | 2012-02-17|
---------------------------- -------------------------------
What I'm after is a result that will show me a series of counts based on measurement ranges for a particular entry from the "possibilities" table where the status of the related "realities" is 1 (meaning it's currently being tracked), BUT the only measurement that is relevant is the most recent one.
Here is an example result I'm looking for using animal as the possibility.
-----------------------
| 0-9 | 10-19 | 20-29 |
|---------------------|
| 2 | 1 | 1 |
-----------------------
So, in this example, the apple row was not used to make a count because it isn't an animal, nor was bat because it's status was set to no (meaning don't measure), and only the most recent measurements were used to determine the count.
I currently have a workaround in my real world use but it doesn't follow good database normalization. In my realities table I have a current_meas column that get updated when a new measurement is taken and entered in the measurements table. Then I only need to use the first two tables and I have a single SELECT statement with a bunch of embedded SUM statements that use an IF the value is between 0-9 for example. It gives me exactly what I want, however my app has evolved to the point where this convenience has become a problem in other areas.
So, the question is, is there a more elegant way to do this in one statement? Subselects? temporary tables? Getting the counts is the heart of what the app is about.
This is a PHP5, MySQL5, JQuery 1.8 based webapp, in case that give me some more options. Thanks in advance. I love the stack and hope to help back as much as it has helped me.
Here's one approach
Create a temp table to get the recent measurements
CREATE TEMPORARY TABLE RecentMeasurements
SELECT * FROM Measurements m
INNER JOIN (SELECT max(mid) max_id,date FROM Measurements GROUP BY DATE ORDER BY DATE ) x
ON x.max_id=m.mid
then do you query:
SELECT *, your counting logic
FROM Realities
WHERE status = 1 AND pid = 1
INNER JOIN RecentMeasurements
Here is what I ended up doing based on the two answers suggested.
First I created a temporary table that generates a table of
realities that are based on one possibility (animals) and whose
status is 1 (yes).
Second I created a temporary table that generates a table of the
individual realities from the first temp table, and finds the most
recent measurement for each one.
From this second table I do a select that gives me the breakdown of
counts in ranges.
When I tried it with just one temp table the query would take 5-10 seconds per possibility. In my real-world use I currently have 30 possibilities (a script loops through each one and generates these temp tables and selects), well over 1,000 realities (600 active on any given day, 100 added per month), and over 21,000 measurements (20-30 added daily). That just wasn't working for me. So breaking it up into smaller pools to draw from reduced it to the whole report running in under 3-4 seconds.
Here is the MySQL stuff with my real-world table and column names.
//Delete the temporary tables in advance
$delete_np_prod = 'DROP TABLE IF EXISTS np_infreppool';
mysql_query($delete_np_prod) or die ("Drop NP Prod Error " . mysql_error ());
$delete_np_max = 'DROP TABLE IF EXISTS np_maxbrixes';
mysql_query($delete_np_max) or die ("Drop NP Max Error " . mysql_error ());
//Make a temporary table to hold the totes of this product at North Plains that are active
$create_np_prod_pool_statement = 'CREATE TEMPORARY TABLE np_infreppool
SELECT inf_row_id FROM infusion WHERE formid = ' . $active_formids["formid"] . ' AND location = 1 AND status = 1';
mysql_query($create_np_prod_pool_statement) or die ("Prod Error " . mysql_error ());
//Make a temporary table to hold the tote with its most recent brix value attached to it.
$create_np_maxbrix_pool_statement = 'CREATE TEMPORARY TABLE np_maxbrixes
SELECT b.inf_row_id AS inf_row_id, b.brix AS brix from brix b, np_infreppool pool WHERE b.inf_row_id = pool.inf_row_id AND b.capture_date = (SELECT max(capture_date) FROM brix WHERE inf_row_id = pool.inf_row_id )';
mysql_query($create_np_maxbrix_pool_statement) or die ("Brix Error " . mysql_error ());
//Get the counts for slected form from NP
$get_report_np = "SELECT
SUM(IF(brix BETWEEN 0 AND 4,1,0)) as '0-4',
SUM(IF(brix BETWEEN 5 AND 9,1,0)) as '5-9',
SUM(IF(brix BETWEEN 10 AND 14,1,0)) as '10-14',
SUM(IF(brix BETWEEN 15 AND 19,1,0)) as '15-19',
SUM(IF(brix BETWEEN 20 AND 24,1,0)) as '20-24',
SUM(IF(brix BETWEEN 25 AND 29,1,0)) as '25-29',
SUM(IF(brix BETWEEN 30 AND 34,1,0)) as '30-34',
SUM(IF(brix BETWEEN 35 AND 39,1,0)) as '35-39',
SUM(IF(brix BETWEEN 40 AND 44,1,0)) as '40-44',
SUM(IF(brix BETWEEN 45 AND 49,1,0)) as '45-49',
SUM(IF(brix BETWEEN 50 AND 54,1,0)) as '50-54',
SUM(IF(brix BETWEEN 55 AND 59,1,0)) as '54-49',
SUM(IF(brix BETWEEN 60 AND 64,1,0)) as '60-64',
SUM(IF(brix BETWEEN 65 AND 69,1,0)) as '65-69',
SUM(IF(brix >=70, 1, 0)) as 'Over 70'
FROM np_maxbrixes";
$do_get_report_np = mysql_query($get_report_np);
$got_report_np = mysql_fetch_array($do_get_report_np);
UPDATE
I got it to work in a single SELECT statement without using temporary tables and it works faster. Using my sample schema above, here is how it looks.
SELECT
SUM(IF(m.meas BETWEEN 0 AND 4,1,0)) as '0-4',
SUM(IF(m.meas BETWEEN 5 AND 9,1,0)) as '5-9',
SUM(IF(m.meas BETWEEN 10 AND 14,1,0)) as '10-14',
SUM(IF(m.meas BETWEEN 15 AND 19,1,0)) as '15-19',
SUM(IF(m.meas BETWEEN 20 AND 24,1,0)) as '20-24',
SUM(IF(m.meas BETWEEN 25 AND 29,1,0)) as '25-29',
SUM(IF(m.meas BETWEEN 30 AND 34,1,0)) as '30-34',
SUM(IF(m.meas BETWEEN 35 AND 39,1,0)) as '35-39',
SUM(IF(m.meas BETWEEN 40 AND 44,1,0)) as '40-44',
SUM(IF(m.meas BETWEEN 45 AND 49,1,0)) as '45-49',
SUM(IF(m.meas BETWEEN 50 AND 54,1,0)) as '50-54',
SUM(IF(m.meas BETWEEN 55 AND 59,1,0)) as '54-49',
SUM(IF(m.meas BETWEEN 60 AND 64,1,0)) as '60-64',
SUM(IF(m.meas BETWEEN 65 AND 69,1,0)) as '65-69',
SUM(IF(m.meas >=70, 1, 0)) as 'Over 70'
FROM measurement m, realities r
WHERE r.status = 1 AND r.pid = " . $_GET['pid'] . " AND r.rid = m.rid AND m.date = (SELECT max(date) FROM measurements WHERE rid = r.rid)