2011-10-25 197 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);