是否可以左联接两个表并让右表为每行提供不超过一次? [英] Is it possible to left join two tables and have the right table supply each row no more than once?

查看:60
本文介绍了是否可以左联接两个表并让右表为每行提供不超过一次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出此表结构:

Table A
ID    AGE    EDUCATION
1     23     3
2     25     6
3     22     5

Table B
ID    AGE    EDUCATION
1     26     4
2     24     6
3     21     3

我想找到两个年龄在2岁以下且教育程度在2岁以下的表之间的所有匹配项.但是,我不想从TableB中多次选择任何行. B中的每一行应选择0或1次,A中的每一行应选择一次或多次(标准左联接).

SELECT *
FROM TableA as A LEFT JOIN TableB as B ON 
    abs(A.age - B.age) <= 2 AND 
    abs(A.education - B.education) <= 2

A.ID    A.AGE    A.EDUCATION    B.ID    B.AGE   B.EDUCATION
1       23       3              3       21      3
2       25       6              1       26      4
2       25       6              2       24      6
3       22       5              2       24      6
3       22       5              3       21      3

如您所见,与整个结果集相比,输出中的最后两行具有重复的B.ID 2和3.我希望这些行作为A.ID = 3的单个空匹配返回,因为它们都与先前的A值匹配.

所需的输出:

(请注意,对于A.ID = 3,B中没有匹配项,因为B中的所有行均已连接到A中的行.)

A.ID    A.AGE    A.EDUCATION    B.ID    B.AGE   B.EDUCATION
1       23       3              3       21      3
2       25       6              1       26      4
2       25       6              2       24      6
3       22       5              null    null    null

我可以用一个简短的程序来做到这一点,但是我想使用SQL查询解决问题,因为它不适合我,而且我再也不会看到数据或操纵环境." >

有什么想法吗?谢谢

解决方案

正如@Joel Coehoorn先前所说,必须有一种机制可以选择从中滤除哪些对(a,b)和相同的(b).输出. SQL不能很好地允许您在多个匹配项时选择ONE行,因此需要创建数据透视查询,以在其中过滤掉不需要的记录.在这种情况下,可以通过将B的所有匹配ID减小为最小(或最大,这并不重要)来进行过滤,使用任何会从集合中返回一个值的函数,即min()和max ()最方便使用.一旦将结果简化为知道所关心的(a,b)对,然后针对该结果加入,以提取其余表数据.

select a.id a_id, a.age a_age, a.education a_e,
b.id b_id, b.age b_age, b.education b_e
from a left join
(
SELECT   
  a.id a_id, min(b.id) b_id from a,b where 
  abs(A.age - B.age) <= 2 AND 
  abs(A.education - B.education) <= 2
  group by a.id
) g on a.id = g.a_id
left join b on b.id = g.b_id;

Given this table structure:

Table A
ID    AGE    EDUCATION
1     23     3
2     25     6
3     22     5

Table B
ID    AGE    EDUCATION
1     26     4
2     24     6
3     21     3

I want to find all matches between the two tables where the age is within 2 and the education is within 2. However, I do not want to select any row from TableB more than once. Each row in B should be selected 0 or 1 times and each row in A should be selected one or more times (standard left join).

SELECT *
FROM TableA as A LEFT JOIN TableB as B ON 
    abs(A.age - B.age) <= 2 AND 
    abs(A.education - B.education) <= 2

A.ID    A.AGE    A.EDUCATION    B.ID    B.AGE   B.EDUCATION
1       23       3              3       21      3
2       25       6              1       26      4
2       25       6              2       24      6
3       22       5              2       24      6
3       22       5              3       21      3

As you can see, the last two rows in the output have duplicated B.ID of 2 and 3 when compared to the entire result set. I'd like those rows to return as a single null match with A.ID = 3 since they were both matched to previous A values.

Desired output:

(note that for A.ID = 3, there is no match in B because all rows in B have already been joined to rows in A.)

A.ID    A.AGE    A.EDUCATION    B.ID    B.AGE   B.EDUCATION
1       23       3              3       21      3
2       25       6              1       26      4
2       25       6              2       24      6
3       22       5              null    null    null

I can do this with a short program, but I'd like to solve the problem using a SQL query because it is not for me and I will not have the luxury of ever seeing the data or manipulating the environment.

Any ideas? Thanks

解决方案

As @Joel Coehoorn said earlier, there has to be a mechanism that selects which pairs of (a,b) with the same (b) are filtered out from the output. SQL is not great on allowing you to select ONE row when multiple match, so a pivot query needs to be created, where you filter out the records you don't want. In this case, filtering can be done by reducing all of match IDs of B as a smallest (or largest, it doesn't really matter), using any function that will return one value from a set, it's just min() and max() are most convenient to use. Once you reduced the result to know which (a,b) pairs you care about, then you join against that result, to pull out the rest of the table data.

select a.id a_id, a.age a_age, a.education a_e,
b.id b_id, b.age b_age, b.education b_e
from a left join
(
SELECT   
  a.id a_id, min(b.id) b_id from a,b where 
  abs(A.age - B.age) <= 2 AND 
  abs(A.education - B.education) <= 2
  group by a.id
) g on a.id = g.a_id
left join b on b.id = g.b_id;

这篇关于是否可以左联接两个表并让右表为每行提供不超过一次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆