PostgreSQL 및 DEV 파고들기

오늘 일찍 나는 드래프트Community Wellness badge 풀 요청을 검토하고 있었습니다. 그리고 요즘 SQL에 깊이 빠져 있는 저는 이 논리를 만들기 위한 쿼리를 작성해 봐야겠다고 생각했습니다.

다음 쿼리는 사용자 ID와 오늘 이후로 사용자가 부정적인 중재자 반응이 없는 댓글을 두 개 이상 작성한 주를 찾습니다.
  • user_id : 사용자의 데이터베이스 ID입니다.
  • weeks_ago : 댓글을 그룹화하는 주 수(오늘부터)
  • number_of_comments_with_positive_reaction : weeks_ago에 대해 긍정적인 댓글이 몇 개 있었나요?

  • SELECT user_id,
        COUNT(user_id) as number_of_comments_with_positive_reaction,
        /* Get the number of weeks, since today for posts */
        (trunc((extract(epoch FROM (current_timestamp- created_at))) / 604800)) AS weeks_ago
    FROM comments
    /* Only select comments from the last 32 weeks that
       don't have a negative moderator reaction */
    INNER JOIN
        /* Find all comments in the last 32 weeks */
        (SELECT DISTINCT reactable_id
        FROM reactions
        WHERE reactable_type = 'Comment'
        AND created_at > (now() - interval '224' day)
        /* Omit any comments that got a negative moderator reaction */
        EXCEPT
          SELECT DISTINCT reactable_id
          FROM reactions
          WHERE reactable_type = 'Comment'
          AND created_at > (now() - interval '224' day)
          AND category IN ('thumbsdown', 'vomit')) AS positve_reactions
        ON comments.id = positve_reactions.reactable_id
    INNER JOIN
        /* Find the users who have at least two comments in the last week */
        (SELECT count(id) AS number_of_comments,
            user_id AS comment_counts_user_id
          FROM comments
          WHERE created_at >= (now() - interval '7' day)
          GROUP BY user_id) AS comment_counts
          ON comments.user_id = comment_counts_user_id
          AND comment_counts.number_of_comments > 1
    /* Don’t select anything older than 224 days ago, or 32 weeks ago */
    WHERE created_at > (now() - interval '224' day)
    GROUP BY user_id, weeks_ago
    


    위의 쿼리는 사용자 ID당 여러 행을 생성합니다. 괜찮습니다. 하지만 루프를 돌고 싶다면 일시적인 변수 마법을 터뜨려야 합니다.

    이걸 하나의 쿼리로 줄일 수 있을지 궁금합니다. Forem의 몇몇 사람들의 도움을 받아 다음과 같은 쿼리를 작성하여 해당 정보를 집계했습니다. 하지만 약간의 조립 작업이 필요합니다.

    열은 다음과 같습니다.
  • user_id : 사용자의 데이터베이스 ID입니다.
  • serialized_weeks_ago : 의견이 있는 주의 쉼표로 구분된 목록입니다.
  • weeks_ago_array : serialized_weeks_ago 의 문자열이 아닌 표현인 정수 배열입니다. 우리는 ActiveRecord가 이 정수 배열을 어떻게 처리하는지 알고 싶습니다. serialized_weeks_ago 의 간단한 버전입니다.
  • serialized_comment_counts : 쉼표로 구분된 댓글 수 목록입니다.
  • serialized_weeks_ago 의 첫 번째 숫자는 serialized_comment_counts 의 첫 번째 숫자에 매핑됩니다. 그리고 사용자당 하나의 행을 얻습니다.

    SELECT user_id,
           /* A comma separated string of "weeks_ago" */
           array_to_string(array_agg(weeks_ago), ',') AS serialized_weeks_ago,
           /* Will active record convert this to an array of integers? */
           array_agg(weeks_ago) AS weeks_ago_array,
           /* A comma separated string of comment counts.  The first value in this string
           happens on the week that is the first value in serialized_weeks_ago */
           array_to_string(array_agg(number_of_comments_with_positive_reaction), ',') AS serialized_comment_counts
    FROM
    /* This is the same query as the first example query */
    (SELECT user_id,
        COUNT(user_id) as number_of_comments_with_positive_reaction,
        /* Get the number of weeks, since today for posts */
        (trunc((extract(epoch FROM (current_timestamp- created_at))) / 604800)) AS weeks_ago
    FROM comments
    /* Only select comments from the last 32 weeks that
       don't have a negative moderator reaction */
    INNER JOIN
        /* Find all comments in the last 32 weeks */
        (SELECT DISTINCT reactable_id
        FROM reactions
        WHERE reactable_type = 'Comment'
        AND created_at > (now() - interval '224' day)
        /* Omit any comments that got a negative moderator reaction */
        EXCEPT
          SELECT DISTINCT reactable_id
          FROM reactions
          WHERE reactable_type = 'Comment'
          AND created_at > (now() - interval '224' day)
          AND category IN ('thumbsdown', 'vomit')) AS positve_reactions
        ON comments.id = positve_reactions.reactable_id
    INNER JOIN
        /* Find the users who have at least two comments in the last week */
        (SELECT count(id) AS number_of_comments,
            user_id AS comment_counts_user_id
          FROM comments
          WHERE created_at >= (now() - interval '7' day)
          GROUP BY user_id) AS comment_counts
          ON comments.user_id = comment_counts_user_id
          AND comment_counts.number_of_comments > 1
    /* Don’t select anything older than 224 days ago, or 32 weeks ago */
    WHERE created_at > (now() - interval '224' day)
    GROUP BY user_id, weeks_ago
    ) AS user_comment_counts_by_week GROUP BY user_id
    


    저는 이러한 PostgreSQL 접근 방식을 공유하고 싶습니다. 많은 작은 쿼리 실행을 우회하는 데 도움이 될 수 있기 때문입니다. 나는 또한 우리가 올바른 논리를 작성했고 그것이 충분히 성능이 있는지 확인하기 위해 두 사람과 짝을 이룰 기회가 있었습니다.

    좋은 웹페이지 즐겨찾기