嗨大家好,感谢您的阅读
我的网站上有一个测验功能,它将分数,用户名和IP地址存储为最重要的列。我目前有一系列可怕的观点根据我需要的标准带回高分......
最低得分,但......只是每个测验用户的最低得分。
如果用户更改了ip,即保持相同的用户名但具有不同的IP或者如果用户保留相同的IP地址但更改用户名,则复杂性就在于此。
通过示例更容易解释。
Table with VALUES(UserID, IPA, Score)
高分查询会向您显示
得分值极不可能重复,但我想这是可能的。上面的数字被简化以解释我的难题!
任何人都可以提出一种有效的方法来删除这些重复项,因为我的表现已超过15,000条记录并且视图嘎然而止!
非常感谢。
答案 0 :(得分:3)
识别重复(UserID,IPA)
元组的出现非常简单:
SELECT s.UserID
, s.IPA
FROM mytable s
GROUP
BY s.UserID
, s.IPA
HAVING COUNT(1) > 1
要获得最低分,您可以将MIN(s.Score)
添加到选择列表中。
删除重复项有点困难,因为您似乎没有任何唯一性保证。有些人会建议您将要保留的行复制到单独的表中,然后使用重命名交换表,或者截断原始表并从新表重新加载。 (这通常是最有效的方法。)
CREATE TABLE newtable LIKE mytable ;
INSERT INTO newtable (UserID,IPA,Score)
SELECT s.UserID
, s.IPA
, MIN(Score) AS Score
FROM mytable s
GROUP
BY s.UserID
, s.IPA ;
如果您只想通过UserID识别重复项,则可以使用相同的方法。如果IPA值来自得分最低的行并不重要,那就更容易了。我可以将获取用户得分最低的行的查询放在一起。
如果要从现有表中删除行,而不在每行上添加唯一标识符(如AUTO_INCREMENT id列),也可以这样做。
这会让你中途,删除分数高于最低分的给定(UserID,IPA)的所有行:
DELETE t.*
FROM mytable t
JOIN ( SELECT s.UserID
, s.IPA
, MIN(s.Score)
FROM mytable s
GROUP
BY s.Userid
, s.IPA
) k
ON k.UserID = t.UserID
AND k.IPA = t.IPA
AND k.Score < t.Score
但是仍然会出现重复(UserID,IPA,Score)
元组重复出现的情况。如果表中没有其他列使行唯一,则删除重复项会更困难一些。 (同样,一种常见的技术是将要保留的行复制到另一个表,并交换表或从保存的行重新加载原始表。
请注意,使用MySQL,视图(存储和内联)的性能都很昂贵,因为视图实现为临时MyISAM表(MySQL称之为“派生表”)。
但相关的子查询在大型集合上可能更成问题。
所以,选择你的毒药。
如果表中有索引ON (userID, Score, IPA)
,那么我将如何得到结果集:
SELECT IF(@prev_user=t.UserID,@i:=@i+1,@i:=1) AS seq
, @prev_user := t.UserID AS UserID
, t.IPA
, t.Score
FROM mytable t
JOIN (SELECT @i := NULL, @prev_user := NULL) i
GROUP
BY t.UserID ASC
, t.Score ASC
, t.IPA ASC
HAVING seq = 1
这利用了一些特定于MySQL的功能:user_variables并保证GROUP BY将返回已排序的结果集。 (EXPLAIN输出将显示“Using index”,这意味着我们避免了排序操作,但查询仍将创建派生表。我们使用user_variables来标识每个UserID的“第一”行,并且HAVING子句将全部删除但是第一行。
测试用例:
create table mytable (UserID VARCHAR(6), IPA varchar(3), Score INT);
create index mytable_IX ON mytable (UserID, Score, IPA);
insert into mytable values ('User 1','IP1',13)
,('User 1','IP1',20)
,('User 1','IP2',30)
,('User 1','IP3',10)
,('User 2','IP4',20)
,('User 2','IP5',22)
,('User 2','IP5',15)
,('User 3','IP6',12)
,('User 3','IP6',20)
,('User 4','IP6',15)
,('User 5','IP6',11);
从结果集中消除“用户4”和“用户5”(完全不清楚为什么你会想要或者需要这样做。如果是因为那些用户在表中只有一行,那么你可以添加JOIN到子查询(内联视图),获取有多个UserID值的列表,其中有多行,如下所示:
SELECT IF(@prev_user=t.UserID,@i:=@i+1,@i:=1) AS seq
, @prev_user := t.UserID AS UserID
, t.IPA
, t.Score
FROM mytable t
JOIN ( SELECT d.UserID
FROM mytable d
GROUP
BY d.UserID
HAVING COUNT(1) > 1
) m
ON m.UserID = t.UserID
CROSS
JOIN (SELECT @i := NULL, @prev_user := NULL) i
GROUP
BY t.UserID ASC
, t.Score ASC
, t.IPA ASC
HAVING seq = 1