2010-11-04 92 views
1

我有图像库。每张图片的评分都是基于有多少用户投票“我喜欢这个”。 MySQL表的这种笑话是这样的:MySQL查询:使用非唯一值排序列表时,获取下一个或上一个项目

id  |image  |rating 
------------------------------------ 
166  |6.png  |9 
165  |8.png  |9 
189  |1.png  |8 
171  |99.png  |8 
169  |56.png  |8 
155  |34.png  |8 
265  |7.png  |7 
754  |86.png  |6 
166  |37.png  |4 
342  |95.png  |2 
99  |35.png  |0 
76  |34.png  |0 
44  |3.png  |0 
8  |22.png  |0 

任务是:可以查看按评级排序的画廊。使用ORDER BY rating DESC, id DESC很容易列出图像,但用户点击图像时出现问题,我必须在打开的图像旁边显示“上一张图像”和“下一张图像”按钮。

假设我们现在看到的与示例表ID = 169的图像:

id  |image  |rating 
------------------------------------ 
169  |99.png  |8 

我怎么能写我的查询,以获得一张图像(ID = 171)?问题是,主要排序是rating(这不是唯一的)和次要是由唯一的id。在任何情况下,哪个查询都会给我正确的以前的图像。

我已经试过:

SELECT * 
FROM images 
WHERE rating >= 8 AND id >=169 
ORDER BY rating, id 
LIMIT 1 

,但它并没有这样做,因为id >=169必须检查只有等级相同。

我有点困惑,请帮助我。

UPDATE

想通了这一个我自己。假设我对当前记录的ratingid值,查询得到此前的纪录是:

SELECT * 
FROM `images` 
WHERE 
    (`rating` = 8 AND `id` > 169) 
    OR `rating` > 8 
ORDER BY `rating`, `id` 
LIMIT 1 

如果rating = 8 AND id > 169没有评估,然后rating > 8进入游戏。原来很简单。

谢谢大家!接受大多数赞成的答案。

+0

我不明白。你不能写另一个查询来选择下一个图像,按ID排序? – thomaux 2010-11-04 12:10:30

+0

@Anzeo nope,他不能。他的图像不是按照id排序的,而是按评分排序的。 – 2010-11-04 12:16:33

+0

如果你想获得上一个记录,你需要ORDER BY DESC。这种方式'评级'<8不会触发第一个最低评级的条目。 – theflamingskunk 2014-03-10 18:30:57

回答

5
SELECT * FROM images ORDER BY rating DESC, id LIMIT $n, 1 

其中$n是ID从0开始订购计数器可以确保后续调用始终保持相同的顺序。因此下一张图片将是$n+1,之前的$n-1

此外,如果您想要确保当前存在下一张图片(用户当然不喜欢点击“下一个”以获取404错误),您总是可以抓取2张图片。

编辑:从评论

嗯了新的要求,你可以尝试计算最小距离你当前的图像。

SELECT * from images 
WHERE id < $current[id] AND rating >= $current[rating] 
ORDER by ((abs($current[rating] - rating) << 32) + abs($current[id] - id)) 
LIMIT 1 

SELECT * from images 
WHERE id > $current[id] AND rating <= $current[rating] 
ORDER by ((abs($current[rating] - rating) << 32) + abs($current[id] - id)) 
LIMIT 1 

不要问我任何性能虽然;)

+0

我真的不明白我在哪里可以得到$ n?我的网页URl是http://example.com/gallery/15?order_by=rating,其中15是图片的ID。我不能使用“序列号”而不是ID,因为图片必须有永久链接。当前图像的ID是我所拥有的。 – 2010-11-04 13:34:26

+0

@silver看我编辑 – sfussenegger 2010-11-04 16:03:11

+0

哇,现在很酷:)管理做到这一点简单。看看问题更新。 – 2010-11-05 09:11:36

0

获取一个查询中的所有条目,将其放入一个php数组中,然后使用它来代替!

+0

我有100000图像。这会杀死服务器... – 2010-11-04 13:31:39

1

您正在寻找一种面向游标的方法。另一个非常简单的方法是通过特定的排序条件选择所有ID,将它们保存在应用程序中,然后使用ID和查询进行浏览和选择,具体选择所需的行。

+0

我认为需要存储过程的游标对于这个问题有点沉重的解决方案。 ID的另一种解决方案看起来不错,但如果我有100K记录,它将成为性能杀手。 – 2010-11-04 13:40:06

+0

100k记录x 4字节(ID)是一个杀手? 400k的原始数据对我来说似乎不是一个杀手。即使你使用不同的分类(可能是5 x 100k x 4字节),它仍然可以很好地扩展。 – 0xCAFEBABE 2010-11-04 13:46:38

+0

你也可以乘以10000每小时的请求:)无论如何,获取所有表格内容(即使它是一个int列)似乎是错误的,如果我只需要一个记录。 SQL服务器通常在这种工作上更好,然后是PHP。看来答案很简单,我无法得到它。感谢您的帮助,我认为这个解决方案将起作用。我只是希望少一些“残酷”的:) – 2010-11-04 14:08:10

1

试试这一个,更换test你的表名和更换curr.id =169当前页ID

SELECT curr.id AS currid, curr.image as curr_img, 
      prev.id AS previd, prev.image as prev_img, 
      next.id AS nextid, next.image as next_img 
    FROM test curr 
    LEFT JOIN test prev 
     ON prev.id != curr.id 
     AND (
      prev.rating > curr.rating 
      OR (
       prev.rating = curr.rating 
       AND prev.id > curr.id 
     ) 
     ) 
    LEFT JOIN test next 
     ON next.id != curr.id 
     AND (
      next.rating < curr.rating 
      OR (
        next.rating = curr.rating 
        AND next.id < curr.id 
      ) 
     ) 
    WHERE curr.id =169 
    ORDER BY prev.rating ASC , next.rating DESC , prev.id ASC , next.id DESC 
    LIMIT 1 
+0

@silverlight是我的解决方案的工作? – cww 2010-11-06 15:35:50

0

我记得一篇我在MySQL性能博客上阅读的关于pagination的文章,并敲起了这个例子,这可能会有帮助(@row_id是多余的)

drop table if exists gallery; 
create table gallery 
(
id int unsigned not null, 
image varchar(255) not null, 
rating tinyint unsigned default 0 
) 
engine=innodb; 

insert into gallery values 
(166,'6.png',9),(165,'8.png',9),(189,'1.png',8), 
(171,'99.png',8),(169,'56.png',8),(155,'34.png',8), 
(265,'7.png',7),(754,'86.png',6),(37,'37.png',4), 
(342,'95.png',2),(99 ,'35.png',0),(76 ,'34.png',0), 
(44 ,'3.png',0),(8 ,'22.png',0), (1001 ,'1001.png',0); 

drop procedure if exists list_gallery_paged; 

delimiter # 

create procedure list_gallery_paged 
(
in p_last_id int unsigned, 
in p_last_rating tinyint unsigned 
) 
proc_main:begin 

    set @row_id = 0; 

    if p_last_id <= 0 then 
     select @row_id:[email protected]_id+1 as row_id, g.* 
     from gallery g order by rating desc, id desc limit 4; 
    else 
     select @row_id:[email protected]_id+1 as row_id, g2.* 
     from gallery g inner join gallery g2 on g.id = g2.id 
     where 
     g.rating <= p_last_rating and (g.id < p_last_id or g.rating < p_last_rating) 
     order by 
     g.rating desc, g.id desc limit 4; 
    end if; 

end proc_main # 

delimiter ; 

-- in pages of 4 (use all rows) 
select g.* from gallery g order by rating desc, id desc; 
call list_gallery_paged(0,0); 
call list_gallery_paged(171,8); 
call list_gallery_paged(754,6); 
call list_gallery_paged(99,0); 
call list_gallery_paged(8,0); 

-- one at a time (use top row only) 
select g.* from gallery g order by rating desc, id desc; 
call list_gallery_paged(265,7); 
call list_gallery_paged(754,6); 
call list_gallery_paged(37,4); 
call list_gallery_paged(342,2); 
call list_gallery_paged(1001,0); 
call list_gallery_paged(99,0); 
call list_gallery_paged(76,0); 
call list_gallery_paged(44,0); 
call list_gallery_paged(8,0); 
相关问题