我正在建立一个系统,在每天结束时记录用户总分(“XP”),以便玩家可以跟踪他们的进度。 目前我正在尝试编写一个查询,该查询可以根据之前X天的XP更改返回排行榜,并具有排名。我不想建立一个专用的排行榜,因为区间X可以改变。
修改
我在这里添加了一个SQL小提琴:
http://sqlfiddle.com/#!9/a7c1c/9 - 这是一个没有等级的工作版本 http://sqlfiddle.com/#!9/a7c1c/11 - 这是我可以得到的最接近排名(这不起作用,但希望清楚我正在尝试的事情)
问题:
HAVING BY
子查询中countsub
子句的方法; xp_change
列中的countsub
列不可用,因此我实际上无法比较更改在我看来,我已经错误地编写了查询,或者我错过了一些东西。我一直试图找出一种方法来删除COUNT
子查询,但到目前为止还没有任何运气。如果有人能指出我正确的方向,那就太棒了!
/修改
以下是我的架构:
帐户
CREATE TABLE `accounts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`display_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`scanned_at` datetime DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `accounts_display_name_index` (`display_name`),
KEY `accounts_last_tracked_index` (`scanned_at`),
KEY `accounts_slug_index` (`slug`)
)
account_instances
有问题的游戏有多种游戏类型,每种类型都有不同的排行榜,因此一个帐户可以有多个“实例”:
CREATE TABLE `account_instances` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`account_id` int(10) unsigned NOT NULL,
`game_type_id` int(10) unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `account_instances_account_id_game_type_id_unique` (`account_id`,`game_type_id`),
KEY `account_instances_game_type_id_foreign` (`game_type_id`),
CONSTRAINT `account_instances_game_type_id_foreign` FOREIGN KEY (`game_type_id`) REFERENCES `game_types` (`id`)
)
统计
这些是用户可以获得XP分数的统计数据:
CREATE TABLE `stats` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`display_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `stats_name_unique` (`name`),
UNIQUE KEY `stats_display_name_unique` (`display_name`),
KEY `stats_name_index` (`name`)
)
account_instance_stats
将帐户实例和统计信息与给定日期的分数(xp
)相关联:
CREATE TABLE `account_instance_stats` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`account_instance_id` int(10) unsigned NOT NULL,
`stat_id` int(10) unsigned NOT NULL,
`xp` bigint(20) DEFAULT NULL,
`date` date NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `account_instance_stats_account_instance_id_stat_id_date_unique` (`account_instance_id`,`stat_id`,`date`),
KEY `account_instance_stats_stat_id_foreign` (`stat_id`),
KEY `account_instance_stats_xp_index` (`xp`),
KEY `account_instance_stats_date_index` (`date`),
CONSTRAINT `account_instance_stats_account_instance_id_foreign` FOREIGN KEY (`account_instance_id`) REFERENCES `account_instances` (`id`),
CONSTRAINT `account_instance_stats_stat_id_foreign` FOREIGN KEY (`stat_id`) REFERENCES `stats` (`id`)
)
这是我到目前为止所写的查询。它不会运行,但希望你能看到我在这里尝试的东西:
SELECT
a.*,
SUM(ais.xp - ais2.xp) AS xp_change,
(
select count(*) FROM
(
SELECT COUNT(sub.id)
FROM account_instance_stats AS sub
LEFT JOIN account_instance_stats sub2
ON sub.account_instance_id = sub2.account_instance_id
AND sub.stat_id = sub2.stat_id
AND sub2.date = date_sub(sub.date, INTERVAL 1 day)
JOIN account_instances AS ai ON sub.account_instance_id = ai.id
WHERE ai.game_type_id = 1
AND sub.date = curdate()
AND sub.stat_id = 1
GROUP BY sub.id
HAVING SUM(sub.xp - sub2.xp) > xp_change
) AS countsub
) AS rank
FROM account_instance_stats AS ais
LEFT JOIN account_instance_stats ais2
ON ais.account_instance_id = ais2.account_instance_id
AND ais.stat_id = ais2.stat_id
AND ais2.date = date_sub(ais.date, INTERVAL 1 day)
JOIN account_instances AS ai ON ais.account_instance_id = ai.id
JOIN accounts AS a ON ai.account_id = a.id
WHERE ai.game_type_id = 1
AND ais.date = curdate()
AND ais.stat_id = 1
GROUP BY a.id
ORDER BY rank DESC
LIMIT 10;
根据时间间隔,父left join
的大部分父查询都是account_instance_stats
自身,因此我可以比较两个日期的xp
列。这个位按预期工作。我正在努力的部分是rank
子查询。这几乎执行相同的查询,但通过计算有多少帐户具有更高的xp_change
来计算排名。
谢谢!
答案 0 :(得分:1)
Here is a solution that uses SQL variables
显示此代码。
SELECT *, @rank := COALESCE(@rank + 1, 1) AS ranking
FROM (
SELECT a.ID AS ID, a.display_name AS display_name
, SUM(ais.xp - ais2.xp) AS xp_change
FROM accounts AS a
JOIN account_instances AS ai
ON ai.account_id = a.id
JOIN account_instance_stats AS ais
ON ais.account_instance_id = ai.id
LEFT JOIN account_instance_stats ais2
ON ais.account_instance_id = ais2.account_instance_id
AND ais.stat_id = ais2.stat_id
AND ais2.date = date_sub(ais.date, INTERVAL 1 day)
WHERE ai.game_type_id = 1
AND ais.date = '2016-06-02'
AND ais.stat_id = 1
GROUP BY a.id
ORDER BY xp_change DESC) AS t1
我将您的表JOIN顺序重新排列为(IMO)更合乎逻辑的顺序。
鉴于此解决方案:如果您想要一个帐户(假设ID = 2),我会将代码更改为:
SELECT *
FROM (
SELECT *, @rank := COALESCE(@rank + 1, 1) AS ranking
FROM (
SELECT a.ID AS ID, a.display_name AS display_name
, SUM(ais.xp - ais2.xp) AS xp_change
FROM accounts AS a
JOIN account_instances AS ai
ON ai.account_id = a.id
JOIN account_instance_stats AS ais
ON ais.account_instance_id = ai.id
LEFT JOIN account_instance_stats ais2
ON ais.account_instance_id = ais2.account_instance_id
AND ais.stat_id = ais2.stat_id
AND ais2.date = date_sub(ais.date, INTERVAL 1 day)
WHERE ai.game_type_id = 1
AND ais.date = '2016-06-02'
AND ais.stat_id = 1
GROUP BY a.id
ORDER BY xp_change DESC) AS t1
) AS foo
WHERE id = 2;