Mysql – is it possible to make order by clause using index in MySQL 5.7

MySQL

Now I am using this command to query in MySQL 5.7:

       SELECT max(user_name) AS user_name, 
    user_id AS user_id, 
    max(login_name) AS login_name, 
    max(nick_name) AS nick_name, 
    GROUP_CONCAT(app_name) AS app_name, 
    GROUP_CONCAT(app_mark) AS app_mark_concat, 
    sum(gain) AS gain, 
    sum(room_reward + expect_reward) AS room_reward, 
    sum(platform_profit) AS platform_profit, 
    sum(room_count) AS room_count, 
    sum(room_create_count) AS room_create_count, 
    sum(invite_count) AS invite_count, 
    sum(temp_room_count) AS temp_room_count, 
    CASE WHEN sum(online_status) > 0 THEN 1 ELSE 0 END AS online_status,
 max(account_create_time) AS account_create_time, 
    max(latest_login_time) AS latest_login_time, 
    max(first_login_device) AS first_login_device, 
    sum(total_spent) AS total_spent, 
    sum(total_gain) AS total_gain, 
    sum(trans_into_count) AS trans_into_count, 
    sum(trans_out_count) AS trans_out_count, 
    sum(trans_into_amount) AS trans_into_amount, 
    sum(trans_out_amount) AS trans_out_amount, 
    max(status) AS status, 
    max(current_login_device) AS current_login_device, max(current_login_device_name) AS current_login_device_name, max(registered_time) AS registered_time, 
    avg(is_user_logined) AS is_user_logined, 
    sum(round_count) AS round_count, 
    avg(wallet_remain_count) AS wallet_remain_count, 
    max(ip_location) AS ip_location, 
    max(lastest_login_ip) AS lastest_login_ip, 
    sum(entry_app_count) AS entry_app_count, 
    sum(entry_house_count) AS entry_house_count, 
    sum(entry_room_count) AS entry_room_count 
    FROM report_user 
    WHERE report_user.tenant_id = 1 
    GROUP BY user_id, statistic_date  
        ORDER BY registered_time DESC
        LIMIT 10

when I am not using ORDER BY registered_time DESD clause, the query response very fast, but when I add the order by clause, the query could not reponse. there have 400000 lines in my table.This is the index I am created now:

CREATE INDEX report_user_userid_staticdate_idx 
ON report_user
(
    user_id,
    statistic_date,
    registered_time,
    tenant_id,
    is_user_logined
);

this is the table DML:

CREATE TABLE `report_user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `app_name` varchar(256) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '应用名称',
  `app_id` bigint(20) NOT NULL COMMENT '应用ID',
  `app_mark` varchar(256) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '应用标识',
  `created_time` bigint(20) NOT NULL COMMENT '创建时间',
  `updated_time` bigint(20) NOT NULL COMMENT '更新时间',
  `creator` bigint(20) NOT NULL DEFAULT '-1' COMMENT '创建者',
  `sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
  `deleted` int(11) NOT NULL DEFAULT '0' COMMENT '是否删除',
  `status` int(11) NOT NULL DEFAULT '0' COMMENT '状态(1:正常 -1:锁定)',
  `online_status` int(11) NOT NULL DEFAULT '0' COMMENT '在线状态(0:离线 1:在线)',
  `room_count` int(11) NOT NULL DEFAULT '0' COMMENT '创建的普通房间数',
  `room_create_count` int(11) NOT NULL DEFAULT '0' COMMENT '创建的普通房间数次数',
  `invite_count` int(11) NOT NULL DEFAULT '0' COMMENT '拉新人数',
  `temp_room_count` int(11) NOT NULL DEFAULT '0' COMMENT '临时房间次数',
  `latest_login_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '最后登陆时间',
  `latest_active_time` bigint(20) DEFAULT '0' COMMENT '最后活跃时间',
  `first_login_device` int(11) NOT NULL DEFAULT '-1' COMMENT '1.IOS 2:Android 3.H5 4:PC',
  `current_login_device` int(11) DEFAULT '-1' COMMENT '1.IOS 2:Android 3.H5 4:PC',
  `current_login_device_name` varchar(256) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '设备名称',
  `account_create_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '账户创建时间',
  `wallet_remain_count` int(11) NOT NULL DEFAULT '0' COMMENT '游戏钱包余额',
  `trans_into_count` int(11) NOT NULL DEFAULT '0' COMMENT '转入次数',
  `trans_out_count` int(11) NOT NULL DEFAULT '0' COMMENT '转出次数',
  `trans_into_amount` int(11) NOT NULL DEFAULT '0' COMMENT '转入金额',
  `trans_out_amount` int(11) NOT NULL DEFAULT '0' COMMENT '转出金额',
  `user_name` varchar(256) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '用户名',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `nick_name` varchar(256) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户昵称',
  `login_name` varchar(256) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户登录名',
  `back_money` bigint(20) DEFAULT '0' COMMENT '金额',
  `registered_time` bigint(20) DEFAULT NULL COMMENT '注册时间',
  `tenant_id` bigint(20) NOT NULL COMMENT '租户ID',
  `effective_bets` bigint(20) NOT NULL DEFAULT '0' COMMENT '有效',
  `is_user_logined` int(11) NOT NULL DEFAULT '0' COMMENT '用户是否登录过系统',
  `round_count` bigint(20) DEFAULT '0' COMMENT '玩游戏的局数',
  `lastest_login_ip` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '最后一次登录IP',
  `ip_location` varchar(11) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'IP所属地址',
  `entry_app_count` int(11) NOT NULL DEFAULT '0' COMMENT '进入过的游戏数(进入游戏房间才算数)',
  `statistic_time` bigint(11) NOT NULL COMMENT '统计时间',
  `gain` bigint(20) DEFAULT '0',
  `entry_house_count` int(11) DEFAULT '0' COMMENT '进入大厅次数',
  `entry_room_count` int(11) DEFAULT '0' COMMENT '进入房间次数',
  `statistic_date` varchar(16) COLLATE utf8mb4_bin GENERATED ALWAYS AS (date_format(from_unixtime((`statistic_time` / 1000)),'%Y-%m-%d')) STORED,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`,`app_id`,`statistic_time`),
  KEY `report_user_userid_staticdate_idx` (`user_id`,`statistic_date`,`registered_time`,`tenant_id`,`is_user_logined`)
) ENGINE=InnoDB AUTO_INCREMENT=439939 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='提转统计';

what should I do to speed up this query again? each user with multi lines of app.

Best Answer

Is there one row per "user"? Or many? If only one, then shouldn't user_id be the PRIMARY KEY? If many, shouldn't the user_name (and many other columns) be moved to another table? That is, split this table into two tables:

users, with PRIMARY KEY(user_id)

user_logins

And make them 1:many. (That is, one user to many logins.)

Once you have fixed that, we can address your original question.

To optimize... Turn the query inside-out. That is, start by finding the ids for the 10 items you want, then do the summation. That will significantly shrink the amount of work to be done.

SELECT ...
    FROM ( SELECT userid, statistic_date FROM ... 
               WHERE ...
               ORDER BY ...
               LIMIT 10 ) AS x
    JOIN report_user USING(userid, statistic_date)
    GROUP BY ...
    ORDER BY ...

Once you have that written and working, I'll help you make the optimal index.