优化加入多行计数子查询的视图模式

optimize schema of views joining multiple row count subqueries

我创建了一个 table votesstoreiditemiduseridvote,这是一个 INT 范围从 1 到 5.

我想对此有三观table:

一个,store_ratings,有一个列 storeid 和 5 个列 onestar,... fivestars,计算 onestar 列上每个商店的 1 的数量, 2s 表示双星...等等。

另一个完全相同,但用 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 视图存储为 table 并将投票 table 作为它的视图而不是相反的视图(如 this ).但是,让每票选 5 列,其中 1 列就足够了,这似乎是对 space 的浪费,而且对 update/insert 来说不太实用。另外它似乎也不那么灵活:如果我决定切换到 0-5 而不是 1-5 怎么办?或者,更糟的是,0-100?在第一个模型上调整 5 个视图似乎比在第二个模型上调整 table 更容易。无论如何,我很感激以前遇到过这个问题的任何人的任何想法或经验...

使用 IF(相当于 Oracle 的 DECODE,虽然更标准 CASE 也可以)。我认为这样做可以:

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;

fiddle: http://www.sqlfiddle.com/#!9/3063b/1