我是PL SQL的新手。我有以下两个表:UserGame和Game。
CREATE TABLE Game (
GameID INT NOT NULL,
Name CHAR(100) NOT NULL,
Description CHAR(100) NOT NULL,
Publisher CHAR(100) NOT NULL,
AgeRating INT NOT NULL,
ImageLink CHAR(100),
WebsiteUrl CHAR(100),
AverageRating FLOAT,
OverallRanking INT,
CONSTRAINT pkGameId
PRIMARY KEY (GameID),
CONSTRAINT AgeRating CHECK (AgeRating >= 0),
CONSTRAINT OverallRankingMin CHECK (OverallRanking >= 0)
);
CREATE TABLE UserGame (
PlayerID INT NOT NULL,
GameID INT NOT NULL,
Rating INT,
RatingComment CHAR(100),
LastPlayed DATE,
HighestScore INT,
InProgress CHAR(1),
CONSTRAINT pkUserGame
PRIMARY KEY (PlayerID, GameID),
CONSTRAINT fkPlayerID
FOREIGN KEY (PlayerID)
REFERENCES Player (PlayerID),
CONSTRAINT fkGameIdTer
FOREIGN KEY (GameID)
REFERENCES Game (GameID),
CONSTRAINT RatingMin CHECK (Rating >= 0),
CONSTRAINT RatingMax CHECK (Rating <= 5),
CONSTRAINT HighestScore CHECK (HighestScore >= 0),
CONSTRAINT InProgress CHECK (InProgress IN (0,1))
);
每当玩家更新UserGame中的评分时,我想更新游戏的平均评分。
这就是我想出来的。
CREATE OR REPLACE TRIGGER averageUpdate
AFTER UPDATE OF Rating ON UserGame
BEGIN
FOR r1 in (SELECT DISTINCT GameID FROM UserGame)
LOOP
UPDATE Game
SET Game.AverageRating = (SELECT AVG(Rating) FROM UserGame WHERE GameID = r1.GameID GROUP BY GameID)
WHERE Game.GameID = r1.GameID;
END LOOP;
END averageUpdate;
但它没有用,我收到了这个错误:
Error: ORA-00900: invalid SQL statement
SQLState: 42000
ErrorCode: 900
Error occured in:
END LOOP
有人可以向我解释我做错了吗?
Justin Cave证实,我发布的代码似乎是正确的。我的设置肯定有问题。
为了确保这一点,我使用SQL Fiddle运行了查询,并取得了成功。
答案 0 :(得分:3)
你确定你发布的内容实际上是你正在运行的吗?它对我来说很好(一旦我删除了你没有提供的表的外键)。
SQL> CREATE TABLE Game (
2 GameID INT NOT NULL,
3 Name CHAR(100) NOT NULL,
4 Description CHAR(100) NOT NULL,
5 Publisher CHAR(100) NOT NULL,
6 AgeRating INT NOT NULL,
7 ImageLink CHAR(100),
8 WebsiteUrl CHAR(100),
9 AverageRating FLOAT,
10 OverallRanking INT,
11 CONSTRAINT pkGameId
12 PRIMARY KEY (GameID),
13 CONSTRAINT AgeRating CHECK (AgeRating >= 0),
14 CONSTRAINT OverallRankingMin CHECK (OverallRanking >= 0)
15 );
Table created.
SQL> ed
Wrote file afiedt.buf
1 CREATE TABLE UserGame (
2 PlayerID INT NOT NULL,
3 GameID INT NOT NULL,
4 Rating INT,
5 RatingComment CHAR(100),
6 LastPlayed DATE,
7 HighestScore INT,
8 InProgress CHAR(1),
9 CONSTRAINT fkGameIdTer
10 FOREIGN KEY (GameID)
11 REFERENCES Game (GameID),
12 CONSTRAINT RatingMin CHECK (Rating >= 0),
13 CONSTRAINT RatingMax CHECK (Rating <= 5),
14 CONSTRAINT HighestScore CHECK (HighestScore >= 0),
15 CONSTRAINT InProgress CHECK (InProgress IN (0,1))
16* )
SQL> /
Table created.
SQL> CREATE OR REPLACE TRIGGER averageUpdate
2 AFTER UPDATE OF Rating ON UserGame
3 BEGIN
4 FOR r1 in (SELECT DISTINCT GameID FROM UserGame)
5 LOOP
6 UPDATE Game
7 SET Game.AverageRating = (SELECT AVG(Rating) FROM UserGame WHERE GameID = r1.GameID GROUP BY GameID)
8 WHERE Game.GameID = r1.GameID;
9 END LOOP;
10 END averageUpdate;
11 /
Trigger created.
你可以像我在这里做的那样剪切和粘贴SQL * Plus会话,准确显示你在做什么吗?
这对您当前的问题没有任何影响。但我强烈建议您不要在此数据模型中使用char(100)
或float
数据类型。所有这些字符串都是可变长度的,因此您应该使用varchar2
。 char
是固定宽度的数据类型。 char(100)
将始终存储100个字节的数据。如果您的实际数据小于该数据,Oracle将在最后添加空格。如果您尝试在表中搜索特定值并最终得到char
比较语义,则需要确保将搜索字符串空格填充为100个字节。 varchar2
是可变宽度数据类型。它仅使用实际数据所需的空间。它没有做无意义和浪费的数据空间填充。而且你永远不必担心空间填充搜索字符串。
我还可以保证您希望评分为number
一些长度和精度的数据类型,而不是float
。浮点数本身就是不精确的,所以平均得分为4.4的游戏可能会在浮点数中表示为4.3999999999865或4.4000000000107(使数字上升)。这种评分不太可能是您的用户希望看到的。如果使用number(4,3)
,您将获得3位十进制数字的精度,您不必在数据的最低有效位中处理错误(或者如果您愿意,则不精确)。平均得分为4.4的游戏的值为4.4,而不是非常接近4.4。
从性能角度来看,我强烈建议您不要使用触发器来满足此要求。特别是不是每当任何游戏被评级时重新计算每个游戏的得分的触发器。这将无法很好地扩展,你将花费大量时间不断重新计算分数。假设您需要存储计算得分,您可能希望定期刷新,而不是在输入评级时立即刷新。如果您确实想在每次评级游戏时重新计算得分,只需重新计算评分系统中不是每个游戏的游戏得分。