MySQL JOIN two tables on closest matching value - mysql

I have the following situation:
tableA
+-------+-------+
| id | Value |
+-------+-------+
| 1 | 1000 |
| 2 | 20 |
| 3 | 62 |
| 4 | 0 |
+-------+-------+
tableB
+-------+--------+
| Value | Lookup |
+-------+--------+
| 10 | a |
| 20 | b |
| 30 | b |
| 40 | g |
| 50 | h |
| 60 | f |
| 70 | a |
| 80 | a |
| 90 | v |
| 100 | b |
+-------+--------+
And I need to return the lookup in table B that most closely matches the value field in table A. For example.
+-------+-------+--------+
| id | Value | Lookup |
+-------+-------+--------+
| 1 | 1000 | b |
| 2 | 20 | b |
| 3 | 62 | f |
| 4 | 0 | a |
+-------+-------+--------+
How can I go about doing this?

Here is an option using joins:
SELECT
a.Id, a.Value, b.Lookup
FROM tableA a
CROSS JOIN tableB b
INNER JOIN
(
SELECT a.Id, MIN(ABS(a.Value - b.Value)) AS min_abs_value
FROM tableA a
CROSS JOIN tableB b
GROUP BY a.Id
) t
ON a.Id = t.Id AND
ABS(a.Value - b.Value) = t.min_abs_value;
Demo
While this query does join to a subquery, the subquery is not correlated.

One way is to use a correlated subquery:
SELECT a.Id, a.Value,
(SELECT b.Lookup
FROM TableB AS b
ORDER BY ABS(a.Value - b.Value) LIMIT 1)
FROM TableA AS a
Demo here

Related

How to join tables and include entries where not in both tables?

I have two tables like:
Table A
+-----------+---------------+
| AccountID | NumberOfAdams |
+-----------+---------------+
| A1 | 2 |
| A2 | 5 |
| A3 | 10|
+-----------+---------------+
Table B:
+-----------+----------------+
| AccountID | NumberOfBrowns |
+-----------+----------------+
| A3 | 13 |
+-----------+----------------+
Using a LEFT JOIN, with table B as the first table, the resulting table is:
+-----------+---------------+----------------+
| AccountID | NumberOfAdams | NumberOfBrowns |
+-----------+---------------+----------------+
| A3 | 10 | 13 |
+-----------+---------------+----------------+
What I want to get is:
+-----------+---------------+----------------+
| AccountID | NumberOfAdams | NumberOfBrowns |
+-----------+---------------+----------------+
| A1 | 2 | NULL |
| A2 | 5 | NULL |
| A3 | 10 | 13 |
+-----------+---------------+----------------+
How can I retrieve all AccountIDs from both with a JOIN?
This query might work
SELECT p.AccountID, a.NumberOfAdams, b.NumberOfBrowns
FROM (
SELECT AccountID
FROM table_a
UNION
SELECT AccountID
FROM table_b
) p
LEFT JOIN table_a a ON p.AccountID = a.AccountID
LEFT JOIN table_b b ON p.AccountID = b.AccountID

Count records in a MySQL table based for each record of another table

I have two tables in a MySQL database -
table_a:
+----+---------+-------------+-----------------
| id | section | sub_section | ...other_fields
+----+---------+-------------+-----------------
| 1 | A | X |
| 2 | A | Y |
| 3 | A | Z |
| 4 | B | P |
| 5 | B | Q |
| 6 | C | L |
| 7 | C | M |
| 8 | C | N |
| 9 | C | O |
+----+---------+-------------+-----------------
table_b:
+----+-------------+---------+-----------------
| id | sub_section | b_count | ...other_fields
+----+-------------+---------+-----------------
| 1 | X | 1 |
| 2 | Y | 1 |
| 3 | L | 0 |
| 4 | P | 1 |
| 5 | P | 1 |
| 6 | X | 0 |
| 7 | M | 1 |
| 8 | Y | 0 |
| 9 | Q | 1 |
+----+-------------+---------+-----------------
I want to find the count of sub_section in table_b and the sum of b_count from Table B for each distinct section in table_a -
Expected Result:
+---------+--------------------+--------------+
| section | COUNT(sub_section) | SUM(b_count) |
+---------+--------------------+--------------+
| A | 4 | 2 |
| B | 3 | 3 |
| C | 2 | 1 |
+---------+--------------------+--------------+
One way to do this would be to run Count(section) number of queries and then combine the results.
Something like:
SELECT 'A' AS section, COUNT(sub_section), SUM(b_count) FROM table_b WHERE sub_section IN (SELECT DISTINCT sub_section FROM table_a WHERE section='A')
UNION
SELECT 'B' AS section, COUNT(sub_section), SUM(b_count) FROM table_b WHERE sub_section IN (SELECT DISTINCT sub_section FROM table_a WHERE section='B')
UNION
SELECT 'C' AS section, COUNT(sub_section), SUM(b_count) FROM table_b WHERE sub_section IN (SELECT DISTINCT sub_section FROM table_a WHERE section='C');
Is there a better way to do this in a query?
The section list in table_a is dynamic and might change and I do not want to update my query each time the values change.
SELECT t1.section, COUNT(DISTINCT t1.sub_section), SUM(t2.b_count)
FROM table_a t1
LEFT JOIN table_b t2 USING (sub_section)
GROUP BY t1.section;
fiddle

SQL - How can I JOIN two tables and SUM a column based on IDs between them?

I have two tables. One table is with master data
Table tbl1:
+-------------+------------+------------+
| ID | Name | Total |
+-------------+------------+------------+
| 1 | a | 10 |
| 2 | b | 5 |
| 3 | c | 4 |
| 4 | a | 4 |
+-------------+------------+------------+
Second table tbl2 contains child data. The key between tables is ID
Table tbl2:
+-------------+------------+
|id | qty |
+-------------+------------+
| 1 | 4 |
| 1 | 3 |
| 1 | 1 |
| 3 | 1 |
| 3 | 3 |
+-------------+------------+
I need to get output like this:
Output:
+-------------+------------+------------+
| name | sum_tot | sum_qty |
+-------------+------------+------------+
| a | 14 | 8 |
| b | 5 | 0 |
| c | 4 | 4 |
+-------------+------------+------------+
I had tried with this:
select tbl1.name, SUM(tbl1.total), SUM(tbl2.qty)
from tbl1
left join tbl2 ON tbl1.id = tbl2.id
GROUP by tbl1.name
The output that I get is:
Output:
+-------------+------------+------------+
| name | sum_tot | sum_qty |
+-------------+------------+------------+
| a | 34 | 8 |
| b | 5 |null |
| c | 8 | 4 |
+-------------+------------+------------+
Which is not correct.
Here is the sql fiddle:
The summary from first table is not in relation with second table. It seems that somehow query runs three times.
You can simply have a correlated sub-query that calculates the tbl2 sum:
select tbl1.name,
SUM(tbl1.total),
SUM(COALESCE((select SUM(tbl2.qty)
from tbl2
where tbl1.id = tbl2.id), 0)) as qty_tot
from tbl1
GROUP by tbl1.name
SELECT A.name, SUM(A.total) as sum_tot, COALESCE(B.sum_qty, 0) as sum_qty
FROM tbl1 A
LEFT JOIN (
SELECT id, SUM(qty) as sum_qty
FROM tbl2
GROUP BY id
) B ON B.id = A.id
GROUP BY A.name
select tbl1.name, SUM(tbl1.total), SUM(COALESCE(tbl2.qty, 0))
from tbl1
left join tbl2 ON tbl1.id = tbl2.id
GROUP by tbl1.name

Mysql count column values and merge columns

I was having problems in creating counting rows by grouping based on a given field value.
For example: I have a Table A structure like this:
+------+------------+
| id | Person |
+------+------------+
| 1 | "Sandy" |
| 2 | "Piper" |
| 3 | "Candy" |
| 4 | "Pendy" |
+------------+------+
Also I have a Table B structure like this:
+------+------------+---------+
| id | Person | Point |
+------+------------+---------+
| 1 | "Sandy" | 10 |
| 2 | "Piper" | 20 |
| 3 | "Candy" | 30 |
| 4 | "Sandy" | 10 |
| 5 | "Piper" | 20 |
| 6 | "Zafar" | 30 |
+------------+------+---------+
And needed a result like:
+------+------------+---------+
| id | Person | Point |
+------+------------+---------+
| 1 | "Piper" | 40 |
| 2 | "Candy" | 30 |
| 3 | "Zafar" | 30 |
| 4 | "Sandy" | 20 |
| 5 | "Pendy" | 0 |
+------------+------+---------+
I hope the table examples are itself self-explanatory.
SELECT person
, SUM(point) total
FROM
( SELECT person,point FROM table_b
UNION
ALL
SELECT person,0 FROM table_a
) x
GROUP
BY person
ORDER
BY total DESC;
It is a simple left join with a group by
select tableA.person, sum(tableB.points) from tableA left join tableB on tableA.person = tableB.person group by tableA.person
union
select tableB.person, sum(tableB.points) from tableB left join tableA on tableA.person = tableB.person where tableA.id is null group by tableA.person
I think below sql useful to you.
select a.id, a.Person,b.total_point from (
select id, Person from tablea) as a join
(select Person, sum(Point) as total_point from tableb group by person) as b on a.person =b.person
Thank you

One to many join but only the first row

I have 2 tables that I need to join via ID without getting the duplicate values For ID, InfoA, and InfoB. I do not need the data in column InfoB2. When I join the table on ID because it is a 1 to many join I end up with duplicate values and want to get rid of those. I only want ID, InfoA, and InfoB without the duplicates. Any ideas?
Example:
TableA:
| ID | InfoA |
| 1 | animals|
| 2 | plants |
TableB:
| ID | InfoB | InfoB2 |
| 1 | A | X |
| 1 | A | Y |
| 1 | A | Z |
| 2 | B | X |
| 2 | B | Y |
| 2 | B | Z |
Doing a normal join, because it is 1 to many I get this but do not want the duplicates. I don't want this:
| ID | InfoB | InfoB |
| 1 | animals| A |
| 1 | animals| A |
| 1 | animals| A |
| 2 | plants | B |
| 2 | plants | B |
| 2 | plants | B |
My goal is to get this (note I do not need column InfoB2):
| ID | InfoA | InfoB |
| 1 | animals| A |
| 2 | plants | B |
You could use the distinct keyword:
SELECT DISTINCT a.id, infoa, infob
FROM tablea a
JOIN tableb b ON a.id = b.id
The fastest way is likely to be:
select a.*,
(select b.infob from tableb b on a.id = b.id limit 1)
from tablea a;
For performance, you would want an index on tableb(id, infob).