Как вычислить ранжирование с помощью хранимых процедур MySQL? - PullRequest
2 голосов
/ 01 февраля 2012

Предположим, у нас есть очень простая таблица:

|class   |student|
---------------
 Math     Alice
 Math     Bob
 Math     Peter
 Math     Anne
 Music    Bob
 Music    Chis
 Music    Debbie
 Music    Emily
 Music    David
 Sports   Alice
 Sports   Chris
 Sports   Emily
 .
 .
 .

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

|student |common classes|
 Brad     6
 Melissa  4
 Chris    3
 Bob      3
 .
 .
 .

То, что я сейчас делаю, - это отдельный запрос для каждого класса. Слияние результатов производится на стороне клиента. Это очень медленно, потому что я очень трудолюбивый ученик и посещаю около 1000 уроков - как и большинство других учеников. Я хотел бы сократить транзакции и выполнить обработку на стороне сервера, используя хранимые процедуры. Я никогда не работал с sprocs, поэтому я был бы рад, если бы кто-нибудь дал мне несколько советов, как это сделать.

(примечание: я использую кластер MySQL, потому что это очень большая школа с 1 миллионом классов и несколькими миллионами учеников)

UPDATE

Хорошо, очевидно, что я не эксперт по БД;) 4 раза почти такой же ответ означает, что это слишком просто. В любом случае, спасибо! Я протестировал следующий оператор SQL, и он возвращает то, что мне нужно, хотя он очень медленный в кластере (но это будет другой вопрос, я думаю).

SELECT student, COUNT(class) as common_classes
FROM classes_table
WHERE class in (my_subject_list)
GROUP BY student
ORDER BY common_classes DESC

Но на самом деле я слишком сильно упростил свою задачу, поэтому давайте немного усложним:

Некоторые классы важнее других, поэтому они имеют вес:

| class   | importance |
  Music     0.8
  Math      0.7
  Sports    0.01
  English   0.5
  ...

Кроме того, студенты могут быть более или менее важными. (Если вам интересно, о чем все это ... это аналогия. И она становится все хуже. Поэтому, пожалуйста, просто примите этот факт. Это связано с нормализацией.)

|student | importance |
 Bob       3.5
 Anne      4.2
 Chris     0.3
 ...

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

map<Student,float> studentRanking;
foreach (Class c in myClasses)
{
     float myScoreForClassC = getMyScoreForClass(c);
     List students = getStudentsAttendingClass(c);
     foreach (Student s in students)
     {
          float studentScoreForClassC = c.classImportance*s.Importance;
          studentRanking[s] += min(studentScoreForClassC, myScoreForClassC);
     }
}

Надеюсь, это не слишком запутанно.

Я должен также упомянуть, что меня нет в базе данных, поэтому я должен сообщить оператору SELECT / хранимой процедуре, какие классы я посещаю.

Ответы [ 4 ]

2 голосов
/ 01 февраля 2012
SELECT 
  tbl.student, 
  COUNT(tbl.class) AS common_classes
FROM
  tbl
WHERE tbl.class IN (SELECT 
                      sub.class 
                    FROM 
                      tbl AS sub 
                    WHERE 
                      (sub.student = "BEN")) -- substitue "BEN" as appropriate
GROUP BY tbl.student
ORDER BY common_classes DESC;
1 голос
/ 01 февраля 2012
SELECT student, COUNT(class) as common_classes
FROM classes_table
WHERE class in (my_subject_list)
GROUP BY student
ORDER BY common_classes DESC

Обновление обновление вашего вопроса.

Предполагая, что есть таблица class_importance и student_importance, как вы описали выше:

SELECT classes.student, SUM(ci.importance*si.importance) AS weighted_importance
FROM classes
LEFT JOIN class_importance ci ON classes.class=ci.class
LEFT JOIN student_importance si ON classes.student=si.student
WHERE classes.class in (my_subject_list)
GROUP BY classes.student
ORDER BY weighted_importance DESC

Единственное, чего у него нет - это LEAST(weighted_importance, myScoreForClassC), потому что я не знаю, как вы это рассчитываете.

Предположим, у вас есть еще один стол myScores:

class | score
Math      10
Sports     0
Music     0.8
...

Вы можете комбинировать все это следующим образом (см. Дополнительные LEAST внутри SUM):

SELECT classes.student, SUM(LEAST(m.score,ci.importance*si.importance)) -- min
                         AS weighted_importance
FROM classes
LEFT JOIN class_importance ci ON classes.class=ci.class
LEFT JOIN student_importance si ON classes.student=si.student
LEFT JOIN myScores m ON classes.class=m.class                 -- add in myScores
WHERE classes.class in (my_subject_list)
GROUP BY classes.student
ORDER BY weighted_importance DESC

Если у вашего myScores нет баллов по определенному классу, и вы хотите назначить какое-то значение по умолчанию, вы можете использовать IFNULL(m.score,defaultvalue).

1 голос
/ 01 февраля 2012

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

SELECT
  otherStudents.Student,
  COUNT(*)   AS sharedClasses
FROM
  class_student_map          AS myClasses
INNER JOIN
  class_student_map          AS otherStudents
    ON  otherStudents.class    = myClasses.class
    AND otherStudents.student != myClasses.student
WHERE
  myClasses.student = 'Ben'
GROUP BY
  otherStudents.Student

EDIT

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

Используя пример SQL, который вы дали в редакторе ...

SELECT
  classes_table.student,
  MIN(class_importance.importance * student_importance.importance) as rank
FROM
  classes_table
INNER JOIN
  class_important
    ON classes_table.class = class_importance.class
INNER JOIN
  student_important
    ON classes_table.student = student_importance.student
WHERE
  classes_table.class in (my_subject_list)
GROUP BY
  classes_table.student
ORDER BY
  2
1 голос
/ 01 февраля 2012

Насколько я понимаю ваш вопрос, вы можете просто выполнить запрос следующим образом:

SELECT `student`, COUNT(`class`) AS `commonClasses` 
FROM `classes_to_students` 
WHERE `class` IN ('Math', 'Music', 'Sport') 
GROUP BY `student` 
ORDER BY `commonClasses` DESC
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...