在LIMIT更改中从MySQL查询获得不一致的排序结果

时间:2013-04-24 20:55:58

标签: mysql sql pagination codeigniter-2

我的查询(下面两个)在我增加页面时返回不一致的排序结果(例如LIMIT 3,3到LIMIT 4,3)。请参阅下面的示例图片,注意我有两个新项目,尽管只将FROM索引更改为1。

最糟糕的是,我不能始终如一地生成任何特定的排序顺序,除了第一组:LIMIT 0,6。然后LIMIT 6,6和其他变化会返回截然不同的结果。例如,如果我更改了WANT值,我可以得到一组完全不同的项目。

示例: http://trackauthoritymusic.com/wwwroot/images/pagination-error-proof.jpg

环境:

  • echo mysql_get_server_info(); // 5.1.67-log
  • echo PHP_VERSION; // 5.4.14
  • character_set_client == utf8
  • character_set_connection == utf8
  • character_set_database == utf8
  • character_set_filesystem == binary
  • character_set_results == utf8
  • character_set_server == latin1
  • character_set_system == utf8
  • collat​​ion_connection == utf8_unicode_ci
  • collat​​ion_database == utf8_unicode_ci
  • collat​​ion_server == latin1_swedish_ci
  • 我正在使用codeigniter,但我正在使用下面的标准sql。
  • 在共享主机软件包

查询:

SELECT S.*, G.* FROM games C LEFT JOIN songs T ON G.game_id = S.game_id WHERE G.game_status != 'deleted' and G.game_status != 'hidden' AND G.group_id = 1 GROUP BY G.game_id ORDER BY ISNULL(G.game_uploading_starts), G.game_listening_starts desc LIMIT 4, 3

SELECT S.*, G.* FROM games C LEFT JOIN songs T ON G.game_id = S.game_id WHERE G.game_status != 'deleted' and G.game_status != 'hidden' AND G.group_id = 1 GROUP BY G.game_id ORDER BY (CASE WHEN G.game_uploading_starts IS NULL then 1 ELSE 0 END), G.game_listening_starts desc LIMIT 4, 3

这两个查询都会产生不一致的排序结果,而不是两者之间的简单不同集合。

查询表架构:

DROP TABLE IF EXISTS `games`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `games` (
  `game_id` mediumint(6) NOT NULL AUTO_INCREMENT,
  `group_id` mediumint(6) NOT NULL,
  `game_status` enum('playlist','game','compilation','halloffame','theme','deleted','hidden') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'hidden' COMMENT 'except deleted, this is not maintained at runtime, but on a cron job',
  `game_title` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `game_image` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `game_author_id` mediumint(6) NOT NULL,
  `game_ta_id` mediumint(6) DEFAULT NULL,
  `game_winner_id` mediumint(6) DEFAULT NULL,
  `song_id` mediumint(6) DEFAULT NULL COMMENT 'either first example or 1st place song if awarded already',
  `game_buyin` int(11) DEFAULT NULL,
  `game_minpool` int(11) DEFAULT NULL,
  `game_rating_avg` tinyint(3) DEFAULT NULL,
  `game_songs_per_user` tinyint(2) NOT NULL DEFAULT '1',
  `game_uploading_starts` int(10) DEFAULT NULL,
  `game_listening_starts` int(10) DEFAULT NULL,
  `game_rating_starts` int(10) DEFAULT NULL,
  `game_awarding_starts` int(10) DEFAULT NULL,
  `game_uploading_ends` int(10) DEFAULT NULL,
  `game_awarding_complete` int(10) DEFAULT NULL,
  `game_created` int(10) NOT NULL,
  `game_summary` varchar(2000) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`game_id`),
  KEY `game_status` (`game_status`,`song_id`)
) ENGINE=MyISAM AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;


DROP TABLE IF EXISTS `songs`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `songs` (
  `song_id` mediumint(6) NOT NULL AUTO_INCREMENT,
  `game_id` mediumint(6) NOT NULL,
  `group_id` tinyint(3) NOT NULL,
  `user_id` mediumint(6) NOT NULL,
  `user_vendor_id` tinyint(1) DEFAULT NULL,
  `receipt_id` mediumint(6) DEFAULT NULL,
  `song_status` enum('example','game','playlist','compilation','deleted','winner') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'example',
  `song_title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `song_artist` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `song_album` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `song_released` smallint(4) DEFAULT NULL,
  `song_genre` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `song_location` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `song_reason` varchar(2500) COLLATE utf8_unicode_ci DEFAULT NULL,
  `song_image` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `song_rating_avg` smallint(3) NOT NULL DEFAULT '0',
  `song_adjusted_avg` decimal(5,2) NOT NULL DEFAULT '0.00',
  `song_rated_count` mediumint(6) NOT NULL DEFAULT '0',
  `win_rating` smallint(3) DEFAULT NULL,
  `song_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `song_added` int(11) NOT NULL,
  `song_order` int(4) DEFAULT NULL,
  `song_dedication` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`song_id`),
  KEY `game_id` (`game_id`),
  KEY `user_id` (`user_id`),
  KEY `song_status` (`song_status`)
) ENGINE=MyISAM AUTO_INCREMENT=305 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

1 个答案:

答案 0 :(得分:5)

我认为问题在于您有多个行具有G.game_listening_starts的相同值。这些行可以按任何顺序返回,从一种到下一种。

您想要的是稳定的排序。订购不会改变的地方。要获得稳定的排序,您需要唯一标识每一行。我可能会建议:

ORDER BY ISNULL(G.game_uploading_starts), G.game_listening_starts desc, game_id

由于group by

,这保证是唯一的

我碰巧认为SQL Server documentation最能解释稳定的种类:

  

使用OFFSET和查询请求之间获得稳定的结果   FETCH,必须满足以下条件:

     

基础数据   查询使用的一定不能更改。也就是说,要么触摸行   由查询未更新或来自查询的所有页面请求   使用快照或在单个事务中执行   可序列化的事务隔离。有关这些的更多信息   事务隔离级别,请参阅SET TRANSACTION ISOLATION LEVEL   (处理SQL)。

     

ORDER BY子句包含列或组合   保证唯一的列。

我意识到你正在使用MySQL,但同样的想法适用。