2011-10-25 115 views
1

我有一个投票系统,其目的是这样的:给定一串物品ID的如何优化该查询?

CREATE TABLE `vote` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `weight` int(11) NOT NULL, 
    `submited_date` datetime NOT NULL, 
    `resource_type` varchar(255) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=2963832 DEFAULT CHARSET=latin1; 

CREATE TABLE `article_preselection_vote` (
    `id` int(11) NOT NULL, 
    `article_id` int(11) DEFAULT NULL, 
    `user_id` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `IDX_9B145DEA62922701` (`article_id`), 
    KEY `IDX_9B145DEAA76ED395` (`user_id`), 
    CONSTRAINT `article_preselection_vote_ibfk_4` FOREIGN KEY (`article_id`) REFERENCES `article` (`id`), 
    CONSTRAINT `article_preselection_vote_ibfk_5` FOREIGN KEY (`id`) REFERENCES `vote` (`id`) ON DELETE CASCADE, 
    CONSTRAINT `article_preselection_vote_ibfk_6` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

v.weight可以是+1或-1,我需要,得到的每个正票之和(+ 1)和负面投票(-1)每篇文章ID的总和。

然后我的结果应该是

article_id | vote_up | vote_down 
-----------|---------|---------- 
    1  | 36 |  20 
-----------|---------|---------- 
    68  | 12 |  56 
-----------|---------|---------- 
    25  | 90 |  12 
-----------|---------|---------- 

我可以做下面的请求得到这一结果,但它是相当沉重和200万票缓慢。

SELECT apv.article_id, COALESCE(SUM(up),0) as up, COALESCE(SUM(down),0) as down 
FROM article_preselection_vote apv 
LEFT JOIN(
    SELECT id, weight up FROM vote WHERE weight > 0 AND vote.resource_type = 'article') v1 ON apv.id = v1.id 
LEFT JOIN(
    SELECT id, weight down FROM vote WHERE weight < 0 AND vote.resource_type = 'article') v2 ON apv.id = v2.id 
WHERE apv.article_id IN (11702,11703,11704,11632,11652,11658) 
GROUP BY apv.article_id 

任何想法?

在此先感谢。

回答

1

你可以尝试一个连接:

SELECT 
    apv.article_id, 
    SUM(COALESCE(weight, 0) > 0) AS up, 
    SUM(COALESCE(weight, 0) < 0) AS down 
FROM article_preselection_vote apv 
LEFT JOIN vote 
    ON apv.id = vote.id 
    AND vote.resource_type = 'article' 
WHERE apv.article_id IN (11702, 11703, 11704, 11632, 11652, 11658) 
GROUP BY apv.article_id 

如果您需要经常计算这可能是值得的非规范化数据库和存储结果的缓存副本。

+0

谢谢您的回答,我给试试看,感谢您的建议过,但我使用的ORM,所以我真的不能进行非规范化我的架构,顺便说一句,我需要“实时”的结果,将是值得任何仍要使用一个缓存表? – Trent

+0

您可以尝试使用触发器更新缓存的结果。 –

+0

我只是想你的查询,但我得到了0,0每个资金,但是它是相当快:) – Trent

0

相反加权票的,你为什么不只是创建两个表,一个是选票和一个向下票?它会使问题复杂化的唯一因素是投票组合,它仍然是两个不同查询计数的简单总和。

2

子查询,IN (...)GROUP BY在一个查询是杀手。

你应该重新设计有一个更传统的解决方案:

  1. 有一个表票article_id, votes_up, votes_down, vote_date, ...
  2. 更新(的cron)的摘要字段在你的文章表votes_up, votes_down, ...一个UPDATE

这样一来,就可以更好地处理该行/表锁,并具有快速查询

+1

喜,实际上它是什么,我们已经做了经典的票,但是,这样的投票需要实时,因为他们习惯于“预先选择”文章,所以我不能只运行一个cron来更新文章表中的列,但感谢您的建议 – Trent

0

在坚果壳做这样的事情:

select * from article where article_id in (1,2,3); 
+------------+-----------+---------------+-----------------+ 
| article_id | title  | up_vote_count | down_vote_count | 
+------------+-----------+---------------+-----------------+ 
|   1 | article 1 |    2 |    3 | 
|   2 | article 2 |    2 |    1 | 
|   3 | article 3 |    1 |    1 | 
+------------+-----------+---------------+-----------------+ 
3 rows in set (0.00 sec) 


drop table if exists article; 
create table article 
(
article_id int unsigned not null auto_increment primary key, 
title varchar(255) not null, 
up_vote_count int unsigned not null default 0, 
down_vote_count int unsigned not null default 0 
) 
engine = innodb; 

drop table if exists article_vote; 
create table article_vote 
(
article_id int unsigned not null, 
user_id int unsigned not null, 
score tinyint not null default 0, 
primary key (article_id, user_id) 
) 
engine=innodb; 

delimiter # 

create trigger article_vote_after_ins_trig after insert on article_vote 
for each row 
begin 
if new.score < 0 then 
    update article set down_vote_count = down_vote_count + 1 where article_id = new.article_id; 
else 
    update article set up_vote_count = up_vote_count + 1 where article_id = new.article_id; 
end if; 
end# 

delimiter ; 

insert into article (title) values ('article 1'),('article 2'), ('article 3'); 

insert into article_vote (article_id, user_id, score) values 
(1,1,-1),(1,2,-1),(1,3,-1),(1,4,1),(1,5,1), 
(2,1,1),(2,2,1),(2,3,-1), 
(3,1,1),(3,5,-1); 

select * from article where article_id in (1,2,3);