我正在尝试使用Stack Exchange Data Explorer(SEDE)查找一种情况,其中Stack Overflow上的两个不同的用户已经接受了彼此的答案。例如:
Post A { Id: 1, OwnerUserId: "user1", AcceptedAnswerId: "user2" }
和
Post B { Id: 2, OwnerUserId: "user2", AcceptedAnswerId: "user1" }
我目前有一个查询,可以找到两个在问题上进行过合作的用户作为发问者-回答者,但它不能确定这种关系是否是对等的:
SELECT user1.Id AS User_1, user2.Id AS User_2
FROM Posts p
INNER JOIN Users user1 ON p.OwnerUserId = user1.Id
INNER JOIN Posts p2 ON p.AcceptedAnswerId = p2.Id
INNER JOIN Users user2 ON p2.OwnerUserId = user2.Id
WHERE p.OwnerUserId <> p2.OwnerUserId
AND p.OwnerUserId IS NOT NULL
AND p2.OwnerUserId IS NOT NULL
AND user1.Id <> user2.Id
GROUP BY user1.Id, user2.Id HAVING COUNT(*) > 1;
对于不熟悉该架构的任何人,都有两个这样的表:
Posts
--------------------------------------
Id int
PostTypeId tinyint
AcceptedAnswerId int
ParentId int
CreationDate datetime
DeletionDate datetime
Score int
ViewCount int
Body nvarchar (max)
OwnerUserId int
OwnerDisplayName nvarchar (40)
LastEditorUserId int
LastEditorDisplayName nvarchar (40)
LastEditDate datetime
LastActivityDate datetime
Title nvarchar (250)
Tags nvarchar (250)
AnswerCount int
CommentCount int
FavoriteCount int
ClosedDate datetime
CommunityOwnedDate datetime
和
Users
--------------------------------------
Id int
Reputation int
CreationDate datetime
DisplayName nvarchar (40)
LastAccessDate datetime
WebsiteUrl nvarchar (200)
Location nvarchar (100)
AboutMe nvarchar (max)
Views int
UpVotes int
DownVotes int
ProfileImageUrl nvarchar (200)
EmailHash varchar (32)
AccountId int
答案 0 :(得分:2)
最简单形式的查询(这样它就不会超时查询16M个问题)将是:
WITH accepter_acceptee(a, b) AS (
SELECT q.OwnerUserId, a.OwnerUserId
FROM Posts AS q
INNER JOIN Posts AS a ON q.AcceptedAnswerId = a.Id
WHERE q.PostTypeId = 1 AND q.OwnerUserId <> a.OwnerUserId
), collaborations(a, b, type) AS (
SELECT a, b, 'a accepter b' FROM accepter_acceptee
UNION ALL
SELECT b, a, 'a acceptee b' FROM accepter_acceptee
)
SELECT a, b, COUNT(*) AS [collaboration count]
FROM collaborations
GROUP BY a, b
HAVING COUNT(DISTINCT type) = 2
ORDER BY a, b
结果:
答案 1 :(得分:1)
使用Salman A's answer中的技术,改进了排序并添加了一些更有用的列。
结合my other answer中的查询,它显示了一些有趣的关系。
WITH QandA_users AS (
SELECT q.OwnerUserId AS userQ
, a.OwnerUserId AS userA
FROM Posts q
INNER JOIN Posts a ON q.AcceptedAnswerId = a.Id
WHERE q.PostTypeId = 1
),
pairsUnion (user1, user2, whoAnswered) AS (
SELECT userQ, userA, 'usr 2 answered'
FROM QandA_users
WHERE userQ <> userA
UNION ALL
SELECT userA, userQ, 'usr 1 answered'
FROM QandA_users
WHERE userQ <> userA
),
collaborators AS (
SELECT user1, user2, COUNT(*) AS [Reciprocations]
FROM pairsUnion
GROUP BY user1, user2
HAVING COUNT (DISTINCT whoAnswered) > 1
)
SELECT
'site://u/' + CAST(c.user1 AS NVARCHAR) + '|Usr ' + u1.DisplayName AS [User 1]
, 'site://u/' + CAST(c.user2 AS NVARCHAR) + '|Usr ' + u2.DisplayName AS [User 2]
, c.Reciprocations AS [Reciprocal Accptd posts]
, (SELECT COUNT(*) FROM QandA_users qau WHERE qau.userQ = c.user1) AS [Usr 1 Qstns wt Accptd]
, (SELECT COUNT(*) FROM QandA_users qau WHERE qau.userQ = c.user1 AND qau.userA = c.user2) AS [Accptd Ansr by Usr 2]
, (SELECT COUNT(*) FROM QandA_users qau WHERE qau.userA = c.user2) AS [Usr 2 Ttl Accptd Answrs]
FROM collaborators c
INNER JOIN Users u1 ON u1.Id = c.user1
INNER JOIN Users u2 ON u2.Id = c.user2
ORDER BY c.Reciprocations DESC
, u1.DisplayName
, u2.DisplayName
结果如下:
答案 2 :(得分:0)
这就是我要做的。以下是一些简化的数据:
if object_id('tempdb.dbo.#Posts') is not null drop table #Posts
create table #Posts
(
PostId char(1),
OwnerUserId int,
AcceptedAnswerUserId int
)
insert into #Posts
values
('A', 1, 2),
('B', 2, 1),
('C', 2, 3),
('D', 2, 4),
('E', 3, 1),
('F', 4, 1)
出于我们的目的,我们并不真正在意PostId
,而我们的出发点是一组成对的帖子拥有者(OwnerUserId
)和接受的答卷者({ {1}}。
(尽管不是必需的,您可以像这样可视化设置)
AcceptedAnswerUserId
现在,我们要查找该集合中所有具有相反两个字段的条目。即所有者(如果一个帖子是另一个帖子的已接受答案)。因此,在一对是(1,2)的情况下,我们想找到(2,1)。
我使用左联接进行了此操作,因此您可以看到它省略的行,但是将其更改为内部联接会将其限制为您所描述的集合。您可以随意获取信息(通过从帽子中挑选任何一列,或者如果您希望将它们放在一行中,则从其中一个表中返回两列)。
select distinct OwnerUserId, AcceptedAnswerUserId
from #Posts
编辑。如果要排除自己的答案,只需在select
u1.OwnerUserId,
u1.AcceptedAnswerUserId,
u2.OwnerUserId,
u2.AcceptedAnswerUserId
from #Posts u1
left outer join #Posts u2
on u1.AcceptedAnswerUserId = u2.OwnerUserId
and u1.OwnerUserId = u2.AcceptedAnswerUserId
子句中添加and u1.AcceptedAnswerUserId != u1.OwnerUserId
。
就个人而言,我总是觉得很有趣的是,SQL和关系代数在集合论中扎根多么深,但是在SQL中进行基于集合的操作却感觉很笨拙。通常是因为为了保持顺序的缺乏,您必须在单个列中表示集合成员。但是然后要比较SQL中的集合成员,您需要将集合成员表示为单独的列。
现在考虑一下,您如何将其扩展到对同一则帖子发表评论的三合会用户?
答案 3 :(得分:0)
一个CTE
和简单的inner joins
就可以了。正如我在其他答案中观察到的那样,不需要那么多的代码。请注意我的很多评论。
链接到 StackExchange Data Explorer 并保存示例结果
with questions as ( -- this is needed so that we have ids of users asking and answering
select
p1.owneruserid as question_userid
, p2.owneruserid as answer_userid
--, p1.id -- to view sample ids
from posts p1
inner join posts p2 on -- to fetch answer post
p1.acceptedanswerid = p2.id
)
select distinct -- unique pairs
q1.question_userid as userid1
, q1.answer_userid as userid2
--, q1.id, q2.id -- to view sample ids
from questions q1
inner join questions q2 on
q1.question_userid = q2.answer_userid -- accepted answer from someone
and q1.answer_userid = q2.question_userid -- who also accepted our answer
and q1.question_userid <> q1.answer_userid -- and we aren't self-accepting
以帖子为例:
尽管如此,由于大型数据集和distinct
部分,StackExchange可能会使您超时。如果您想查看一些数据,请删除distinct
并在开始时添加top N
:
with questions as (
...
)
select top 3 ...
答案 4 :(得分:0)
ETA:糟糕。误解了问题; Op想要 Accepted 答案,而以下是任何互惠答案。 (修改起来很容易,但我还是对后者更感兴趣。)
由于数据集非常大(并且需要不使SEDE超时),我选择限制AMAP集并从那里构建。
所以这个查询:
-- UserA: Enter ID of user A
-- UserB: Enter ID of user B
WITH possibleAnswers AS (
SELECT
a.Id AS aId
, a.ParentId AS qId
, a.OwnerUserId
, a.CreationDate
FROM Posts a
WHERE a.PostTypeId = 2 -- answers
AND a.OwnerUserId IN (##UserA:INT##, ##UserB:INT##)
),
possibleQuestions AS (
SELECT
q.Id AS qId
, q.OwnerUserId
, q.Tags
FROM Posts q
INNER JOIN possibleAnswers pa ON q.Id = pa.qId
WHERE q.PostTypeId = 1 -- questions
AND q.OwnerUserId IN (##UserA:INT##, ##UserB:INT##)
AND q.OwnerUserId != pa.OwnerUserId -- No self answers
)
SELECT
pa.OwnerUserId AS [User Link]
, 'answers' AS [Action]
, pq.OwnerUserId AS [User Link]
, pa.CreationDate AS [at]
, pq.qId AS [Post Link]
, pq.Tags
FROM possibleQuestions pq
INNER JOIN possibleAnswers pa ON pq.qId = pa.qId
WHERE pq.OwnerUserId = ##UserB:INT##
AND EXISTS (SELECT * FROM possibleQuestions pq2 WHERE pq2.OwnerUserId = ##UserA:INT##)
UNION ALL SELECT
pa.OwnerUserId AS [User Link]
, 'answers' AS [Action]
, pq.OwnerUserId AS [User Link]
, pa.CreationDate AS [at]
, pq.qId AS [Post Link]
, pq.Tags
FROM possibleQuestions pq
INNER JOIN possibleAnswers pa ON pq.qId = pa.qId
WHERE pq.OwnerUserId = ##UserA:INT##
AND EXISTS (SELECT * FROM possibleQuestions pq2 WHERE pq2.OwnerUserId = ##UserB:INT##)
ORDER BY pa.CreationDate
它产生的结果如下(单击以查看大图):