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

环境:

  • 回声 mysql_get_server_info(); // 5.1.67-日志
  • 回声 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

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

查询表架构:

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 */;
4

1 回答 1

6

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

你想要的是一个稳定的排序。一种顺序不变的。要获得稳定的排序,您需要对每一行进行唯一标识。我可能会建议:

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

这保证是唯一的,因为group by.

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

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

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

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

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

于 2013-04-24T21:02:38.073 回答