2012-02-27 128 views
2

我以前的搜索结果(包含MySQL - row number in recordset?真的帮了我很大忙)并不令人满意,因为按其他列排序会覆盖本机排序@RowNumber总是递增的mysql行号实现(从1到n排序)

考虑我的简化member见下表:

row_number | id | firstname | lastname 
-------------------------------------- 
1   | 1 | Steve  | Jobs 
2   | 2 | Bill  | Gates 
3   | 3 | Rasmus | Lerdorf 
4   | 4 | Linus  | Torvalds 

导致由以下查询:

SELECT id, firstname, lastname, @RowNumber = @RowNumber + 1 AS row_number 
FROM member, (SELECT @RowNumber := 0) AS counter_table 

但是,当我要去基于比ROW_NUMBER或ID,其他的一些列排序row_number的排序会损坏:

SELECT id, firstname, lastname, @RowNumber = @RowNumber + 1 AS row_number 
FROM member, (SELECT @RowNumber := 0) AS counter_table 
ORDER BY firstname DESC 

结果:

row_number | id | firstname | lastname 
-------------------------------------- 
2   | 2 | Bill  | Gates 
4   | 4 | Linus  | Torvalds 
3   | 3 | Rasmus | Lerdorf 
1   | 1 | Steve  | Jobs 

注意:表,查询和其他任何东西都不是测试示例。只有问题本身在现实世界中!

我认为这些简单的情况很容易理解和解决。 (如果他们有任何问题,请忘记这些小错误,并考虑自己的重要问题)

+1

当然它会“腐败”。没有规则说明记录集中行号是什么,因为结果是动态创建的,但是您有一列表示行的编号。这就是人们通常在应用程序而不是数据库中执行此功能的原因。 – 2012-02-27 14:22:35

+1

rownumber不是表中的静态列,因此列上的任何排序都将优先。您可以尝试创建一个将rownumber作为其中一列的视图,然后针对该视图运行查询。 – Brian 2012-02-27 14:22:54

+1

这是因为'ORDER'在SELECT后出现。如果您在名字上添加索引,结果将按顺序返回,而不是稍后排序,因此它可以工作。它仍然闻起来。 – 2012-02-27 14:27:12

回答

0

您对ROW_NUMBER实现依赖的结果在从数据库返回已您在其中显示的顺序。

当您按名字排序时,失败的原因是因为结果是按主键的顺序从数据库返回的,所以id 1获得row_number 1,然后它们通过firstname命令(通过filesort) 。

如果你在firstname列上添加一个索引,那么结果将从数据库中按firstname而不是主键排序,所以行号会很好。

如果你在你的查询上做了EXPLAIN,你会注意到它可能使用主键作为索引,并使用filesort命令结果,因为它没有名字上的索引。在从表格中获取结果之后发生该订单。

添加上姓名索引,你会看到,它会使用在姓名索引来从已经姓名订购的数据库中的结果,所以第一个名字都会有ROW_NUMBER 1.

发挥它安全的,你可能会想要强制索引。

但是,这对于解释发生了什么以及如何解决该问题是严格的学术。

最有可能的解决方案是将您的第一个查询作为子查询执行,并在结果已经命令后在外部查询中添加行号。 某些应用程序会在用户想要更改排序顺序时重新发出查询,但是,某些应用程序会缓存结果,在应用程序中对其进行排序,并在应用程序中生成行号。

+0

非常感谢您的好解释。所以我在我的情况下得知,用户可能想按每个列进行排序,PHP方式看起来更安全(当然更简单:D)。 – 2012-02-27 14:45:57

2

这应该是显而易见的:数字行先排序,稍后再排序。

SELECT * FROM (
    SELECT id, firstname, lastname, @RowNumber = @RowNumber + 1 AS row_number 
    FROM member, (SELECT @RowNumber := 0) AS counter_table 
) AS sq 
ORDER BY firstname DESC 

哦,等等......你真的想反过来,第一顺序,编号后:

SELECT sq.*, @RowNumber = @RowNumber + 1 AS row_number FROM (
    SELECT id, firstname, lastname 
    FROM member 
    ORDER BY firstname DESC 
) AS sq 
CROSS JOIN (SELECT @RowNumber := 0) AS counter_table 
+0

CodeIgniter的Active Record不是100%可执行的!我认为我应该去PHP方row_number。不管怎样,谢谢你。 – 2012-02-27 14:40:30

+0

如果您的原始解决方案在CI中可实现,那么应该这样做。它没有引入任何新的语法。尽管在这里猜测,但我不知道CI – Mchl 2012-02-27 14:50:57

+1

非常感谢您的解决方案。我知道我没有提到我是一名CI人。就像我在某些地方阅读的那样,不幸的是,子查询实际上是可以实现的,但有一些技巧。无论如何,你的解决方案对我来说非常专业!我应该感谢你的帮助。 +1 – 2012-02-27 14:54:11