Выберите строку в зависимости от того, существует ли другая строка таблицы - PullRequest
0 голосов
/ 02 мая 2018

Трудно написать хорошее название для этого ...

У меня есть 4 таблицы в моей базе данных postgresql:

courses:
| id | data | created_at | updated_at | status |

course_versions:
| id | course_id | data | created_at | updated_at | status |

course_progresses:
| id | course_version_id | user_id | data | created_at | updated_at |

users:
| id | email |

У курса может быть несколько курсов_версий, и у пользователя могут быть курсы_прогресса, и каждый курс_прогресс связан с версией курса. Теперь я хочу получить все версии_курса для конкретного пользователя, то есть, если у пользователя нет курса_прогресса для курса_версии course id: 1, он должен вернуть самую последнюю версию курса. Если у пользователя course_progress для course_version course id: 2, то course_version должен быть возвращен, независимо от того, существуют ли более новые версии course_version… Получите? :)

Это запрос, который работает:

WITH
course_versions_with_progress AS (
  SELECT cv.*
  FROM course_versions AS cv
  INNER JOIN courses AS c ON c.id = cv.course_id
  INNER JOIN course_progresses AS cp ON cp.course_version_id = cv.id
  WHERE c.status = 1 AND cv.status = 1 AND cp.user_id = 123
),
latest_course_versions AS (
  SELECT DISTINCT ON(cv.course_id) cv.*
  FROM course_versions AS cv
  INNER JOIN courses AS c ON c.id = cv.course_id
  WHERE c.status = 1 AND cv.status = 1
  ORDER BY cv.course_id, cv.created_at DESC
)
SELECT * FROM course_versions_with_progress
UNION
  SELECT * FROM latest_course_versions
  WHERE NOT EXISTS (
    SELECT 1
    FROM course_versions_with_progress
    WHERE course_id = latest_course_versions.course_id
  )

Хотя это выглядит довольно ужасно ... Есть ли лучший способ написать этот запрос?

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Трудно что-то написать без примеров данных от вас, но:

with -- Example data, just to check that there are no syntax errors 
    courses(id, status) as (values
        (1,1),(2,1),(3,0)),
    course_versions(id, course_id, status, created_at) as (values
        (1,1,1,current_timestamp),(2,1,1,current_timestamp),(3,2,1,current_timestamp),(4,2,0,current_timestamp)),
    course_progresses(id, course_version_id, user_id) as (values
        (2,3,123))
select distinct on (cp.course_version_id, cv.course_id)
    cv.*
from
    course_versions as cv
        join courses as c on (c.id = cv.course_id and c.status = 1 and cv.status = 1)
        left join course_progresses cp on (cp.course_version_id = cv.id and cp.user_id = 123)
order by
    cp.course_version_id nulls last, cv.course_id, cv.created_at desc;
0 голосов
/ 02 мая 2018

Вы можете использовать cross join для создания 2D матрицы пользователей и курсов. Для каждой записи ищите либо версию с прогрессом, либо последнюю версию:

select  u.email
,       c.course_id
,       coalesce(progress_version.created_at, latest_version.created_at)
from    users u
cross join
        courses c
left join
        (
        select  cp.user_id
        ,       cv.course_id
        ,       cv.created_at
        from    course_progresses cp
        join    course_versions cv
        on      cv.id = cp.course_version_id
        ) progress_version
on      progress_version.user_id = u.id
        and progress_version.course_id = c.id
left join
        (
        select  row_number() over (partition by course_id 
                                   order by created_at desc) rn
        ,       *
        from    course_versions
        ) latest_version
on      latest_version.course_id = c.id
        and latest_version.rn = 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...