Reputation: 13
I am trying to join two tables like these:
Table A
A1, A2, A3
A, 1, 35
A, 1, 70
A, 1, 105
Table B
B1,B2, B3
B, 1, 30
C, 1, 32
D, 1, 40
E, 1, 55
F, 1, 60
G, 1, 77
H, 1, 80
The expected result is like this:
A, 1, 35, C, 32
A, 1, 70, F, 60
A, 1, 105, H, 80
That is, for every row in table A, I want to pick just one row from table B, which should be the row with the value of B3 smaller, but closest to the value of A3.
So far, I have tried the following query:
SELECT A1, A2, A3, B1, B3 FROM A JOIN (SELECT B1, B2, B3 FROM B ORDER BY B2, B3 DESC) AS A ON A.A2 = B.B2 AND A.B3 < A.A3
However, this results in a table as follows:
A, 1, 35, B, 30
A, 1, 35, C, 32
A, 1, 70, B, 30
A, 1, 70, C, 32
A, 1, 70, D, 40
A, 1, 70, E, 55
and so forth. I also tried to add a LIMIT 1 to the inner SELECT, but that does not produce any results. If I run the inner query by itself (with LIMIT 1), I do get the expected result. How can I change the query to produce the desired result? I am using sqlite at the moment, but would like to write this in a way that is portable to other DBMS.
Upvotes: 1
Views: 157
Reputation: 8850
I don't have SQLite available, but the following works in SQL Server; you should be able to replace TOP with LIMIT.
You should be able to get it from a nested query in the select, as long as you specify TOP 1:
select *,
(select top 1 B3
from B
where B3 <= A3
order by B3 desc)
from A
EDIT: To add the extra column from B requires you to nest the above query into a subquery:
select t.*, B.B1 from (
select *,
(select top 1 B3
from B
where B3 <= A3
order by B3 desc) as B3
from A
) as t join B on B.B3 = t.B3
Upvotes: 0
Reputation: 4412
Try the following query:
SELECT
a.A1,
a.A2,
a.A3,
(
SELECT b.B3
FROM TableB AS b
ORDER BY ABS(a.A3 - b.B3)
LIMIT 1
) AS x
FROM TableA AS a;
SQL Fiddle wouldn't let me use sqlite cause my browser doesn't support it, so I hope this works for you.
EDIT: This should actually give you the closest value. 77 is closer to 70 than 60... but I don't know if this is exactly what you wanted.
Upvotes: 1
Reputation: 1270091
When you want exactly one row from the initial table, then you can think about using a subquery in the select
clause:
select a.*,
(select b3
from TableB b
where b.b3 <= a.a3
order by b3 desc
limit 1
) as b3
from TableA a
The subquery itself is called a "correlated subquery", because the where
clause references both the inner table (b
) and the outer table (a
). It orders the table b results by b3, filtering out all but the values less than or equal to a.a3
in each row. The limit 1
returns a single value, which because of the sort is the closest value less than or equal to the value in a.a3
.
Your question says the closest value, but the example results have the greatest value less than or equal to.
Upvotes: 1