SQL 쿼리 작성에 대한 두 번째 연습

Forem 인스턴스를 쿼리할 때 내 작업 표시



이것은 내 .

내가 작성할 쿼리는 활성 사용자 중 몇 퍼센트가 적어도 하나의 환영 기사에 댓글을 달았는지 답하는 데 도움이 됩니다. 이 쿼리에서 활성 사용자는 지난 7일 중 4일 동안 사이트에 있었던 사람입니다.

엔터티 관계 모델 설정



다시 말하지만 SQL을 작성할 때 관계 다이어그램으로 시작하는 것을 좋아합니다. 이 데이터 수집에는 4개의 관련 테이블이 있습니다.

class User
  has_many :articles
  has_many :comments
end

class Article
  belongs_to :user
  has_many :comments, as: :commentable
end

class Comment
  belongs_to :commentable, polymorphic: true
  belongs_to :user
end

class PageView
  belongs_to :article
  belongs_to :user, optional: true
end



아래는 네 가지 데이터 모델 중 엔터티 관계 모델(ERM)을 선호하는 사용자를 위한 다이어그램입니다.

이러한 쿼리에 사용된 테이블의 ERM입니다.



모든 활성 구성원 쿼리



지난 7일 중 최소 4일 동안 page_views을(를) 보유한 모든 사용자를 원합니다. 이러한 "활성 사용자"를 고려할 것입니다.

먼저 매우 좁은 쿼리를 작성하고 싶습니다. 내가 올바른 길을 가고 있다는 것을 확신하게 해주는 것. 페이지 조회수를 내user_id로 제한합니다.

SELECT user_id, extract(isodow from created_at) AS day_of_week
FROM page_views
WHERE page_views.user_id = 702612
      AND page_views.created_at > CURRENT_DATE - 7
GROUP BY page_views.user_id, day_of_week


다음 쿼리는 시간 초과되었습니다. 모든 사용자에게 쿼리하려고 합니다.

SELECT dow.user_id,
       count(dow.day_of_week) AS number_of_days
FROM (
  SELECT user_id,
         extract(isodow from created_at) AS day_of_week
  FROM page_views
  WHERE page_views.created_at > CURRENT_DATE - 7
    AND user_id IS NOT NULL
  GROUP BY page_views.user_id, day_of_week
) AS dow
GROUP BY dow.user_id
HAVING count(dow.day_of_week) >= 4


페이지 조회수가 엄청나기 때문에 최근에 업데이트된 사용자로만 제한해야 합니다. 다음은 최근 사용자를 가져오는 쿼리입니다.

SELECT id
FROM users
WHERE updated_at
      > CURRENT_DATE - 7


다음 쿼리는 "Forem의 현재 활성 사용자는 누구입니까?"라는 기본 질문입니다.

SELECT DISTINCT dow.user_id FROM (
  SELECT users.id AS user_id,
    extract(isodow from page_views.created_at) AS day_of_week
  FROM users
  INNER JOIN page_views
    ON page_views.user_id = users.id
    AND page_views.created_at > CURRENT_DATE - 7
    AND user_id IS NOT NULL
  -- Extend the window for users just a bit to account --
  -- for timing variance --
  WHERE users.updated_at
        >  NOW()::date - INTERVAL '8 day'
  GROUP BY users.id, day_of_week) AS dow
GROUP BY dow.user_id
HAVING count(dow.day_of_week) >= 4


위의 쿼리를 에 "저장"했습니다. 이제 "현재 DEV.to의 활성 사용자"쿼리가 있습니다.

환영 게시물에 댓글을 단 활성 사용자 쿼리



다음 부분은 환영 게시물에 누가 모두 댓글을 달았는지 알아내는 것입니다. 에서 환영 기사에 댓글을 단 사용자 찾기에 대해 썼습니다.

그러나 코호트 쿼리를 조정해야 합니다. 환영 게시물에 댓글을 단 사용자 만 원합니다. 코호트 쿼리에는 환영 게시물에 댓글을 달거나 댓글을 달지 않은 사용자가 있습니다.

참고로 다음 쿼리의 결과는 환영 게시물에 댓글을 단 모든 사람user_id입니다. 그러나 사용자의 제한이 있음updated_at
SELECT DISTINCT comments.user_id AS user_id
FROM comments
INNER JOIN users
  ON comments.user_id = users.id
    -- Extend the window for users just a bit to --
    -- account for timing variance --
    AND users.updated_at > CURENT_DATE - 8
INNER JOIN articles
  ON comments.commentable_id = articles.id
    AND comments.commentable_type = 'Article'
    AND articles.title LIKE 'Welcome Thread - v%'
    AND articles.published = true
    AND articles.user_id = 3
GROUP BY comments.user_id


이제 두 쿼리를 병합합니다. PostgresqlWITH 문을 사용하여 나중에 참조할 수 있는 두 개의 쿼리를 생성하고 있습니다. 쿼리를 "캡슐화"하는 데 도움이 되는 WITH 문을 찾았고 쿼리를 개념적으로 더 이해하기 쉽게 만들 수 있기를 바랍니다.

WITH cow AS (
  -- User IDs of recent folks who have commented on the
  -- welcome threads --
  SELECT DISTINCT comments.user_id AS user_id
  FROM comments
  INNER JOIN users ON comments.user_id = users.id
    -- Extend the window for users just a bit to account --
    -- for timing variance --
    AND users.updated_at > CURENT_DATE - 8
  INNER JOIN articles
    ON comments.commentable_id = articles.id
    AND comments.commentable_type = 'Article'
    AND articles.title LIKE 'Welcome Thread - v%'
    AND articles.published = true
    AND articles.user_id = 3
  GROUP BY comments.user_id
), dow AS (
  -- User IDs of folks who have interacted at least 4 different
  -- days of this week --
  SELECT user_id FROM (
    SELECT users.id AS user_id,
      extract(isodow from page_views.created_at) AS day_of_week
    FROM users
    INNER JOIN page_views
      ON page_views.user_id = users.id
      AND page_views.created_at > CURRENT_DATE - 7
      AND user_id IS NOT NULL
    -- Extend the window for users just a bit to account for
    -- timing variance --
    WHERE users.updated_at > CURRENT_DATE - 8
    GROUP BY users.id, day_of_week
  ) AS dows
  GROUP BY user_id
  HAVING COUNT(day_of_week) >= 4
)

SELECT COUNT(dow.user_id) AS count_of_users,
  (
    SELECT COUNT(*)
    FROM dow
    INNER JOIN cow
      ON cow.user_id = dow.user_id
  ) AS count_of_users_that_said_hello
FROM dow


결론



마지막 쿼리를 작성할 때 사이트의 모든 활성 사용자가 환영 기사에 댓글을 달았다는 결과를 계속 얻었습니다. 나는 그 결과를 믿지 않았다. 가능성이 매우 희박해 보였습니다. 내 쿼리와 논리를 재검토하고 내 오류를 발견하고 쿼리를 재작업하여 보다 합리적인 답변을 얻었습니다.

무엇이 잘못되었나요? 에서 쿼리를 복사하여 붙여넣었습니다. 하지만 그 질문은 올바른 질문이 아니었습니다. SQL의 한 가지 문제를 강조합니다. 쿼리의 정확성을 테스트하기 어려울 수 있습니다.

좋은 웹페이지 즐겨찾기