Finding the min distance MYSQL - mysql

I want to find the min distance between (x2-x1)^2 + (y2-y1)^2 for a particular macId and timeStamp. I'm trying to find a nearest possible gate
location for an individual at one particular instance of time. So, the query should return one unique value of user at one instance of time that is min to the GATE location.
The data set looks like:
X1 Y1 TimeStamp MACID X2 Y2 Gate
| 5618 | 5303 |1 12:22:02 | 54:ea:a8:53:5b:eb | 5844 | 5377 | C24
| 5848 | 5046 |1 12:22:02 | 54:ea:a8:53:5b:eb | 5844 | 5377 | C18
| 6094 | 5464 |1 12:22:02 | 54:ea:a8:53:5b:eb | 5844 | 5377 | C17
| 6021 | 6540 |1 13:09:48 | 48:5a:3f:6a:01:b9 | 6210 | 6801 | C23
| 6366 | 7036 |1 13:09:48 | 48:5a:3f:6a:01:b9 | 6210 | 6801 | C14
| 6366 | 7036 |1 13:09:48 | 48:5a:3f:6a:01:b9 | 6210 | 6801 | C13
The result set should look like below:
X1 Y1 TimeStamp MACID X2 Y2 Gate
| 5848 | 5046 |1 12:22:02 | 54:ea:a8:53:5b:eb | 5844 | 5377 | C18
| 6021 | 6540 |1 13:09:48 | 48:5a:3f:6a:01:b9 | 6210 | 6801 | C23
I have tried this below query but not working:
select min((x2-x1)^2 + (y2-y1)^2), macID, timeStamp from maptable
groupbymacID, timeStamp
I also tried using self joins but seems completely wrong.
May I know where I'm going wrong.

You could use this query:
SELECT m.*
FROM maptable m, (
SELECT TimeStamp, macid, MIN(POW((x2-x1), 2) + POW((y2-y1), 2)) mindist
FROM maptable
GROUP BY TimeStamp, macid
) a
WHERE m.TimeStamp = a.TimeStamp AND m.macid = a.macid
AND POW((x2-x1), 2) + POW((y2-y1), 2) = a.mindist;
SQL Fiddle: http://sqlfiddle.com/#!9/d7979/6
But note that it does not return one row per macid and date, because in your input data the two final rows are the same, so the min distance is the same for gates C13 and C14

Related

Map value from table to a different value in MySQL

I have two tables in MySQL:
table1:
+----------------+-------------------+
| meterName | retailPrice |
+----------------+-------------------+
| ND40rs v2 Spot | 8.5851642 |
| ND80rs v2 Spot | 4.5851642 |
+------------------------------------+
table2:
+----------------+-------------------+
| mapMeterName | time |
+----------------+-------------------+
| nd40_spot | 1.2 |
| nd80_spot | 2.5 |
+------------------------------------+
I want to map the value of meterName to the mapMeterName, and get the multiplication of the time and the retailPrice. so meterName ND40rs v2 Spot is equivalent to nd40_spot and I want the 1.2 x 8.5851642 value, and similary nd80_spot and ND80rs v2 spot are equivalent and I want 2.5 x 4.5851642
To get something like:
+----------------+-------------------+----------------------------+
| meterName | retailPrice | (time x retailPrice)= cost |
+----------------+-------------------+----------------------------+
| ND40rs v2 Spot | 8.5851642 | 10.302197 |
| ND80rs v2 Spot | 4.5851642 | 11.4629105 |
+------------------------------------+----------------------------+
Is there a one liner in MySQL i can use to do this type of mapping?
You would need another table to store the mapping.
table3
+----------------+-------------------+
| meterName | mapMeterName |
+----------------+-------------------+
| ND40rs v2 Spot | nd40_spot |
| ND80rs v2 Spot | nd80_spot |
+------------------------------------+
Then use a join:
SELECT table1.meterName, table1.retailPrice,
table1.retailPrice * table2.time as `(time x retailPrice)= cost`
FROM table1
JOIN table3 USING (meterName)
JOIN table2 USING (mapMeterName);

MySQL Generate random timestamps from range, order it by date asc, then use it to update missing values from another table

I have a table "ips", where I store my download logs. Accidentally, I forgot to add timestamp for it (yea, stupid mistake)... Now, I have fixed it, but there are already 65.5k entries without timestamp.. Is there a way, how to add random timestamp from date range to fill NULL timestamps?
I was able to generate timestamps list using this queries:
SET #MIN = '2020-04-05 18:30:00';
SET #MAX = NOW();
SELECT TIMESTAMPADD(SECOND, FLOOR(RAND() * TIMESTAMPDIFF(SECOND, #MIN, #MAX)), #MIN) as dldate FROM ips WHERE name="filename1" ORDER BY dldate ASC;
It generated the exact count of entries I need for specific filename, but I have absolutely no idea, how to use this list to update already existing entries in my "ips" table and KEEP IT ORDERED by "dldate"...
When I was testing it, I was close, when I used this query (I was afraid to use UPDATE to not mess my data up, so I used just SELECT):
SELECT ips.id, ips.name, t1.dldate FROM (SELECT id, name FROM ips WHERE name="filename1") ips INNER JOIN (SELECT ips.id as id, TIMESTAMPADD(SECOND, FLOOR(RAND() * TIMESTAMPDIFF(SECOND, #MIN, #MAX)), #MIN) as dldate FROM ips WHERE name="filename1" ORDER BY dldate ASC) t1 ON (ips.id=t1.id) ORDER BY ips.id ASC;
That worked, but timestaps are purely random (obviously :D), and I need them to "respect" id from "ips" table (starting with lower timestamp for lowest id, and then continuously higher timestamps for higher ids).
I'm getting this:
+------+-----------+---------------------+
| id | name | dldate |
+------+-----------+---------------------+
| 15 | filename1 | 2020-12-18 21:35:03 |
| 1118 | filename1 | 2020-12-18 13:34:47 |
| 1141 | filename1 | 2020-08-07 12:49:46 |
| 1142 | filename1 | 2020-11-29 00:43:31 |
| 1143 | filename1 | 2020-05-13 03:00:16 |
| 1286 | filename1 | 2020-12-14 09:58:50 |
| 1393 | filename1 | 2021-04-14 06:45:23 |
| 1394 | filename1 | 2021-03-03 17:42:25 |
| 1395 | filename1 | 2020-09-03 05:56:56 |
| .... |
|62801 | filename1 | 2021-01-05 21:21:29 |
+------+-----------+---------------------+
And I would like to get this:
+------+-----------+---------------------+
| id | name | dldate |
+------+-----------+---------------------+
| 15 | filename1 | 2020-04-05 21:35:03 |
| 1118 | filename1 | 2020-04-18 13:34:47 |
| 1141 | filename1 | 2020-05-07 12:49:46 |
| 1142 | filename1 | 2020-06-29 00:43:31 |
| 1143 | filename1 | 2020-08-13 03:00:16 |
| 1286 | filename1 | 2020-10-14 09:58:50 |
| 1393 | filename1 | 2020-12-14 06:45:23 |
| 1394 | filename1 | 2021-01-03 17:42:25 |
| 1395 | filename1 | 2021-03-03 05:56:56 |
| .... |
|62801 | filename1 | 2021-04-29 14:21:29 |
+------+-----------+---------------------+
Is there any way, how to achieve this output and how to use it with UPDATE statement instead of SELECT with INNER JOIN?
Thank you for help!
How about just starting with a date and adding a time unit?
update ips
set timestamp = '2000-01-01' + interval id second
where timestamp is null;
I'm not sure if second is the right unit. Or if '2000-01-01' is a good base time. But this gives you an approach for doing what you want.
You can, of course, test this using a select first.
If you do want randomness, you can do something like this:
select ips.*,
'2021-04-01' - interval running second
from (select ips.*,
sum(rnd) over (order by id desc) as running
from (select ips.*,
rand() * 1000 as rnd
from ips
where timestamp is null
) ips
) ips;
This calculates a random number of seconds. Then it does a revenue cumulative sum . . . and subtracts those seconds from a base date.

Calculations of different columns in Mysql Query

I have a Table:-
+-----+--------------+--------------+----------+--------------------+---------------+-----------------+
| id | CustomerName | VideoQuality | IsActive | BufferedTime | ElapsedTime | TotalBufferTime |
+-----+--------------+--------------+----------+--------------------+---------------+-----------------+
| 139 | HotStar | 180 | Yes | 10.367167126617211 | 30.000000000 | NULL |
| 140 | HotStar | 1300 | NULL | 5.43524230876729 | 34.000000000 | NULL |
| 141 | HotStar | 1300 | NULL | 5.671054515212042 | 38.000000000 | NULL |
| 142 | HotStar | 1300 | NULL | 5.045639532902047 | 41.000000000 | NULL |
| 143 | HotStar | 1300 | NULL | 5.455747718023355 | 44.000000000 | NULL |
| 144 | HotStar | 1300 | NULL | 5.691559924468107 | 49.000000000 | NULL |
i want to calculate the columns BufferTime and ElapsedTime and insert that output to TotalBufferTime column but i want to skip the first row of the BufferTime.
So the fisrt calculation will be 5.43 + 30.000 second calculation will be 5.67 + 34.00 and so on.
I also have a column IsActive which shows the first row of Buffer time.
I want to do something like this :-
update RequestInfo SET `TotalBufferTime` = BufferedTime + ElapsedTime;
only thing i want to skip only the first row of the column buffered time.
Assuming you a field id that determines row order in your table, you can use a correlated subquery so as to get BufferedTime of previous row like this:
SELECT t1.CustomerName, t1.VideoQuality, t1.IsActive, t1.BufferedTime,
t1.ElapsedTime,
(SELECT t2.BufferedTime
FROM mytable AS t2
WHERE t2.td > t1.id
ORDER BY id LIMIT 1) + t1.ElapsedTime AS TotalBufferTime
FROM mytable AS t1
WHERE IsActive IS NULL
Edit:
To UPDATE you can use the following query:
SET #et = 0;
SET #ElapsedTime = NULL;
UPDATE RequestInfo
SET TotalBufferTime = CASE
WHEN (#et := #ElapsedTime) < 0 THEN NULL
WHEN #ElapsedTime := ElapsedTime THEN BufferedTime + #et
END
ORDER BY id;
The trick here is to use a CASE expression where the first WHEN clause is always evaluated (because it is the first one) but is never true. This way #et variable is initialized with the value of #ElapsedTime, i.e. the value of the previous record.
Demo here

mysql string to date conversion

Need help to suggest sql query where I have a varchar column called Timing like below
+---------------------+
| Timing | MO |
+---------------------+
|7/11/2016 20:45 |ABC |
|7/12/2016 20:45 |ABC |
|2016-07-11 00:00|ABC |
|2016-07-12 00:00|ABC |
+---------------------+
I need to extract date from the Timing column but tricky part is the date is not always in same format. I need to output timing column as below
+-------------+
| Timing | MO|
+-------------+
|7/11/2016|ABC|
|7/12/2016|ABC|
|7/11/2016|ABC|
|7/12/2016|ABC|
+-------------+
It's simple enough using substring and replace functions to cast varchar as data and recast them to you required output.
for example
DROP TABLE T;
CREATE TABLE T (TIMING VARCHAR(20),MO VARCHAR(2));
INSERT INTO T VALUES
('7/11/2016 20:45' ,'AB'),
('7/12/2016 20:45' ,'AB'),
('2016-07-11 00:00','AB'),
('2016-07-12 00:00','AB'),
('27/06/2016 20:45' ,'AB'),
('29/05/2016 20:45' ,'AB')
*/
SELECT *,
CASE
WHEN INSTR(TIMING,'/') = 0 THEN
CASE WHEN INSTR(TIMING,'-') = 5 THEN CAST(TIMING AS DATE)
end
WHEN INSTR(TIMING,'/') = 3 THEN
CAST(
CONCAT(
SUBSTRING(CONCAT('0',TIMING),8,4)
,'-'
,SUBSTRING(CONCAT('0',TIMING),5,2)
,'-'
,SUBSTRING(CONCAT('0',TIMING),1,2)
)
AS DATE
)
WHEN INSTR(TIMING,'/') = 2 THEN
CAST(
CONCAT(
SUBSTRING(CONCAT('0',TIMING),7,4)
,'-'
,SUBSTRING(CONCAT('0',TIMING),4,2)
,'-'
,SUBSTRING(CONCAT('0',TIMING),1,2)
)
AS DATE
)
END
FROM T
results in
-----------------------------------------------------------------------------+
| 7/11/2016 20:45 | AB | 2016-11-07 |
| 7/12/2016 20:45 | AB | 2016-12-07 |
| 2016-07-11 00:00 | AB | 2016-07-11 |
| 2016-07-12 00:00 | AB | 2016-07-12 |
| 27/06/2016 20:45 | AB | 2016-06-02 |
| 29/05/2016 20:45 | AB | 2016-05-02 |
+------------------+------+-----------------------------------------------------
You do have to code for every known variant - in this case I have identified that 7/11/2016 needs to left padded with zero and slashes replaced with dashs before date conversion. Hopefully you have a limited number of date variations in your source to code for and you will end up with a large query - but that's life. God luck,

How can I select all rows which have been inserted in the last day?

I have a table like this:
// reset_password_emails
+----+----------+--------------------+-------------+
| id | id_user | token | unix_time |
+----+----------+--------------------+-------------+
| 1 | 2353 | 0c274nhdc62b9dc... | 1339412843 |
| 2 | 2353 | 0934jkf34098joi... | 1339412864 |
| 3 | 5462 | 3408ujf34o9gfvr... | 1339412894 |
| 4 | 3422 | 2309jrgv0435gff... | 1339412899 |
| 5 | 3422 | 34oihfc3lpot4gv... | 1339412906 |
| 6 | 2353 | 3498hfjp34gv4r3... | 1339412906 |
| 16 | 2353 | asdf3rf3409kv39... | 1466272801 |
| 7 | 7785 | 123dcoj34f43kie... | 1339412951 |
| 9 | 5462 | 3fcewloui493e4r... | 1339413621 |
| 13 | 8007 | 56gvb45cf3454g3... | 1339424860 |
| 14 | 7785 | vg4er5y2f4f45v4... | 1339424822 |
+----+----------+--------------------+-------------+
Each row is an email. Now I'm trying to implement a limitation for sending-reset-password email. I mean an user can achieve 3 emails per day (not more).
So I need an query to check user's history for the number of emails:
SELECT count(1) FROM reset_password_emails WHERE token = :token AND {from not until last day}
How can I implement this:
. . . {from now until last day}
Actually I can do that like: NOW() <= (unix_time + 86400) .. But I guess there is a better approach by using interval. Can anybody tell me what's that?
Your expression will work, but has 3 problems:
the way you've coded it means the subtraction must be performed for every row (performance hit)
because you're not using the raw column value, you couldn't use an index on the time column (if one existed)
it isn't clear to read
Try this:
unix_time > unix_timestamp(subdate(now(), interval '1' day))
here the threshold datetime is calculated once per query, so all of the problems above have been addressed.
See SQLFiddle demo
You can convert your unix_time using from_unixtime function
select r.*
from reset_password_emails r
where now() <= from_unixtime(r.unix_time) - interval '1' day
Just add the extra filters you want.
See it here: http://sqlfiddle.com/#!9/4a7a9/3
It evaluates to no rows because your given data for unix_time field is all from 2011
Edited with a sqlfiddle that show the conversion:
http://sqlfiddle.com/#!9/4a7a9/4