MySQL LIMIT和OFFSET搞乱我的结果顺序

时间:2011-10-07 18:38:50

标签: mysql sql

我有一个大查询,最终返回并订购我想要的所有内容。

我在我的项目中坚持分页,但是现在LIMIT和OFFSET在查询中,结果的排序方式不同。 有什么想法吗?

添加更多信息 -

我的表格就是这样:

ANIMAL                       FOOD
AID   AnimalName             FID   FoodName       BuyOn           AID
------------------            ------------------------------------------
 1       Dog                   1     DogBix         2011-11-27     1         
 2       Cat                   2     Tuna           2011-11-11     2
 3       Rabbit                3     Bones          2012-06-08     1
 4       Bird                  4     CatBix         2010-06-04     2  
                               5     Bird Seed      2011-12-12     4
                               6     Carrots        2011-05-04     3
                               7     Pedigree Chum  2011-02-08     1
                               8     Rabbit Mix     2011-09-02     3

我希望按此顺序输出以下内容:

AnimalName   FoodName       BuyOn
----------------------------------------
Cat          Tuna           2011-11-11
Cat          CatBix         2010-06-04

Dog          DogBix         2011-11-27
Dog          Bones          2012-06-08
Dog          Pedigree Chum  2011-02-08

Bird         Bird Seed      2011-12-12

Rabbit       Rabbit Mix     2011-09-02
Rabbit       Carrots        2011-05-04

按动物排序和分组,未来日期最接近现在。 然后,动物的相关日期按最接近现在上升的日期排序,接着是最接近现在下降的过去日期。

我得到的查询是:

$offset = ~whatever page I'm on * LIMIT~

$Query = "
  SELECT *
  FROM animal AS a
  INNER JOIN food AS f ON a.aid = f.fid
  INNER JOIN
  (
     SELECT f2.aid,
     MIN( IF( DATEDIFF( f2.buyOn, CURDATE() ) >= 0, DATEDIFF( f2.buyOn, CURDATE() ), 1000000 ) ) AS dateSortFuture,
     MAX( IF( DATEDIFF( f2.buyOn, CURDATE() ) < 0, DATEDIFF( f2.buyOn, CURDATE() ), -1000000 ) ) AS dateSortPast
     FROM food AS f2
     GROUP BY f2.aid
 LIMIT 5
 OFFSET ".$offset."
  )
  AS f3 ON f3.aid = a.aid
  ORDER BY f3.dateSortFuture ASC,
  f3.dateSortPast DESC,
  IF( f.buyOn >= CURDATE(), 0, 1 ) ASC,
  ABS( DATEDIFF( f.buyOn, CURDATE() ) ) ASC, animalName;";

删除LIMIT和OFFSET后,它会按我的意思运行。

可能的解决方案?:

$Query = "
  SELECT *
  FROM animal AS a
  INNER JOIN food AS f ON a.aid = f.fid
  INNER JOIN
  (
     SELECT f2.aid,
     MIN( IF( DATEDIFF( f2.buyOn, CURDATE() ) >= 0, DATEDIFF( f2.buyOn, CURDATE() ), 1000000 ) ) AS dateSortFuture,
     MAX( IF( DATEDIFF( f2.buyOn, CURDATE() ) < 0, DATEDIFF( f2.buyOn, CURDATE() ), -1000000 ) ) AS dateSortPast
     FROM food AS f2
     GROUP BY f2.aid
     ORDER BY f3.dateSortFuture ASC,
     f3.dateSortPast DESC,
     IF( f.buyOn >= CURDATE(), 0, 1 ) ASC,
     ABS( DATEDIFF( f.buyOn, CURDATE() ) ) ASC, animalName;
 LIMIT 5
 OFFSET ".$offset."
  )
  AS f3 ON f3.aid = a.aid
  ORDER BY f3.dateSortFuture ASC,
  f3.dateSortPast DESC,
  IF( f.buyOn >= CURDATE(), 0, 1 ) ASC,
  ABS( DATEDIFF( f.buyOn, CURDATE() ) ) ASC, animalName;
  ";

1 个答案:

答案 0 :(得分:1)

除非同一查询中有LIMIT,否则

OFFSETORDER BY通常不是您想要的。在示例代码中,LIMIT位于子查询中,并且该子查询中的记录是无序的。所以基本上,你从f3得到一个随机的5行。

不幸的是,到目前为止,我所知道的处理这个的最好方法是使用窗口,MySQL还不支持(AFAIK)。你可以用类似

的片段来破解类似的东西
(SELECT f9.*, (SELECT COUNT(*) FROM mytable as f99 
               WHERE f99.sortkey <= f9.sortkey) 
              AS rank
 FROM mytable AS f9) AS kludge

然后有像

这样的代码
SELECT /* lots */ FROM 
mytable join myothertable ON /* blah */
JOIN /* copy above fragment */
ON kludge.id = mytable.id AND rank<=5;