минимизируя время выполнения расчета нескольких рангов (на основе оценок) для больших таблиц sql, насколько коротким он может быть (доступ ms) - PullRequest
1 голос
/ 25 января 2010

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

У меня есть большая таблица (320 000 строк), которая содержит коды учеников (служит вместо идентификаторов их именами), аудиторию учеников, тест, дату экзаменов, предмет, номер вопроса и оценку учеников. по этому вопросу. Эта таблица является основой для всего остального, что вычисляется, и ее размер делает все эти вычисления очень очень медленными, до такой степени, что я нахожу меня почти ломающим все здесь на работе.

Сначала немного информации о школе (очень мало информации, чтобы понять проблему)
Здесь, в школе, мы проводим еженедельные тесты по нескольким предметам. Школа также разделена на классные комнаты с разными целями (одна сфокусирована на математике, физике и химии, другая - биологии, а последняя - истории, португальском и географии). Но все они проводят одни и те же тесты каждую неделю.

То, что мы хотим сделать, - это рассчитать стандартное отклонение для каждого вопроса для всех в школе (не для класса) и средней оценки за вопрос (также для всех в школе), а затем сформировать следующие ранги (все из них на дату):

-Ранк по предмету в классе (с «необработанными» оценками), Ранг по предмету с учетом всей школы (с «необработанными» оценками) и Ранг по предмету с учетом всей школы (с использованием нормированных оценок, со стандартным отклонением на вопрос и средняя оценка за вопрос информации)
Те же ранги, которые были упомянуты выше, но не по предмету, а вместо этого рассматриваются все предметы

Как видите, после расчета средних оценок и стандартных отклонений нам все еще необходимо рассчитать суммы оценок по каждому вопросу и ранжировать в соответствии с этими суммами (фактические оценки по предмету / тесту). Я атаковал эту проблему несколькими способами:

1) Созданы две таблицы, одна с оценками на каждого учащегося по предмету (поля: код ученика, класс ученика, дата тестирования, предмет, оценка, нормализованная оценка, рейтинг в классе, рейтинг в школе, рейтинг в школе с использованием нормализованных оценок), а другая с оценки на одного учащегося в каждом тесте (все предметы учитываются, поля: код ученика, класс ученика, дата экзамена, оценка, нормализованная оценка, рейтинг в классе, рейтинг в школе, рейтинг в школе с использованием нормализованных оценок).

Вставка данных в эти таблицы занимает около 50 секунд

Затем я попытался использовать SQL для ранжирования, однако столкнулся с некоторыми проблемами:
-Access не имеет функций ROW_NUMBER или RANK, и поэтому я должен использовать запросы с COUNT, например (ниже приведена только упрощенная версия):

SELECT 1+(SELECT Count(*) FROM grades_table_per_subject t2 WHERE 
t2.Grade > t1.Grade AND t1.Date=t2.Date AND t1.Subject=t2.Subject) AS [Global Rank],
1+(SELECT Count(*) FROM grades_table_per_subject t3 WHERE t3.Grade > t1.Grade AND
t3.Date=t1.Date AND t3.Subject=t1.Subject AND t3.Classroom=t1.Classroom) AS
[Rank in classroom] FROM grades_table_per_subject;

В запросе все еще есть ранг с нормированными оценками, но я его опустил.
Таблица grades_table_per_subject имеет около 45 000 строк, и этот запрос занимает здесь более 15 минут, даже с индексированием (пробовал много разных комбинаций индексов, даже несколько нечетных, когда я увидел, что те, которые должны работать, не работают).
Я также попытался ORDER BY Count (
) DESC внутреннее выделение, но я нажал Ctrl + Break через 7 минут и безрезультатно.

2) В приведенные выше таблицы добавлены следующие поля: Позиция в классе, Позиция в школе, Позиция в школе с использованием нормированных оценок

Затем я попытался использовать VBA с DAO и вручную обновить поля Rank, запустив следующий код (упрощенная версия):

Set rs = CurrentDb.OpenRecordset("SELECT Classroom, Date, Subject, Grade, [Rank in classroom] FROM
grades_table_per_subject ORDER BY Date, Classroom, Subject, Grade DESC;", dbOpenDynaset)
...
...
rs.movefirst
i=1
While Not rs.eof
 'Verifies if there was a change on either one of Subject, Classroom, Date and if so:
 ...
  i = 1
 ...

 rs.Edit
 rs![Rank in classroom]=i
 rs.Update

 i = i + 1
 rs.movenext
Wend
rs.close

Это, очевидно, строит только один из рангов (в данном случае на каждого предмета в классе), и это занимает всего 3 минуты 10 секунд.
Я проверил, что это занимает так много времени из-за записей в таблицу (виновниками являются rs.Edit и rs.Update, комментируя их, все запускается всего за 4 секунды), но мне нужны ранги, записанные в таблицу для генерации отчет о доступе позже.

ЗАКЛЮЧИТЕЛЬНО:
Я мог бы сгенерировать все ранги один раз и предоставить пользователям быстрый доступ ко всем данным, но идея в том, что все должно быть рассчитано на лету.Однако достигнутые нами времена делают это невозможным.

В целом, вопрос, который нужно задать, следующий:
-Есть ли способ вычислить показанные выше ранги с помощью запроса на доступ менее чем за 10 секунд или использовать VBA и вычислить-вставить эти рангитаблица в аналогичное время, учитывая размер таблиц, используемых здесь?

Кроме того, я хотел бы увидеть список эффективных алгоритмов ранжирования, так что даже если я не могу все сделать быстро, я могу максимально улучшить его.

Ответы [ 2 ]

1 голос
/ 25 января 2010

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

Почему?

Зачем восстанавливать одни и те же данные снова и снова? Скорее всего, предпочтительнее генерировать эту статистику, когда данные изменяются, и просто просматривать их каждый раз. Повторять работу, которую вы уже сделали, когда кто-то хочет что-то проверить, просто глупо.

0 голосов
/ 25 января 2010

Я только что видел, как вы говорите MS Access только

поэтому проигнорируйте этот ответ - или рассмотрите возможность перехода к реальной БД, если вы хотите иметь возможность выполнять этот тип обработки энергии.

оригинальный ответ ниже

У меня нет доступа к вашим тестовым данным, но как быстро это выполняется?

SELECT RANK () OVER (PARTITION BY [Date],[Subject] ORDER BY Grade) AS [Global Rank],
       RANK () OVER (PARTITION BY [Date],[Subject], Classroom ORDER BY Grade) AS [Rank in classroom]
FROM grades_table_per_subject

Полагаю, вы не сможете превзойти скорость ранжирования SQL-серверов в VBA. Если этого недостаточно, вам нужно заглянуть в профилировщик и посмотреть, какие индексы он предлагает вам создать.

...