Saturday, January 07, 2006

A little join problem

Today I found a question in a newsgroup asking for help with creating a join. This join should take a date out of table a and find the record in table b which has a date less than that of table a, but still closest to that date. The tables should be joined through the id column.

Here's my example data (I've changed it a bit from the original request):
mysql> SELECT * FROM a;
+-----+----+------------+
| row | id | d |
+-----+----+------------+
| 1 | 15 | 2005-12-25 |
| 2 | 20 | 2005-12-29 |
| 3 | 25 | 2006-01-12 |
| 4 | 30 | 2006-01-15 |
+-----+----+------------+
4 rows in set (0.00 sec)

mysql> SELECT * FROM b;
+-----+----+------------+
| row | id | d |
+-----+----+------------+
| 1 | 15 | 2005-12-19 |
| 2 | 15 | 2005-12-21 |
| 3 | 15 | 2005-12-27 |
| 4 | 20 | 2005-12-11 |
| 5 | 20 | 2005-12-30 |
| 6 | 20 | 2006-01-09 |
| 7 | 25 | 2005-12-31 |
| 8 | 25 | 2006-01-02 |
| 9 | 25 | 2006-01-12 |
| 10 | 25 | 2006-01-16 |
| 11 | 30 | 2006-01-01 |
| 12 | 30 | 2006-01-08 |
| 13 | 30 | 2006-01-13 |
+-----+----+------------+
13 rows in set (0.00 sec)

So for example row 1 from table a should refer to row 2 from table b, because the 21st December is closest, but still less, to 25th December; row 2 from table a should refer to row 4 a.s.o.

For a real MySQL prof this shouldn't be much of a problem, but I like it, because it includes a derived table that's included in a join. This is my solution to this problem:
mysql> SELECT c.row as row_a,
-> b.row as row_b,
-> c.max_d
-> FROM b INNER JOIN
-> (SELECT a.row, max(b.d) as max_d
-> FROM a INNER JOIN b
-> ON a.id = b.id AND b.d < a.d
-> GROUP BY a.row) AS c
-> ON b.d = c.max_d;
+-------+-------+------------+
| row_a | row_b | max_d |
+-------+-------+------------+
| 1 | 2 | 2005-12-21 |
| 2 | 4 | 2005-12-11 |
| 3 | 8 | 2006-01-02 |
| 4 | 13 | 2006-01-13 |
+-------+-------+------------+
4 rows in set (0.00 sec)

If the current date should be included, the only thing that's to be changed would be to alter the < sign to a <= operator. A little but fine query that shows the power of SQL.

No comments: