MySQL查询:使用非唯一值排序列表时获取下一个或上一个项目

时间:2010-11-04 12:02:28

标签: php 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

我有点困惑,请帮帮我。

更新

自己想出这个。假设我有当前记录的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会发挥作用。结果很简单。

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

5 个答案:

答案 0 :(得分:5)

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

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

此外,如果您想确保当前图像有下一张图像,您可以随时获取2张图像(用户当然不喜欢点击“下一步”以获得404错误)。

编辑:评论的新要求

嗯,您可以尝试计算与当前图像的最小距离。

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

不要问我任何关于表现的事情;)

答案 1 :(得分:1)

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

答案 2 :(得分: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

答案 3 :(得分:0)

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

答案 4 :(得分: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:=@row_id+1 as row_id, g.* 
        from gallery g order by rating desc, id desc limit 4;
    else
      select @row_id:=@row_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);