优化连接多个行计数子查询的视图模式

时间:2015-05-23 14:31:51

标签: mysql subquery views database-performance

我创建了一个表votes,其中包含storeiditemiduseridvote列,其范围为1到5。 / p>

我希望对此表有三种观点:

一个,store_ratings,列storeid和5列onestar,... fivestars,计算第一列,2s上每个商店的1的数量对于两个人......等等。

另一个相同,但改为使用product_ratingsitemid

第三个,与其他两个相同,但改为使用user_ratingsuserid

只要我最终得到这三个视图,并且每次投票只执行一次插入操作,我就可以使用任何模式。

我使用的架构有效,但我担心它的性能。有一个更好的方法吗?这就是我现在正在实现的方法(查看this fiddle以了解它如何与某些测试数据一起使用):

CREATE TABLE `votes` (
`storeid` INT NOT NULL, 
`itemid` INT NOT NULL, 
`userid` INT NOT NULL, 
`vote` INT NOT NULL, 
PRIMARY KEY (`storeid`, `itemid`, `userid`), 
KEY `storeid` (`storeid`), 
KEY `itemid` (`itemid`), 
KEY `userid` (`userid`), 
KEY `vote` (`vote`)
);

CREATE OR REPLACE VIEW votes_onestar AS
SELECT itemid, storeid, userid, COUNT(*) AS onestar FROM votes WHERE vote = 1 
GROUP BY itemid, storeid, userid;

CREATE OR REPLACE VIEW votes_twostars AS
SELECT itemid, storeid, userid, COUNT(*) AS twostars FROM votes WHERE vote = 2 
GROUP BY itemid, storeid, userid;

CREATE OR REPLACE VIEW votes_threestars AS
SELECT itemid, storeid, userid, COUNT(*) AS threestars FROM votes WHERE vote = 3 
GROUP BY itemid, storeid, userid;

CREATE OR REPLACE VIEW votes_fourstars AS
SELECT itemid, storeid, userid, COUNT(*) AS fourstars FROM votes WHERE vote = 4 
GROUP BY itemid, storeid, userid;

CREATE OR REPLACE VIEW votes_fivestars AS
SELECT itemid, storeid, userid, COUNT(*) AS fivestars FROM votes WHERE vote = 5 
GROUP BY itemid, storeid, userid;

CREATE OR REPLACE VIEW votes_matrix AS
SELECT 
  i1.itemid, i1.storeid, i1.userid, 
  IFNULL (s1.onestar, 0) onestar, 
  IFNULL (s2.twostars, 0) twostars, 
  IFNULL (s3.threestars, 0) threestars, 
  IFNULL (s4.fourstars, 0) fourstars, 
  IFNULL (s5.fivestars, 0) fivestars 
FROM votes i1
LEFT JOIN votes_onestar s1
ON i1.itemid = s1.itemid AND i1.storeid = s1.storeid AND i1.userid = s1.userid
LEFT JOIN votes_twostars s2
ON i1.itemid = s2.itemid AND i1.storeid = s2.storeid AND i1.userid = s2.userid
LEFT JOIN votes_threestars s3
ON i1.itemid = s3.itemid AND i1.storeid = s3.storeid AND i1.userid = s3.userid
LEFT JOIN votes_fourstars s4
ON i1.itemid = s4.itemid AND i1.storeid = s4.storeid AND i1.userid = s4.userid
LEFT JOIN votes_fivestars s5
ON i1.itemid = s5.itemid AND i1.storeid = s5.storeid AND i1.userid = s5.userid;

CREATE OR REPLACE VIEW store_ratings AS
SELECT
  storeid, 
  SUM(onestar) AS onestar, 
  SUM(twostars) AS twostars,
  SUM(threestars) AS threestars, 
  SUM(fourstars) AS fourstars, 
  SUM(fivestars) AS fivestars
FROM votes_matrix
GROUP BY storeid;

CREATE OR REPLACE VIEW user_ratings AS
SELECT
  userid, 
  SUM(onestar) AS onestar, 
  SUM(twostars) AS twostars,
  SUM(threestars) AS threestars, 
  SUM(fourstars) AS fourstars, 
  SUM(fivestars) AS fivestars
FROM votes_matrix
GROUP BY userid;

CREATE OR REPLACE VIEW product_ratings AS
SELECT
  itemid, 
  SUM(onestar) AS onestar, 
  SUM(twostars) AS twostars,
  SUM(threestars) AS threestars, 
  SUM(fourstars) AS fourstars, 
  SUM(fivestars) AS fivestars
FROM votes_matrix
GROUP BY itemid;

显而易见的替代方法是将votes_matrix视图存储为表,并将投票表构建为视图而不是相反(如this)。但是每次投票都需要5列,其中1就足够了,这似乎是浪费空间而不是非常实用的更新/插入。此外,它似乎也不太灵活:如果我决定切换到0-5而不是1-5,该怎么办;或者更糟糕的是,0-100?在第一个模型上调整5个视图比在第二个模型上调整表更容易。无论如何,我感谢任何在此之前遇到过这个问题的人的想法或经历......

1 个答案:

答案 0 :(得分:0)

使用IF(相当于Oracle' s DECODE,虽然更标准CASE,但使用了更简单的第一个模型版本,避免了所有嵌套视图和子查询1}}也没问题。我想这会做:

CREATE TABLE `votes` (
`storeid` INT NOT NULL, 
`itemid` INT NOT NULL, 
`userid` INT NOT NULL, 
`vote` INT NOT NULL, 
PRIMARY KEY (`storeid`, `itemid`, `userid`), 
KEY `storeid` (`storeid`), 
KEY `itemid` (`itemid`), 
KEY `userid` (`userid`), 
KEY `vote` (`vote`)
);


CREATE OR REPLACE VIEW store_ratings AS
SELECT
  storeid, 
  SUM(IF (vote=1,1,0)) AS onestar, 
  SUM(IF (vote=2,1,0)) AS twostars,
  SUM(IF (vote=3,1,0)) AS threestars, 
  SUM(IF (vote=4,1,0)) AS fourstars, 
  SUM(IF (vote=5,1,0)) AS fivestars
FROM votes
GROUP BY storeid;

CREATE OR REPLACE VIEW user_ratings AS
SELECT
  userid, 
  SUM(IF (vote=1,1,0)) AS onestar, 
  SUM(IF (vote=2,1,0)) AS twostars,
  SUM(IF (vote=3,1,0)) AS threestars, 
  SUM(IF (vote=4,1,0)) AS fourstars, 
  SUM(IF (vote=5,1,0)) AS fivestars
FROM votes
GROUP BY userid;

CREATE OR REPLACE VIEW product_ratings AS
SELECT
  itemid, 
  SUM(IF (vote=1,1,0)) AS onestar, 
  SUM(IF (vote=2,1,0)) AS twostars,
  SUM(IF (vote=3,1,0)) AS threestars, 
  SUM(IF (vote=4,1,0)) AS fourstars, 
  SUM(IF (vote=5,1,0)) AS fivestars
FROM votes
GROUP BY itemid;

小提琴:http://www.sqlfiddle.com/#!9/3063b/1