Предотвращение дублирования значений в нескольких соединениях одной таблицы - PullRequest
0 голосов
/ 19 июня 2020

У меня есть две таблицы в моем проекте, которые мне нужно соединить несколько сложным образом, и это вызывает у меня очень странные проблемы.

У меня есть концепция команд и концепция FeedItems. FeedItems означает, что команда решила задачу. Мне нужно знать, когда они в последний раз решали задачу, и мне также нужно вычислить сумму элементов FeedItem на основе точек.

SELECT COALESCE(sum(challenges.point_value), 0) + COALESCE(sum(point_feed_items.point_value), 0) as team_score, 
GREATEST(MAX(pentest_feed_items.created_at), MAX(point_feed_items.created_at)) as last_solve_time, teams.* FROM "teams" 
    LEFT JOIN feed_items AS point_feed_items
             ON point_feed_items.team_id = teams.id
             AND point_feed_items.type IN ('StandardSolvedChallenge', 'ScoreAdjustment')
    LEFT JOIN feed_items AS pentest_feed_items
             ON pentest_feed_items.team_id = teams.id
             AND pentest_feed_items.type IN ('PentestSolvedChallenge')
    LEFT JOIN challenges ON challenges.id = point_feed_items.challenge_id
             AND challenges.type IN ('StandardChallenge') WHERE "teams"."division_id" = $1
    GROUP BY teams.id ORDER BY "teams"."created_at" ASC

Это работает почти всегда, я просто сталкиваюсь с краем случай, когда я иногда получаю то же самое ScoreAdjustment в сумме point_feed_items.point_value. Я позвонил по номеру COUNT(point_feed_items.point_value) и подтвердил, что мне каким-то образом возвращаются 3 элемента, хотя их должно быть только 1. Я до сих пор не мог понять, почему один и тот же элемент иногда возвращается несколько раз, или как вызвать DISTINCT как часть LEFT JOIN, чтобы полностью избежать проблемы.

Я обнаружил, что удаление 2-го LEFT JOIN устранило проблему, однако мне нужны данные из этого LEFT JOIN. задать другой способ, я заменил COALESCE(sum(point_feed_items.point_value), 0) на COALESCE(COUNT(point_feed_items.point_value), 0) и проверил без корректировки ScoreAdjustments в базе данных, которую он возвратил 0. Затем я создал одно ScoreAdjustment с правильной командой и COALESCE(COUNT(point_feed_items.point_value), 0) затем вернул 3 вместо 1. Я неправильно понимаю, как LEFT JOIN AS работает?

Это часть приложения rails, однако в основном оно написано как ручной запрос для повышения производительности.

1 Ответ

0 голосов
/ 20 июня 2020

Оказывается, все это произошло из-за непонимания того, как работает LEFT JOIN, когда вы делаете это несколько раз.

Учитывая следующие данные (упрощенный пример):

Teams Feed items

Я думал, что LEFT JOIN выглядит примерно так:

What I thought the data looked like

На самом деле это выглядело примерно так:

What the data actually looked like

Я пошел дальше и изменил свой запрос, чтобы он выглядел следующим образом:

SELECT COALESCE(sum(point_feed_items.team_score), 0) as team_score, 
  GREATEST(MAX(pentest_feed_items.last_solve_time), 
  MAX(point_feed_items.last_solve_time)) as last_solve_time, 
      teams.*
  FROM "teams" 
  LEFT JOIN LATERAL
  (
    SELECT 
      COALESCE(sum(challenges.point_value), 0) + COALESCE(sum(feed_items.point_value), 0) as team_score, 
      MAX(feed_items.created_at) as last_solve_time
    FROM feed_items
    LEFT JOIN challenges ON challenges.id = feed_items.challenge_id AND challenges.type IN ('StandardChallenge')
    WHERE feed_items.team_id = teams.id
    AND feed_items.type IN ('StandardSolvedChallenge', 'ScoreAdjustment')
  ) AS point_feed_items ON true
  LEFT JOIN LATERAL
  (
    SELECT MAX(feed_items.created_at) as last_solve_time
    FROM feed_items
    WHERE feed_items.team_id = teams.id
    AND feed_items.type IN ('PentestSolvedChallenge')
  ) AS pentest_feed_items ON true
  WHERE "teams"."division_id" = $1 GROUP BY teams.id

И теперь все отлично работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...