代码之家  ›  专栏  ›  技术社区  ›  Andrew Allison

SQL查找互惠关系

  •  2
  • Andrew Allison  · 技术社区  · 7 年前

    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
    
    5 回复  |  直到 7 年前
        1
  •  1
  •   Kamil Gosciminski    7 年前

    一个 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 部分。如果要查看某些数据,请删除 不同的 并添加 top N

    with questions as (
    ...
    )
    select top 3 ...
    
        2
  •  2
  •   Salman Arshad    7 年前

    最简单的查询形式(这样就不会超时查询1600万个问题)是:

    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
    

    结果:

        3
  •  1
  •   Community Mohan Dere    6 年前

    Salman A's answer ,改进了排序并添加了更多有用的列。

    结合中的查询 my other answer ,它显示了一些有趣的关系。

    See it in SEDE.

    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
    

    结果如下:

    results

        4
  •  0
  •   Xedni    7 年前

    我就这么办。以下是一些简化的数据:

    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 ,我们的出发点是一组有序的post所有者对( OwnerUserId )和公认的回答者( AcceptedAnswerUserId

    (虽然不是必需的,但您可以这样形象化设置)

    select distinct OwnerUserId, AcceptedAnswerUserId
    from #Posts
    

    现在我们要找到这个集合中所有这两个字段颠倒的条目。也就是说,如果一个帖子是另一个帖子的公认答案,那么这个帖子的所有者。如果一对是(1,2),我们就要找到(2,1)。

    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 on 条款。

    就我个人而言,我一直觉得有趣的是,在集合论中,SQL和关系代数是多么根深蒂固,然而在SQL中做这样基于集合的操作往往会感觉非常笨拙。主要是因为为了保持顺序的缺乏,您必须在单个列中表示集合成员。但是要比较SQL中的集合成员,需要将集合成员表示为单独的列。

        5
  •  0
  •   Community Mohan Dere    6 年前

    埃塔:哦。误读问题;Op想要 认可的 答案和以下是 任何 相互的回答(它很容易修改,但我对后者更感兴趣。)


    所以这个查询:

    1. 只有存在互惠关系时才返回任何行。
    2. 排除自我回答。
    3. SEDE's query parameters and magic columns 可用性。

    See it live in SEDE.

    -- 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
    

    它会产生如下结果(单击以获得更大的视图):

    results


    this SEDE query .