鉴于此表结构:
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查询来解决这个问题,因为它不适合我,我不会有奢侈品看到数据或操纵环境。
有什么想法吗?感谢
答案 0 :(得分:2)
正如@Joel Coehoorn先前所说,必须有一种机制可以选择从输出中滤出哪些(a,b)与(b)相同的对。 SQL允许您在多个匹配时选择一行,因此不太好,因此需要创建一个数据透视查询,过滤掉您不想要的记录。在这种情况下,可以通过将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;
答案 1 :(得分:1)
据我所知,使用简单的select语句和连接是不可能的,因为你需要知道已经选择了什么以消除重复。
你可以尝试更多这样的东西:
DECLARE @JoinResults TABLE
(A_ID INT, A_Age INT, A_Education INT, B_ID INT, B_Age INT, B_Education INT)
INSERT INTO @JoinResults (A_ID, A_Age, A_Education)
SELECT ID, AGE, EDUCATION
FROM TableA
DECLARE @i INT
SET @i = 1
--Assume that A_ID is incremental and no values missed
WHILE (@i < (SELECT Max(A_ID) FROM @JoinResults
BEGIN
UPDATE @JoinResult
SET B_ID = SQ.ID,
B_Age = SQ.AGE,
B_Education = SQ.Education
FROM (
SELECT ID, AGE, EDUCATION
FROM TableB b
WHERE (
abs((SELECT A_Age FROM @JoinResult WHERE A_Id = @i) - AGE) <=2
AND abs((SELECT A_Education FROM @JoinResult WHERE A_Id = @i) - EDUCATION) <=2
) AND (SELECT B_ID FROM @JoinResults WHERE B_ID = b.id) IS NULL
) AS SQ
SET @i = @i + 1
END
SELECT @JoinResults
注意:我目前无法访问数据库,所以这是未经测试的,我厌倦了2个潜在的问题
如果出现这些问题,请告诉我,我可以帮您解决问题。
答案 2 :(得分:1)
在SQL-Server中,您可以使用CROSS APPLY
语法:
SELECT
a.id, a.age, a.education,
b.id AS b_id, b.age AS b_age, b.education AS b_education
FROM tableB AS b
CROSS APPLY
( SELECT TOP (1) a.*
FROM tableA AS a
WHERE ABS(a.age - b.age) <= 2
AND ABS(a.education - b.education) <= 2
ORDER BY a.id -- your choice here
) AS a ;
根据您在子查询中选择的顺序,将选择tableA
中的不同行。
编辑(更新后):但上述查询不会显示A中没有匹配行的行,甚至是某些尚未选中的行。
也可以使用窗口函数完成,但Access没有它们。这是一个我认为可以在Access中使用的查询:
SELECT
a.id, a.age, a.education,
s.id AS s_id, s.age AS b_age, s.education AS b_education
FROM tableB AS a
LEFT JOIN
( SELECT
b.id, b.age, b.education, MIN(a.id) AS a_id
FROM tableB AS b
JOIN tableA AS a
ON ABS(a.age - b.age) <= 2
AND ABS(a.education - b.education) <= 2
GROUP BY b.id, b.age, b.education
) AS s
ON a.id = s.a_id ;
我不确定Access是否允许这样的子查询,但如果没有,则可以将其定义为“查询”,然后在另一个中使用它。
答案 3 :(得分:0)
使用SELECT DISTINCT
SELECT DISTINCT A.id, A.age, A.education, B.age, B.education
FROM TableA as A LEFT JOIN TableB as B ON
abs(A.age - B.age) <= 2 AND
abs(A.education - B.education) <= 2