在消除候选人的同时重新分配投票

时间:2014-06-02 08:58:27

标签: mysql

考虑一下这个想法。我有一个排名选择投票系统,其中不满足阈值的候选人在不同轮次中被淘汰。在这个例子中,我们有6名候选人在初选中竞选两个席位。此时我们已经进入第4轮(没有获胜者),其中候选人3和5已被淘汰。在这一轮中,候选人4被淘汰,他/她的选票即将被重新分配给在候选人4被标记为首选的每次投票中被标记为第二或第三选择的候选人。

我们抓住所有选票,其中候选人4是首选(恰好是4张选票)。它们看起来像这样:

候选人4:
4 1 6 3 2 5
4 3 1 6 2 5
4 5 6 2 3 1
4 3 1 5 6 2

重新分配的投票是(粗体):
4 1 6 3 2 5
4 3 1 6 2 5
4 5 6 2 3 1
4 3 1 5 6 2

现在我的问题是:我需要编写一个MySQL查询来获取这些值并计算每个候选人通过此重新分配获得的投票数。即MySQL查询的结果应如下所示:

候选人1:3票
候选人6:1投票

编辑:此查询需要知道候选人3和5已经被淘汰,因此前往下一列找到任何其他候选人。

如何编写MySQL查询以获得此结果?

2 个答案:

答案 0 :(得分:1)

如果我理解你的话会是这样的吗?

select
coalesce(if(c2 in (3,5), null, c2),
         if(c3 in (3,5), null, c3),
         if(c4 in (3,5), null, c4),
         if(c5 in (3,5), null, c5),
         if(c6 in (3,5), null, c6)) as candidate,
count(*) as votes
from Table1
where c1 = 4
group by candidate

说明:

coalesce()会返回其第一个参数,而不是null。使用if()我们说,如果列值为3或5,则将其替换为null。因此,您按照指定的顺序获得第一列,这不适用于已淘汰的候选人。然后你只需按它分组并计算行数。

答案 1 :(得分:1)

一直在玩。

我认为使用您当前的数据布局,如果可以这样做: -

SELECT COALESCE(IF(vote_candidate_b IN (4,3,5), NULL, vote_candidate_b),
                IF(vote_candidate_c IN (4,3,5), NULL, vote_candidate_c),
                IF(vote_candidate_d IN (4,3,5), NULL, vote_candidate_d),
                IF(vote_candidate_e IN (4,3,5), NULL, vote_candidate_e),
                IF(vote_candidate_f IN (4,3,5), NULL, vote_candidate_f)) AS vote_candidate,
                COUNT(*)
FROM votes
WHERE vote_candidate_a = 4
GROUP BY vote_candidate;

这就像fancypants已经回答的那样。我并不热衷于此,因为它看起来并不容易阅读(如果数据量很大)可能效率低下。

我更倾向于将数据拆分为不同的表结构,每组投票有多行,每个候选者一个。如果系统处于开发的早期阶段(即,您只是生成新表),这将更容易做到。

假设您的现有数据设置如下: -

CREATE TABLE votes
(
    vote_id INT NOT NULL AUTO_INCREMENT,
    vote_candidate_a INT,
    vote_candidate_b INT,
    vote_candidate_c INT,
    vote_candidate_d INT,
    vote_candidate_e INT,
    vote_candidate_f INT
);

INSERT INTO votes
VALUES
(NULL, 4, 1, 6, 3, 2, 5),
(NULL, 4, 3, 1, 6, 2, 5),
(NULL, 4, 5, 6, 2, 3, 1),
(NULL, 4, 3, 1, 5, 6, 2);

我的格式可以按如下方式生成: -

CREATE TABLE vote_orders
(
    id INT NOT NULL AUTO_INCREMENT,
    vote_id INT,
    vote_order INT,
    vote_candidate INT
);

INSERT INTO vote_orders (id, vote_id, vote_order, vote_candidate)
SELECT NULL, vote_id, 1, vote_candidate_a FROM votes
UNION
SELECT NULL, vote_id, 2, vote_candidate_b FROM votes
UNION
SELECT NULL, vote_id, 3, vote_candidate_c FROM votes
UNION
SELECT NULL, vote_id, 4, vote_candidate_d FROM votes
UNION
SELECT NULL, vote_id, 5, vote_candidate_e FROM votes
UNION
SELECT NULL, vote_id, 6, vote_candidate_f FROM votes;

然后您可以使用以下内容获得投票。这使用子查询来获得尚未使用的最高投票,然后将其与数据连接起来。

SELECT vote_candidate, COUNT(*)
FROM vote_orders a
INNER JOIN
(
    SELECT vote_id, MIN(vote_order) AS min_vote_order
    FROM vote_orders
    WHERE vote_candidate NOT IN (4,3,5)
    GROUP BY vote_id
) b
ON a.vote_id = b.vote_id
AND a.vote_order = b.min_vote_order
INNER JOIN
(
    SELECT vote_id
    FROM vote_orders
    WHERE vote_candidate  = 4
    AND vote_order = 1
) c
ON a.vote_id = c.vote_id
GROUP BY vote_candidate

SQL小提琴: -

http://www.sqlfiddle.com/#!2/7d48c/10

混合解决方案(两全其美!): -

SELECT vote_candidate, COUNT(*)
FROM 
(
    SELECT vote_id, 1 AS vote_order, vote_candidate_a AS vote_candidate FROM votes WHERE vote_candidate_a = 4
    UNION
    SELECT vote_id, 2, vote_candidate_b FROM votes WHERE vote_candidate_a = 4
    UNION
    SELECT vote_id, 3, vote_candidate_c FROM votes WHERE vote_candidate_a = 4
    UNION
    SELECT vote_id, 4, vote_candidate_d FROM votes WHERE vote_candidate_a = 4
    UNION
    SELECT vote_id, 5, vote_candidate_e FROM votes WHERE vote_candidate_a = 4
    UNION
    SELECT vote_id, 6, vote_candidate_f FROM votes WHERE vote_candidate_a = 4
) a
INNER JOIN
(
    SELECT vote_id, MIN(vote_order) AS min_vote_order
    FROM 
    (
        SELECT vote_id, 2 AS vote_order, vote_candidate_b AS vote_candidate FROM votes
        UNION
        SELECT vote_id, 3, vote_candidate_c FROM votes
        UNION
        SELECT vote_id, 4, vote_candidate_d FROM votes
        UNION
        SELECT vote_id, 5, vote_candidate_e FROM votes
        UNION
        SELECT vote_id, 6, vote_candidate_f FROM votes
    ) a
    WHERE vote_candidate NOT IN (4,3,5)
    GROUP BY vote_id
) b
ON a.vote_id = b.vote_id
AND a.vote_order = b.min_vote_order
GROUP BY vote_candidate;