是否可以在任何地方使用JOINS替换SQL中的子查询

时间:2016-01-21 02:32:31

标签: sql join

我听说有人说表连接可以在任何地方用来替换子查询。我在查询中测试了它,但发现只有在我使用子查询时才会检索到相应的数据集。我无法使用连接获得相同的数据集。我不确定我发现的是对的,因为我是RDBMS的新手,因此没有那么多经验。我将尝试绘制我正在试验的数据库的模式(用文字表示):

数据库有两个表:

Users ID ,姓名,城市)和友谊( ID Friend_ID

Goal:用户表用于存储简单的用户数据,友谊表用于表示用户之间的友谊。友谊表将列作为外键,引用Users.ID。表之间有多对多的关系。

问题:我必须检索所有用户的Users.ID和Users.Name,这些用户不是特定用户x的朋友,而是来自同一个城市(很像fb&#39的朋友建议系统)。 / p>

通过使用子查询,我能够实现这一目标。查询看起来像:

SELECT ID, NAME 
FROM USERS AS U
WHERE U.ID NOT IN (SELECT FRIENDS_ID
                   FROM FRIENDSHIP,
                        USERS
                   WHERE USERS.ID = FRIENDSHIP.ID AND USERS.ID = x)
  AND U.ID != x AND CITY LIKE '% A_CITY%';

示例条目:

Users

Id = 1姓名= Jon City =孟买

Id = 2 Name = Doe City = Mumbai

Id = 3姓名= Arun City =孟买

Id = 4姓名= Prakash City =德里

Friendship

Id = 1 Friends_Id = 2

Id = 2 Friends_Id = 1

Id = 2 Friends_Id = 3

Id = 3 Friends_Id = 2

我是否可以通过执行连接在单个查询中获取相同的数据集。怎么样?如果我的问题不明确,请告诉我。感谢。

注意:我通过指定两个表在子查询中使用了内连接:友谊,用户。省略Users表并从外部使用U会产生错误(但如果不使用表Users的别名,查询在语法上会变得正常,但此查询的结果包括具有多个用户的ID和用户名朋友,包括ID为x的用户。有趣,但不是问题的主题)。

3 个答案:

答案 0 :(得分:4)

对于not in,您可以使用left join并检查is null

select u.id, u.name 
from  Users u 
left join Friends f on u.id = f.id and f.friend_id = @person
where u.city like '%city%' and f.friend_id is null and u.id <> @person;

在某些情况下,您只能通过内部/左/右连接来解决问题,但您的情况不是其中之一。

请检查sql fiddle:http://sqlfiddle.com/#!9/1c5b1/14

另外还有关于您的注意事项:根据您使用的引擎,lateral加入或cross apply可以实现您尝试做的事情。

答案 1 :(得分:2)

您只能使用连接重写查询。诀窍是使用内部联接加入User表一次,以识别同一城市内的用户,并使用左联接和空检查引用友谊表以识别非朋友。

SELECT
     U1.ID,
     U1.Name
FROM
    USERS U1
INNER JOIN
    USERS U2
ON
     U1.CITY = U2.CITY
LEFT JOIN
     FRIENDSHIP F
ON
    U2.ID = F.ID AND
    U1.ID = F.FRIEND_ID
WHERE
     U2.id = X AND
     U1.ID <> U2.id AND
     F.id IS NULL

上述查询无法处理USER x的主键位于FRIENDSHIP表的FRIEND_ID列中的情况。我假设因为你的子查询版本没有处理这种情况,也许你为每个友谊创建了2行,或者友谊不是双向的。

答案 2 :(得分:-2)

在某些情况下,联接和子查询可用于实现类似的结果,但肯定不是全部。例如,带有子查询的查询无法实现与连接的对比:

SELECT ID, COLUMN1, COUNT(*) FROM MYTABLE
WHERE ID IN (
    SELECT DISTINCT ID FROM MYTABLE
    WHERE COLUMN2 NOT IN (VALUES1, VALUES2)
)
GROUP BY ID;

这只是一个例子,但有很多。

相反,如果不加入子查询,则无法从其他表中获取信息。

关于你的例子

SELECT ID, NAME FROM USERS AS U 
WHERE U.ID NOT IN (
    SELECT FRIENDS_ID FROM FRIENDSHIP, USERS 
    WHERE USERS.ID = FRIENDSHIP.ID AND USERS.ID = x) 
AND U.ID != x AND CITY LIKE '% A_CITY%';

这可以构造为:

select ID, NAME from users u
join FRIENDSHIP f        on f.ID = u.ID
where u.ID = x
and u.ID != y
and CITY like '%A_CITY';

我将你的第二个x改为y,所以不会引起混淆。

当然,如果FRIENDSHIP表中可能有多个结果,您可能还想要LEFT JOIN,即LEFT OUTER JOIN。