MYSQL: выберите N лучших кандидатов - PullRequest
0 голосов
/ 27 мая 2018

У меня есть следующая схема БД для университетских выборов:

db scheme

для каждого отдела, у меня есть следующие должности:

1 CHEF (который является candidate_position = 1)

& 6 членов (который является candidate_position = 2)

Iхочу получить победителей выборов в каждом департаменте.

, чтобы получить победителя позиции CHEF в отделе «Informatique», я сделал следующий запрос:

SELECT doctor.firstname, doctor.lastname, votes

FROM (SELECT COUNT(*) AS votes FROM candidate_votes WHERE candidate_votes.candidate_position = 1 GROUP BY candidate_votes.candidate_id) AS votes, doctor 

INNER JOIN department_candidates ON department_candidates.doctor_id = doctor.id 

INNER JOIN department ON department.id = department_candidates.department_id AND department.name = 'Informatique' 

INNER JOIN candidate_votes ON candidate_votes.candidate_id = doctor.id AND candidate_votes.candidate_position = 1 

GROUP BY candidates_votes.candidate_id

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

на основании результатов может быть ничья (или ничья), я думаю, что мой запрос о выборе победителя позиции Chef правильный,Но мне нужна помощь, чтобы узнать, как выбрать первые 6 кандидатов на позицию Member?

Набор данных:

--
-- Table structure for table `candidate_votes`
--

DROP TABLE IF EXISTS `candidate_votes`;
CREATE TABLE IF NOT EXISTS `candidate_votes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `candidate_id` int(11) NOT NULL,
  `voter_id` int(11) NOT NULL,
  `candidate_position` tinyint(1) NOT NULL COMMENT '1: chef, 2: member',
  `date` date NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk-candidate_votes-voter_id` (`voter_id`),
  KEY `fk-candidate_votes-candidate_id_idx` (`candidate_id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8;

--
-- Dumping data for table `candidate_votes`
--

INSERT INTO `candidate_votes` (`id`, `candidate_id`, `voter_id`, `candidate_position`, `date`) VALUES
(24, 2, 1, 1, '2018-05-26'),
(25, 1, 1, 2, '2018-05-26'),
(26, 6, 1, 2, '2018-05-26'),
(27, 5, 1, 2, '2018-05-26'),
(28, 7, 1, 2, '2018-05-26'),
(29, 8, 1, 2, '2018-05-26'),
(30, 9, 1, 2, '2018-05-26'),
(31, 2, 2, 1, '2018-05-16'),
(32, 3, 7, 1, '2018-05-22'),
(33, 3, 8, 1, '2018-05-22'),
(34, 4, 6, 2, '2018-05-29'),
(35, 7, 6, 2, '2018-05-29');

-- --------------------------------------------------------

--
-- Table structure for table `department`
--

DROP TABLE IF EXISTS `department`;
CREATE TABLE IF NOT EXISTS `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `department-name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

--
-- Dumping data for table `department`
--

INSERT INTO `department` (`id`, `name`) VALUES
(1, 'Informatique'),
(2, 'Mathematique'),
(4, 'physique');

-- --------------------------------------------------------

--
-- Table structure for table `department_candidates`
--

DROP TABLE IF EXISTS `department_candidates`;
CREATE TABLE IF NOT EXISTS `department_candidates` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `department_id` int(11) NOT NULL,
  `doctor_id` int(11) NOT NULL,
  `candidate_position` tinyint(1) NOT NULL COMMENT '1: chef, 2: member',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

--
-- Dumping data for table `department_candidates`
--

INSERT INTO `department_candidates` (`id`, `department_id`, `doctor_id`, `candidate_position`) VALUES
(5, 1, 3, 1),
(7, 1, 4, 2),
(8, 1, 1, 2),
(9, 1, 2, 1),
(10, 1, 6, 2),
(11, 1, 5, 2),
(12, 1, 7, 2),
(13, 1, 8, 2),
(14, 1, 9, 2);

-- --------------------------------------------------------

--
-- Table structure for table `doctor`
--

DROP TABLE IF EXISTS `doctor`;
CREATE TABLE IF NOT EXISTS `doctor` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `firstname` varchar(255) NOT NULL,
  `lastname` varchar(255) NOT NULL,
  `department_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

--
-- Dumping data for table `doctor`
--

INSERT INTO `doctor` (`id`, `firstname`, `lastname`, `department_id`) VALUES
(1, 'doc1_fn', 'doc1_ln', 1),
(2, 'doc2_fn', 'doc2_ln', 1),
(3, 'doc3_fn', 'doc3_ln', 1),
(4, 'doc4_fn', 'doc4_ln', 1),
(5, 'doc5_fn', 'doc5_ln', 1),
(6, 'doc6_fn', 'doc6_ln', 1),
(7, 'doc7_fn', 'doc7_ln', 1),
(8, 'doc8_fn', 'doc8_ln', 1),
(9, 'doc9_fn', 'doc9_ln', 1);

-- --------------------------------------------------------

Sqlfiddle DEMO

Ответы [ 3 ]

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

Рассмотрим следующее:

SELECT x.*
     , CASE WHEN @prev_position = candidate_position THEN CASE WHEN @prev_total = total THEN @i:=@i ELSE @i:=@i+1 END ELSE @i:=1 END i
     , @prev_position := candidate_position prev_position
     , @prev_total := total prev_total
  FROM
     (
SELECT candidate_id
     , candidate_position
     , COUNT(*) total 
  FROM candidate_votes  
 GROUP 
    BY candidate_id
     , candidate_position
     ) x
  JOIN 
     ( SELECT @prev_position := null,@prev_total:=null,@i:=0) vars
 ORDER
    BY candidate_position
     , total DESC;

+--------------+--------------------+-------+------+---------------+------------+
| candidate_id | candidate_position | total | i    | prev_position | prev_total |
+--------------+--------------------+-------+------+---------------+------------+
|            2 |                  1 |     2 |    1 |             1 |          2 |
|            3 |                  1 |     2 |    1 |             1 |          2 |

|            7 |                  2 |     2 |    1 |             2 |          2 |

|            8 |                  2 |     1 |    2 |             2 |          1 |
|            9 |                  2 |     1 |    2 |             2 |          1 |
|            1 |                  2 |     1 |    2 |             2 |          1 |
|            4 |                  2 |     1 |    2 |             2 |          1 |
|            5 |                  2 |     1 |    2 |             2 |          1 |
|            6 |                  2 |     1 |    2 |             2 |          1 |
+--------------+--------------------+-------+------+---------------+------------+

В этом примере i представляет ранг.Для позиции 1 мы видим, что два кандидата связаны за первое место.Для позиции 2 был один прямой победитель, со всеми остающимися кандидатами, связывающими для второго места.

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

Очевидно, я пытался быть слишком умным в своем другом ответе, вы можете получить простую таблицу ранжирования, например, такую:

SELECT cast(dc.candidate_position AS UNSIGNED) AS position, dc.doctor_id, doc.firstname, doc.lastname, v.votes
FROM department_candidates dc
JOIN department dept ON dept.id=dc.department_id AND dept.name='Informatique'
JOIN doctor doc ON doc.id=dc.doctor_id
JOIN (SELECT candidate_position AS cp, candidate_id AS cid, count(candidate_id) AS votes
      FROM candidate_votes
      GROUP BY cid) v
  ON v.cid=doc.id AND v.cp = dc.candidate_position
ORDER BY position, v.votes DESC

Вывод:

position    doctor_id   firstname   lastname    votes
1           3           doc3_fn     doc3_ln     2
1           2           doc2_fn     doc2_ln     2

2           7           doc7_fn     doc7_ln     2
2           4           doc4_fn     doc4_ln     1
2           1           doc1_fn     doc1_ln     1
2           6           doc6_fn     doc6_ln     1
2           5           doc5_fn     doc5_ln     1
2           8           doc8_fn     doc8_ln     1
2           9           doc9_fn     doc9_ln     1

Демо

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

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

SET @position = 2;
SET @numwinners = 3;
SELECT @rank := @rank+1 AS rank, votes
FROM (SELECT COUNT(candidate_id) AS votes 
      FROM candidate_votes 
      WHERE candidate_position = @position
      GROUP BY candidate_id
      ORDER BY votes DESC) vr
JOIN (select @rank := 0) r
GROUP BY rank
HAVING rank = @numwinners

Вывод для ваших данных с @position=2 и @numwinners=3:

rank    votes
3       1

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

SET @position = 2;
SET @numwinners = 3;
SELECT doc.firstname, doc.lastname, v.votes
FROM department_candidates dc
JOIN department dept ON dept.id=dc.department_id AND dept.name='Informatique'
JOIN doctor doc ON doc.id=dc.doctor_id
JOIN (SELECT candidate_id AS cid, count(candidate_id) AS votes
      FROM candidate_votes
      WHERE candidate_position = @position
      GROUP BY cid) v
  ON v.cid=dc.id
WHERE v.votes >= (SELECT votes
                  FROM (SELECT @rank := @rank+1 AS rank, votes
                        FROM (SELECT COUNT(candidate_id) AS votes 
                              FROM candidate_votes 
                              WHERE candidate_position = @position
                              GROUP BY candidate_id
                              ORDER BY votes DESC) vr
                        JOIN (select @rank := 0) r
                        GROUP BY rank
                        HAVING rank = @numwinners
                       ) vt
                  )
ORDER BY v.votes DESC

Вывод для ваших данных с помощью @position=2 и @numwinners=3:

firstname   lastname    votes
doc7_fn     doc7_ln     2
doc4_fn     doc4_ln     1
doc1_fn     doc1_ln     1
doc6_fn     doc6_ln     1
doc5_fn     doc5_ln     1
doc8_fn     doc8_ln     1
doc9_fn     doc9_ln     1

Вывод для ваших данных с помощью @position=1 и @numwinners=1:

firstname   lastname    votes
doc3_fn     doc3_ln     2
doc2_fn     doc2_ln     2

Если вы хотите, чтобы запрос работал, даже если значение @numwinners превышает число кандидатов, измените:

HAVING rank = @numwinners

на

HAVING rank = LEAST(@numwinners, (SELECT COUNT(DISTINCT candidate_id)
                                  FROM candidate_votes
                                  WHERE candidate_position = @position))

Демо

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