Comparing two numbers that are approximately equal - mysql

I have two tables, Table A and Table B. I have two attributes L1 and L2 for each table. I am trying output all the rows for both tables where L1 and L2 are equal for both tables. The problem is that L1 an L2 may differ my some small quantity. So when I run:
SELECT * FROM TableA l1 join TableB l2 on l1.L1 =l2.L1 and l1.L2 = l2.L2
I get an empty set even though there are records that do match. How do I resolve this problem?
Example:
L1 = 118.4363 for Table A but for Table B L1 = 118.445428

Instead of checking for equality, check that the difference is below some threshold (e.g., 0.1, as in the example below).
SELECT * FROM
TableA l1, TableB l2
WHERE
ABS(l1.L1-l2.L1) < 0.1
AND
ABS(l1.L2-l2.L2) < 0.1

You will need to devise some tolerance, like say a difference of 0.01. Then compute the absolute value of the two when subtracted and see if it's within your tolerance
SET #tolerance_value = 0.01;
SELECT *
FROM
TableA l1 join
TableB l2
on ABS(l1.L1 - l2.L1) < #tolerance_value and ABS(l1.L2 - l2.L2) < #tolerance_value;

You cannot ask the engine to return the ones which differ in "some small quantity".
You can choose the rows which difference "abs(a - b)" is between two fixed values.
Like rows where a-b > 5 or a - b > x and a - b < x+10. for example

Try using the round function in Sybase or SQL Server so 118 matches 118. For other DBMS find a round equivalent.
mike

In order to #cheeken's answer to work, you must put a semicolon at the last query, otherwise it won't work:
SELECT * FROM
TableA l1, TableB l2
WHERE
ABS(l1.L1-l2.L1) < 0.1
AND
ABS(l1.L2-l2.L2) < 0.1;

Related

For Loop in MySQL, looping through a table and applying it to a where statement

My problem is how to loop through a table and extract information from another table.
I have a table - X with 470 records:
A B C
111 12 18
121 21 29
127 37 101
I would like to write the following query:
create or replace view NEW as
For j = 1-3
Select * from Y
where imei = X.A(j) and id > X.B(j) and id < X.C(j)
Apologies, I am a matlab programmer so I have used that syntax above to explain what I want. How can I do this in MySql? I have looked up For Loops but mostly it loops through within the same table. I need to loop through a different table and use those criteria in the where statement of a different table.
To get 3 rows from a table, use LIMIT 3 in a subquery. To get related rows in another table, use JOIN.
CREATE OR REPLACE VIEW new AS
SELECT Y.*
FROM Y
JOIN (SELECT *
FROM X
LIMIT 3) AS X ON Y.ime1 = X.a AND Y.id > X.b AND Y.id < X.c
To make LIMIT 3 produce predictable results, you should have an ORDER BY clause in the subquery. Otherwise, it will select an arbitrary set of 3 rows from X.

SQL - query optimization to work with big data

I have table 33_PROBLEM with columns ROOT and ROOT_CUBED. Ten i have a simple procedure, that insert data, let´s begins with ROOT from -10000 to 10000, which means ROOT_CUBED from -10000^3 to 10000^3.
Question is simple:
How can I get all triplets combinations of ROOT_CUBED values, that add to number given?
Said in different way:
I want to find A, B, C for which is true, that A^3 + B^3 + C^3 = number_given
Here is some example for searched number 33:
SELECT T1.r1,
T2.r2,
T3.r3
FROM (SELECT root_3 AS R1
FROM `33_problem`) AS T1,
(SELECT root_3 AS R2
FROM `33_problem`) AS T2,
(SELECT root_3 AS R3
FROM `33_problem`) AS T3
WHERE T1.r1 + T2.r2 + T3.r3 = 33
It works well ... on a small amount of rows. This query makes (COUNT *)^3 rows, which for 20000 input lines equals to 8e+12 rows !! ... RIP serever ...
what is the right way to solve this one?
( I got the idea from https://www.youtube.com/watch?v=wymmCdLdPvM and I hope, when someone comes with some answers, i will understand better, how SQL works and how queries and databases should be designed to work good even for big data )
1) you could try to only select sequential triplets, such that R1 <= R2 <= R3,
2) if you have duplicates, select distinct
SELECT T1.R1
,T2.R2
,T3.R3
FROM (
SELECT DISTINCT ROOT_3 AS R1
FROM `33_PROBLEM`
) AS T1
,(
SELECT DISTINCT ROOT_3 AS R2
FROM `33_PROBLEM`
WHERE R2>=R1
) AS T2
,(
SELECT DISTINCT ROOT_3 AS R3
FROM `33_PROBLEM`
WHERE R3>=R2
) AS T3
WHERE T1.R1 + T2.R2 + T3.R3 = 33
I tried looking from -10000 to 10000 and there isn't a solution then i watched the youtube video and they say that they already tried up to 10 to the 14th and still no solution.
I did it with python code though when i tried -10000 to 10000...and to optimize of looking for the the C value. First I look at the sum of A cubed and B cubed...the subtract that from 33 and calculate cube root the answer to try to find C in one hit... , this optimizes it a little because then you don't have to loop through all possible values of C.
Since there is no solution for up to 10 to 14th i don't think i can find a solution since just with -10000 to 10000 It took my computer over 2 hours to search. If i looked to 10 to the 14th it would takes like millions of years or something crazy.
You could work with a single table if integers, then do a "self join" using a "cross join".
For R3, you only need to check -ROUND(POW(r1.root_3 + r2.root3, 1/3)). This should significantly speed things up. Also, to make this work, be sure that you have a positive number.
SELECT t1.r1, t2.r2, -ROUND(POW(r1.root_3 + r2.root3, 1/3))
FROM `33_PROBLEM` AS t1
JOIN `33_PROBLEM` AS t2
WHERE t1.root_3 > 0
AND t2.root_3 > -t1.root_3
AND (t1.root_3 + t2.root_3) = ROUND(POW(r1.root_3 + r2.root3, 1/3))

mysql lookup match

I have a database column like this:
id
50
55
56
62
63
64
65
68
70
72
80
etc...
I want to iterate through the id column with the following formula to find if the result of the formula is an id number in the same column. I want to compute all the possible combinations of the set of basically 3 records in the id column.
First loop:
Does ((second_id_number - first_id_number) * variable decimal) + second_id_number equal a number in the id column?
Per the formula, the first loop is
(55-50)*2.00(as an example of variable decimal) + 55 = 65. 65 is in the list => 65 is tagged with the 2 records which equal it
Second loop:
Does ((third_id_number - first_id_number) * variable decimal) + second_id_number equal a number in the id column?
(56-50)*2.00(as an example of variable decimal) + 56 = 78. 78 is not in the list => 78 is not tagged
Third loop:
Does ((fourth_id_number - first_id_number) * variable decimal) + second_id_number equal a number in the id column?
etc...
I want the results to show all the tagged records. A tagged record is the set of the 3 records where the third record is the result from the formula.
Anyone got any ideas? Is it possible in mysql?
Thank you
If I'm understanding your requirements properly, it sounds like you'd want to use a self-join on the table, e.g.
SELECT ...
FROM yourtable AS parent
LEFT JOIN yourtable AS child ON
FLOOR((parent.second_id_number - parent.first_id_number) * variable) + parent.second_id) = child.id
You could potentially carry something like this forward, which satisfies your first "loop"
select a.id as first_id_number
, b.id as second_id_number
, ((b.id - a.id) * 2) + b.id as third_id_number
from my_table as a
join my_table as b on a.id = (select max(id) from my_table where id < b.id)
where ((b.id - a.id) * 2) + b.id in (select id from my_table)
According to your description and test data, this would show 65 as "tagged" with first_id_number 50 and 62.
Warning: done on SQL Server using what I think is fairly standard syntax. I would understand if some would rather phrase this as a cross join with the select max... bit in the where clause rather than in the join predicate.

MySQL ranking in presence of indexes using variables

Using the classic trick of using #N=#N + 1 to get the rank of items on some ordered column. Now before ordering I need to filter out some values from the base table by inner joining it with some other table. So the query looks like this -:
SET #N=0;
SELECT
#N := #N + 1 AS rank,
fa.id,
fa.val
FROM
table1 AS fa
INNER JOIN table2 AS em
ON em.id = fa.id
AND em.type = "A"
ORDER BY fa.val ;
The issue is if I don't have an index on the em.type, then everything works fine but if I put an index on em.type then hell unleashes and the rank values instead of coming ordered by the val column comes in the order the rows are stored in the em table.
here are sample outputs -:
without index-:
rank id val
1 05F8C7 55050.000000
2 05HJDG 51404.733458
3 05TK1Z 46972.008208
4 05F2TR 46900.000000
5 05F349 44433.412847
6 06C2BT 43750.000000
7 0012X3 42000.000000
8 05MMPK 39430.399658
9 05MLW5 39054.046383
10 062D20 35550.000000
with index-:
rank id val
480 05F8C7 55050.000000
629 05HJDG 51404.733458
1603 05TK1Z 46972.008208
466 05F2TR 46900.000000
467 05F349 44433.412847
3534 06C2BT 43750.000000
15 0012X3 42000.000000
1109 05MMPK 39430.399658
1087 05MLW5 39054.046383
2544 062D20 35550.000000
I believe the use of indexes should be completely transparent and outputs should not be effected by it. Is this a bug in MySQL?
This "trick" was a bomb waiting to explode. A clever optimizer will evaluate a query as it sees fits, optimizing for speed - that's why it's called optimizer. I don't think this use of MySQL variables was documented to work as you expect it to work, but it was working.
Was working, up until recent improvements on the MariaDB optimizer. It will probably break as well in the mainstream MySQL as there are several improvements on the optimizer in the (yet to be released, still beta) 5.6 version.
What you can do (until MySQL implemented window functions) is to use a self-join and a grouping. Results will be consistent, no matter what future improvements are done in the optimizer. Downside is that that it may not be very efficient:
SELECT
COUNT(*) AS rank,
fa.id,
fa.val
FROM
table1 AS fa
INNER JOIN table2 AS em
ON em.id = fa.id
AND em.type = 'A'
INNER JOIN
table1 AS fa2
INNER JOIN table2 AS em2
ON em2.id = fa2.id
AND em2.type = 'A'
ON fa2.id <= fa.id
--- assuming that `id` is the Primary Key of the table
GROUP BY fa.id
ORDER BY fa.val ;

How do I select one record per group and left join with another table?

I have one table with two location IDs (pointa and pointb) and how far apart they are. Multiple pairs can be the same distance apart.
Example data:
pointa pointb distance
1 2 250
3 4 250
4 5 250
.....
6 8 500
10 12 500
13 17 500
I want to select one pair from each distance class (one from 250, one from 500 and so on) and join those to another table which contain attributes for that location.
If I were to write this as an algorithm, it would go:
Select one pair at random from each distance class from table distance
join with table data based on distance.pointa=data.location.
Then do the same for pointb such that pointb=data.location
So after the join, I end up with:
pointa pointb data_a data_b
1 2 234.5 440.2
Does anyone have ideas on how I can achieve this?
For now I am doing this using PHP (looking up attributes for a, then b, and then updating a new table. Clearly this is inefficient and I want to learn a better way to do this directly in MySQL.
tia.
getting random rows in SQL can be less than efficient. I'd try something like:
SELECT distance, GROUP_CONCAT(pointa) a, GROUP_CONCAT(pointb) b FROM mytable
GROUP BY distance;
Then in PHP,
while($distance_points= $result->fetch_assoc()){
$distance=$distance_points['distance'];
$as=explode($distance_points['a']);
$random_a=array_rand($as,1);
$bs=explode($distance_points['b']);
$random_b=$bs[array_search($as,$random_a)]; //to get the pair assuming points don't repeat
echo "$distance $random_a $random_b";
}
ETA: Since you have too much data for GROUP_CONCAT, a multiquery solution might work better. Something like:
SELECT DISTINCT distance FROM mytable ORDER BY distance; //get an array with the possible distances
foreach($distances as $d)
$q="SELECT distance, a, b FROM mytable WHERE distance=$d ORDER BY RAND() LIMIT 1";
//run query, retrieve single result and output or store
}
Since you want to get extra info about your locations a and b, you could add that join to $q. Something like:
SELECT distance, a, li1.gps, b, li2.gp2
FROM mytable m
JOIN location_info li1 ON (m.a=li1.id)
JOIN location_info li2 ON (m.b=li2.id)
WHERE distance=$d
ORDER BY RAND() LIMIT 1