Как исправить последовательный порядок столбцов с «пробелами» в Ruby on Rails Migration - PullRequest
0 голосов
/ 26 июня 2019

У меня есть приложение Ruby on Rails (версия Rails: 5.1.1 и версия Ruby 2.3.1). Также, используя PostgreSQL, если это имеет значение. У меня проблемы с целостностью данных с таблицей сопоставления, которая отображает студентов на выбранные курсы, где столбец с именем ranking неправильно содержит «пробелы» в рейтинге и должен быть исправлен, чтобы быть последовательным (для каждого учащегося) без пробелов.

Пример: Допустим, у меня есть таблица с именем student_course_rankings, где студент выбирает несколько курсов и ранжирует их на основе своего любимого курса.

Существует уникальное ограничение для (student_id, course_id, ranking), поэтому студент не может выбрать один и тот же курс дважды.

ranking столбец просто имеет ограничение NOT NULL, но не имеет других ограничений и имеет тип integer.

Каков наилучший способ сделать это в Ruby Migration?

class FixStudentCourseRankings < ActiveRecord::Migration[5.1]
  def change

    # Deletes duplicate courses (keeps the first)
    StudentCourse.where.not(
      id: StudentCourse.group(:course_id, :student_id).pluck('min(student_courses.id)')
    ).delete_all

    # Adds unique constraint so students can't accidentally select the same course more than once
    add_index :student_courses, [:course_id, :student_id, :ranking], :unique => true

    # HERE: Fix ranking order???
  end
end

Ниже приведена упрощенная версия моей таблицы и того, как она в настоящее время выглядит с двумя студентами.

 id | student_id | course_id | ranking 
----+------------+-----------+---------
  1 |          1 |         2 |       1
  2 |          1 |         3 |       2
  3 |          1 |         5 |       4
  4 |          1 |         9 |       5
  5 |          1 |         6 |       6
  6 |          2 |         3 |       2
  7 |          2 |         6 |       4
  8 |          2 |         5 |       5

Как я хочу, чтобы это действительно выглядело:

 id | student_id | course_id | ranking 
----+------------+-----------+---------
  1 |          1 |         2 |       1
  2 |          1 |         3 |       2
  3 |          1 |         5 |       3
  4 |          1 |         9 |       4
  5 |          1 |         6 |       5
  6 |          2 |         3 |       1
  7 |          2 |         6 |       2
  8 |          2 |         5 |       3

Любая помощь будет принята с благодарностью!

Ответы [ 3 ]

3 голосов
/ 26 июня 2019

PostgreSQL имеет некоторые оконные функции для ранжирования с / без пробелов.Вам нужен без пробелов - ROW_NUMBER :

class FixStudentCourseRankings < ActiveRecord::Migration[5.1]
  def change

    # Deletes duplicate courses (keeps the first)
    StudentCourse.where.not(
      id: StudentCourse.group(:course_id, :student_id).pluck('min(student_courses.id)')
    ).delete_all

    execute <<~SQL
      UPDATE student_courses
      SET ranking = ranked.ranking
      FROM (
        SELECT
          id,
          ROW_NUMBER() OVER(
            PARTITION BY student_id
            ORDER BY id ASC
          ) AS ranking
        FROM student_courses
      ) ranked
      WHERE student_courses.id = ranked.id
    SQL

    # Adds unique constraint so students can't accidentally select the same course more than once
    add_index :student_courses, %i[course_id student_id ranking], unique: true
  end
end

Db-Fiddle

1 голос
/ 26 июня 2019

Я бы сделал что-то вроде этого:

Student.find_each do |student|
  # load all student_courses for a given student ordered by `ranking`
  student_courses = StudentCourse.where(student_id: student).order(:ranking)

  student_courses.each.with_index(1) do |student_course, index|
    # update the ranking by its position in the list
    student_course.update(ranking: index)
  end
end
0 голосов
/ 26 июня 2019

Полагаю, вам нужно будет сделать следующее:

  1. Вы можете написать грабли, чтобы исправить существующие записи с неправильными / отсутствующими рангами. Я рекомендую написать грабельное задание, так как это будет одноразовое действие, которое на самом деле не будет переключаться вверх / вниз, как при миграциях.
  2. Вы можете ввести проверки на наличие и уникальность рейтингов, ограниченных до course_id и student_id. Ссылка: Проверка уникальности в Rails
  3. Вы можете написать пользовательскую проверку для модели student_course_rankings, чтобы проверить, что в ранжировании нет пробелов.
  4. Кроме того, вам нужно будет обработать порядок ранжирования, используя подходящие представления.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...