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

E.A*_*A.T 1 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 ==二进制
  • 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
Run Code Online (Sandbox Code Playgroud)

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

查询表架构:

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 */;
Run Code Online (Sandbox Code Playgroud)

Gor*_*off 5

我假设问题是你有多个具有相同值的行G.game_listening_starts.这些行可以按任何顺序返回,从一种到下一种.

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

ORDER BY ISNULL(G.game_uploading_starts), G.game_listening_starts desc, game_id
Run Code Online (Sandbox Code Playgroud)

这保证是独特的,因为group by.

我碰巧认为SQL Server文档在解释稳定排序方面做得最好:

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

查询使用的基础数据不得更改.也就是说,查询所触及的行不会更新,或者查询中的所有页面请求都是使用快照或可序列化事务隔离在单个事务中执行的.有关这些事务隔离级别的更多信息,请参阅SET TRANSACTION ISOLATION LEVEL(Transact-SQL).

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

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