我正在使用SQL Server來測試它,因爲我手邊只有SQL Server,但它應該直接轉換爲Oracle語法。
我已經使用SQL Fiddle將它轉換爲Oracle,但我以前從未見過Oracle。查看底部的最終查詢。
的樣本數據
DECLARE @USERS TABLE (ID int, NAME nvarchar(255));
DECLARE @FRIEND TABLE (ID1 int, ID2 int);
INSERT INTO @USERS (ID, NAME) VALUES (1, 'Jimmy');
INSERT INTO @USERS (ID, NAME) VALUES (2, 'Sam');
INSERT INTO @USERS (ID, NAME) VALUES (3, 'Alice');
INSERT INTO @USERS (ID, NAME) VALUES (4, 'Tom');
INSERT INTO @FRIEND (ID1, ID2) VALUES (1,2);
INSERT INTO @FRIEND (ID1, ID2) VALUES (1,3);
INSERT INTO @FRIEND (ID1, ID2) VALUES (4,2);
INSERT INTO @FRIEND (ID1, ID2) VALUES (4,3);
用戶的雙
我們需要對用戶的。這由CROSS JOIN
完成。 CROSS JOIN
會返回兩倍的行數,因爲我們需要(1,2) and (2,1)
,但我們只需要其中的一個,所以我們將添加用戶ID
的過濾器。
WITH
CTE_Pairs
AS
(
SELECT
U1.ID AS ID1
,U2.ID AS ID2
FROM
@USERS AS U1
CROSS JOIN @USERS AS U2
WHERE
U1.ID > U2.ID
)
SELECT *
FROM CTE_Pairs;
結果集:
ID1 ID2
2 1
3 1
4 1
3 2
4 2
4 3
對,不是朋友
一旦我們擁有所有對我們應該刪除那些朋友已經對。表FRIEND
可以列出一對爲(1,2)
或(2,1)
,所以我們應該檢查兩種可能性。我們將使用EXCEPT
來「減去」這些行。
....
,CTE_PairsNonFriends
AS
(
SELECT ID1, ID2
FROM CTE_Pairs
EXCEPT
SELECT ID1, ID2
FROM @FRIEND
EXCEPT
SELECT ID2, ID1
FROM @FRIEND
)
SELECT *
FROM CTE_PairsNonFriends;
結果集:
所選用戶
的
ID1 ID2
3 2
4 1
朋友,我們有對的最終名單。對於每個用戶,我們需要獲得他的直接朋友列表。簡單的join
就夠了。再次表friend
可以有(1,2)
或(2,1)
,所以我們需要做兩次。我們首先爲用戶ID1
執行此操作,然後分別爲用戶ID2
執行此操作。
....
,CTE_FriendsOfUser1
AS
(
SELECT
CTE_PairsNonFriends.ID1 AS IDUser1
,F1.ID2 AS FriendOfUser1
FROM
CTE_PairsNonFriends
INNER JOIN @FRIEND AS F1 ON F1.ID1 = CTE_PairsNonFriends.ID1
UNION -- sic! not ALL
SELECT
CTE_PairsNonFriends.ID1 AS IDUser1
,F1.ID1 AS FriendOfUser1
FROM
CTE_PairsNonFriends
INNER JOIN @FRIEND AS F1 ON F1.ID2 = CTE_PairsNonFriends.ID1
)
,CTE_FriendsOfUser2
AS
(
SELECT
CTE_PairsNonFriends.ID2 AS IDUser2
,F1.ID2 AS FriendOfUser2
FROM
CTE_PairsNonFriends
INNER JOIN @FRIEND AS F1 ON F1.ID1 = CTE_PairsNonFriends.ID2
UNION -- sic! not ALL
SELECT
CTE_PairsNonFriends.ID2 AS IDUser2
,F1.ID1 AS FriendOfUser2
FROM
CTE_PairsNonFriends
INNER JOIN @FRIEND AS F1 ON F1.ID2 = CTE_PairsNonFriends.ID2
)
結果集:
SELECT * FROM CTE_FriendsOfUser1
IDUser1 FriendOfUser1
4 2
4 3
3 1
3 4
SELECT * FROM CTE_FriendsOfUser2
IDUser2 FriendOfUser2
1 2
1 3
2 1
2 4
共同的朋友
join
USER1與他們的好友列表上的用戶2找出他們共同的朋友。與用戶名
....
,CTE_MutualFriends
AS
(
SELECT *
FROM
CTE_FriendsOfUser1
INNER JOIN CTE_FriendsOfUser2 ON CTE_FriendsOfUser2.FriendOfUser2 = CTE_FriendsOfUser1.FriendOfUser1
WHERE
CTE_FriendsOfUser1.IDUser1 <> CTE_FriendsOfUser2.IDUser2
)
計數共同的朋友
,CTE_FriendCount
AS
(
SELECT
IDUser1
,IDUser2
,COUNT(*) AS FriendCount
FROM CTE_MutualFriends
GROUP BY IDUser1, IDUser2
)
最終全場查詢
結果排序的好友數。您只能返回第一行(或前幾行)返回共同朋友數最多的用戶。其實,它應該由TOP
與領帶。
WITH
CTE_Pairs
AS
(
SELECT
U1.ID AS ID1
,U2.ID AS ID2
FROM
@USERS AS U1
CROSS JOIN @USERS AS U2
WHERE
U1.ID > U2.ID
)
,CTE_PairsNonFriends
AS
(
SELECT ID1, ID2
FROM CTE_Pairs
EXCEPT
SELECT ID1, ID2
FROM @FRIEND
EXCEPT
SELECT ID2, ID1
FROM @FRIEND
)
,CTE_FriendsOfUser1
AS
(
SELECT
CTE_PairsNonFriends.ID1 AS IDUser1
,F1.ID2 AS FriendOfUser1
FROM
CTE_PairsNonFriends
INNER JOIN @FRIEND AS F1 ON F1.ID1 = CTE_PairsNonFriends.ID1
UNION -- sic! not ALL
SELECT
CTE_PairsNonFriends.ID1 AS IDUser1
,F1.ID1 AS FriendOfUser1
FROM
CTE_PairsNonFriends
INNER JOIN @FRIEND AS F1 ON F1.ID2 = CTE_PairsNonFriends.ID1
)
,CTE_FriendsOfUser2
AS
(
SELECT
CTE_PairsNonFriends.ID2 AS IDUser2
,F1.ID2 AS FriendOfUser2
FROM
CTE_PairsNonFriends
INNER JOIN @FRIEND AS F1 ON F1.ID1 = CTE_PairsNonFriends.ID2
UNION -- sic! not ALL
SELECT
CTE_PairsNonFriends.ID2 AS IDUser2
,F1.ID1 AS FriendOfUser2
FROM
CTE_PairsNonFriends
INNER JOIN @FRIEND AS F1 ON F1.ID2 = CTE_PairsNonFriends.ID2
)
,CTE_MutualFriendsRaw
AS
(
SELECT
CTE_FriendsOfUser1.FriendOfUser1 AS MutualFriend
,IDUser1
,IDUser2
FROM
CTE_FriendsOfUser1
INNER JOIN CTE_FriendsOfUser2 ON CTE_FriendsOfUser2.FriendOfUser2 = CTE_FriendsOfUser1.FriendOfUser1
WHERE
CTE_FriendsOfUser1.IDUser1 <> CTE_FriendsOfUser2.IDUser2
)
,CTE_MutualFriends
AS
(
SELECT DISTINCT
MutualFriend
,CASE WHEN IDUser1 < IDUser2 THEN IDUser1 ELSE IDUser2 END AS IDUser1
,CASE WHEN IDUser1 < IDUser2 THEN IDUser2 ELSE IDUser1 END AS IDUser2
FROM
CTE_MutualFriendsRaw
)
,CTE_FriendCount
AS
(
SELECT
IDUser1
,IDUser2
,COUNT(*) AS FriendCount
FROM CTE_MutualFriends
GROUP BY IDUser1, IDUser2
)
SELECT
CTE_FriendCount.IDUser1
,CTE_FriendCount.IDUser2
,CTE_FriendCount.FriendCount
,U1.NAME AS Name1
,U2.NAME AS Name2
FROM
CTE_FriendCount
INNER JOIN @USERS AS U1 ON U1.ID = CTE_FriendCount.IDUser1
INNER JOIN @USERS AS U2 ON U2.ID = CTE_FriendCount.IDUser2
ORDER BY FriendCount DESC
;
結果集:
IDUser1 IDUser2 FriendCount Name1 Name2
4 1 2 Tom Jimmy
3 2 2 Alice Sam
可能有CTE_MutualFriends
一個問題。同樣的問題,一對可以列爲(1,2)
或(2,1)
。例如,您可以將(a,b)
與NN
和配對MM
。嚴格地說,應該有另一個步驟來尋找這樣的對並將它們結合在一起。我不確定是否使用當前查詢這樣的對可能或不可以。
There is原始版本CTE_MutualFriends
存在問題,所以我添加了額外的步驟以消除查詢的最終完整版本中的重複項。給定的示例數據太小,並且很容易具有所有可能的變體,因此版本版本提供了正確的結果。如果我們在示例數據中添加更多條目,我們會看到需要額外的步驟。
甲骨文語法版本
經過與http://sqlfiddle.com/#!4/48e1f/21/0
WITH
CTE_Pairs
AS
(
SELECT
U1.ID ID1
,U2.ID ID2
FROM
USERS U1
CROSS JOIN USERS U2
WHERE
U1.ID > U2.ID
)
,CTE_PairsNonFriends
AS
(
SELECT ID1, ID2
FROM CTE_Pairs
MINUS
SELECT ID1, ID2
FROM FRIEND
MINUS
SELECT ID2, ID1
FROM FRIEND
)
,CTE_FriendsOfUser1
AS
(
SELECT
CTE_PairsNonFriends.ID1 IDUser1
,F1.ID2 FriendOfUser1
FROM
CTE_PairsNonFriends
INNER JOIN FRIEND F1 ON F1.ID1 = CTE_PairsNonFriends.ID1
UNION
SELECT
CTE_PairsNonFriends.ID1 IDUser1
,F1.ID1 FriendOfUser1
FROM
CTE_PairsNonFriends
INNER JOIN FRIEND F1 ON F1.ID2 = CTE_PairsNonFriends.ID1
)
,CTE_FriendsOfUser2
AS
(
SELECT
CTE_PairsNonFriends.ID2 IDUser2
,F1.ID2 FriendOfUser2
FROM
CTE_PairsNonFriends
INNER JOIN FRIEND F1 ON F1.ID1 = CTE_PairsNonFriends.ID2
UNION
SELECT
CTE_PairsNonFriends.ID2 IDUser2
,F1.ID1 FriendOfUser2
FROM
CTE_PairsNonFriends
INNER JOIN FRIEND F1 ON F1.ID2 = CTE_PairsNonFriends.ID2
)
,CTE_MutualFriendsRaw
AS
(
SELECT
CTE_FriendsOfUser1.FriendOfUser1 MutualFriend
,IDUser1
,IDUser2
FROM
CTE_FriendsOfUser1
INNER JOIN CTE_FriendsOfUser2 ON CTE_FriendsOfUser2.FriendOfUser2 = CTE_FriendsOfUser1.FriendOfUser1
WHERE
CTE_FriendsOfUser1.IDUser1 <> CTE_FriendsOfUser2.IDUser2
)
,CTE_MutualFriends
AS
(
SELECT DISTINCT
MutualFriend
,CASE WHEN IDUser1 < IDUser2 THEN IDUser1 ELSE IDUser2 END IDUser1
,CASE WHEN IDUser1 < IDUser2 THEN IDUser2 ELSE IDUser1 END IDUser2
FROM
CTE_MutualFriendsRaw
)
,CTE_FriendCount
AS
(
SELECT
IDUser1
,IDUser2
,COUNT(*) FriendCount
FROM CTE_MutualFriends
GROUP BY IDUser1, IDUser2
)
SELECT
CTE_FriendCount.IDUser1
,CTE_FriendCount.IDUser2
,CTE_FriendCount.FriendCount
,U1.NAME Name1
,U2.NAME Name2
FROM
CTE_FriendCount
INNER JOIN USERS U1 ON U1.ID = CTE_FriendCount.IDUser1
INNER JOIN USERS U2 ON U2.ID = CTE_FriendCount.IDUser2
ORDER BY FriendCount DESC
;
_most共享的朋友,雖然他們不是朋友_我不明白這一點。誰不是朋友?你能澄清這一點,也許增加一些樣本輸入/輸出數據? – jpw 2015-02-08 20:09:38
@jpw我認爲OP是在擁有最多共同朋友的人之後,然而他們本身並不是彼此的朋友。 – Mez 2015-02-08 20:14:43
看看這個鏈接:http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=72097 – Mez 2015-02-08 20:16:31