mysql 查询需要优化
mysql query need optimization
完成以下查询需要将近 14 秒。我有一个人 table,里面有 1M 个条目。谁能建议我如何加快查询速度并减少执行时间,例如 1、2 或 3 秒?我在下面附上详细说明。
SELECT p.id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') = DATE_FORMAT(NOW(),'%Y-%m-%d') THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') = DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 DAY), '%Y-%m-%d') THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count,
COUNT('x') AS total_users_count
FROM person p
WHERE p.app_id IN (SELECT p2.id FROM project p2 ) GROUP BY p.app_id
返回 239 行
执行时间:13.504 秒
传输时间:0.001 秒
总时间:13.505 秒
shw 为个人和项目创建 table
person CREATE TABLE `person` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`device_push_token` longtext NOT NULL,
`created_date` datetime NOT NULL,
`since_last_login` datetime NOT NULL,
`platform` smallint(6) NOT NULL,
`hwid` varchar(255) NOT NULL,
`app_id` bigint(20) NOT NULL,
`since_last_push` datetime NOT NULL,
`no_of_pushes` smallint(6) NOT NULL DEFAULT '0',
`language` varchar(50) DEFAULT NULL,
`timezone` bigint(20) DEFAULT '0',
`since_last_hour_push` datetime DEFAULT NULL,
`version` bigint(20) NOT NULL DEFAULT '1',
`active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
UNIQUE KEY `hwid` (`hwid`,`app_id`),
KEY `fk_person_platform` (`platform`),
KEY `fk_person_project` (`app_id`),
CONSTRAINT `fk_person_platform` FOREIGN KEY (`platform`) REFERENCES `platform` (`id`),
CONSTRAINT `fk_person_project` FOREIGN KEY (`app_id`) REFERENCES `project` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1310384 DEFAULT CHARSET=latin1
project CREATE TABLE `project` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`unique_id` varchar(300) NOT NULL,
`name` longtext NOT NULL,
`description` longtext,
`ios_configure` bigint(20) DEFAULT NULL,
`android_configure` bigint(20) DEFAULT NULL,
`freq_push` bigint(20) DEFAULT NULL,
`hour_push` bigint(20) DEFAULT NULL,
`push_sent` bigint(20) DEFAULT '0',
`push_opened` bigint(20) DEFAULT '0',
`version` bigint(20) NOT NULL DEFAULT '1',
`created_date` datetime NOT NULL,
`updated_date` datetime NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`project_apprater` bigint(20) DEFAULT NULL,
`type` smallint(6) NOT NULL DEFAULT '1',
`status` bigint(20) DEFAULT '1',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_id` (`unique_id`),
KEY `fk_project_ios_config` (`ios_configure`),
KEY `fk_project_android_config` (`android_configure`),
KEY `fk_project_freq_push` (`freq_push`),
KEY `fk_project_hour_push` (`hour_push`),
KEY `fk_project_apprater` (`project_apprater`),
KEY `fk_project_platform` (`type`),
KEY `name` (`status`),
CONSTRAINT `fk_project_android_config` FOREIGN KEY (`android_configure`) REFERENCES `project_configure_android` (`id`),
CONSTRAINT `fk_project_apprater` FOREIGN KEY (`project_apprater`) REFERENCES `project_apprater` (`id`),
CONSTRAINT `fk_project_freq_push` FOREIGN KEY (`freq_push`) REFERENCES `freq_push` (`id`),
CONSTRAINT `fk_project_hour_push` FOREIGN KEY (`hour_push`) REFERENCES `hour_push` (`id`),
CONSTRAINT `fk_project_ios_config` FOREIGN KEY (`ios_configure`) REFERENCES `project_configure_ios` (`id`),
CONSTRAINT `fk_project_platform` FOREIGN KEY (`type`) REFERENCES `platform` (`id`),
CONSTRAINT `name` FOREIGN KEY (`status`) REFERENCES `project_status` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=313 DEFAULT CHARSET=latin1
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY p index \N fk_person_project 8 \N 1158770 Using where
2 DEPENDENT SUBQUERY p2 unique_subquery PRIMARY PRIMARY 8 func 1 Using index
已更新完整查询
SELECT
p3.id AS id,
COALESCE(pug.active_users_count, 0) AS userCount,
p3.unique_id AS uniqueId,
p3.name,
p3.description,
DATE_FORMAT(p3.created_date, '%m-%d-%Y %T') AS createdDate,
p3.android_configure AS androidConfigure,
p3.ios_configure AS iosConfigure,
(SELECT
fp.active
FROM
freq_push fp
WHERE fp.id = p3.freq_push) AS freqActive,
(SELECT
hp.active
FROM
hour_push hp
WHERE hp.id = p3.hour_push) AS hourActive,
COALESCE(pug.total_users_count, 0) AS totalUserCount,
COALESCE(pug.today_install_count, 0) AS todayInstallCount,
COALESCE(pug.yesterday_install_count, 0) AS yesterdayInstallCount,
COALESCE(pug.month_install_count, 0) AS monthInstallCount,
COALESCE(pug.year_install_count, 0) AS yearInstallCount,
COALESCE(pug.week_install_count, 0) AS weekInstallCount,
(SELECT
plat.name
FROM
platform plat
WHERE plat.id = p3.type) AS project_type ,
ps.name
FROM
(SELECT
p.app_id,
COUNT(
CASE
WHEN p.active = 1
THEN 1
END) AS active_users_count,
COUNT(
CASE
WHEN DATE(p.created_date) = CURDATE()
THEN 1
END
) AS today_install_count,
COUNT(
CASE
WHEN DATE(p.created_date) = DATE(DATE_SUB(NOW(), INTERVAL 1 DAY))
THEN 1
END
) AS yesterday_install_count,
COUNT(
CASE
WHEN DATE(p.created_date) BETWEEN DATE_SUB(
CURDATE(),
INTERVAL DAY(LAST_DAY(NOW())) DAY
)
AND CURDATE()
THEN 1
END
) AS month_install_count,
COUNT(
CASE
WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(), INTERVAL 1 YEAR)
AND CURDATE()
THEN 1
END
) AS year_install_count,
COUNT(
CASE
WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY)
AND CURDATE()
THEN 1
END
) AS week_install_count,
COUNT('x') AS total_users_count
FROM
person p
INNER JOIN project p2
ON p.app_id = p2.id
GROUP BY p.app_id) AS pug
RIGHT JOIN project p3
ON p3.id = pug.app_id
INNER JOIN project_status ps
ON p3.status = ps.id
ORDER BY userCount DESC,
createdDate DESC
也许你可以尝试加入table。但是我不确定这是否可以将执行时间减少到 3 秒。
SELECT p.id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') = DATE_FORMAT(NOW(),'%Y-%m-%d') THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') = DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 DAY), '%Y-%m-%d') THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count,
COUNT('x') AS total_users_count
FROM person p
INNER JOIN project p2 ON p.app_id = p2.id
GROUP BY p.app_id
在这个查询中,我的理解是,您需要过去一年的所有计数,而不用担心非常旧的数据。在那种情况下,如果你的项目中有一个 project_date table 那么你可以在子查询中限制 ids,这可能有助于比旧的更好地执行。
SELECT p.id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') =DATE_FORMAT(NOW(),'%Y-%m-%d') THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') =DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 DAY), '%Y-%m-%d') THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count
FROM person p
WHERE p.app_id IN (SELECT p2.id FROM project p2 AND p2.project_date > DATE_SUB(CURDATE(),INTERVAL 1 YEAR))
GROUP BY p.app_id;
现在可以单独取每个项目Id的总计数,并与上面的计数合并。
SELECT p.id, COUNT('x') AS total_users_count
FROM person p
WHERE p.app_id IN (SELECT p2.id FROM project p2)
GROUP BY p.app_id;
希望这会有所帮助。
修改以前版本的这个答案。基本上 Mark B 是对的 被质疑。幸运的是,OP 取得了进展,时间已经从 13 秒减少到 6 秒以下。OP 说(在他自己的答案和聊天中的评论中)如果时间可以减少到 1 秒以下,他会考虑其他方法。就像我和他谈论的关于接受有些陈旧的指标一样,他可以选择陈旧的持续时间。用户的陈旧性和速度之间的权衡。
这是一种方法。
一个人使用 Create Event 创建一个事件,该事件在他选择的每个 nnn(时间段)Interval
自动触发。该事件更新了他的最终用户访问的 table。该事件本身根据他的答案运行他的查询,您将看到嵌入在下面的事件中。
架构更改
create table appIdMetrics
( -- this is the table Users hit against
appId int not null primary key,
active_users_count int not null,
today_install_count int not null,
yesterday_install_count int not null,
month_install_count int not null,
year_install_count int not null,
week_install_count int not null,
total_users_count int not null
);
create table evt_appIdMetrics
( -- this is the worktable that only the Event uses
-- while it puts together the refreshed data
-- perhaps once every 5 minutes
appId int not null primary key,
active_users_count int not null,
today_install_count int not null,
yesterday_install_count int not null,
month_install_count int not null,
year_install_count int not null,
week_install_count int not null,
total_users_count int not null
);
活动创建
drop event updateAppIdMetrics;
DELIMITER $$
CREATE EVENT updateAppIdMetrics
ON SCHEDULE
EVERY 5 MINUTE
DO BEGIN
truncate table evt_appIdMetrics; -- this is the table that only the evt has access to
-- time to refresh this table (approx 6 seconds)
-- 280 rows (count as per OP comments)
insert into evt_appIdMetrics
(appId,active_users_count,today_install_count,yesterday_install_count,
month_install_count,year_install_count,week_install_count,total_users_count)
select p.app_id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE(p.created_date)= CURDATE() THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE(p.created_date) = DATE(DATE_SUB(NOW(),INTERVAL 1 DAY)) THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count,
COUNT('x') AS total_users_count
FROM person p
INNER JOIN project p2 ON p.app_id = p2.id
GROUP BY p.app_id;
-- BEGIN LOCK (important)
-- figure out a locking scheme (work-in-progress, not completed yet)
truncate table appIdMetrics; -- this is the table users access
-- the following should take a split second on the approximately 280 rows (count as per OP comments)
insert into appIdMetrics
(appId,active_users_count,today_install_count,yesterday_install_count,
month_install_count,year_install_count,week_install_count,total_users_count)
select appId,active_users_count,today_install_count,yesterday_install_count,
month_install_count,year_install_count,week_install_count,total_users_count
from evt_appIdMetrics;
-- complete locking schema (work-in-progress, not completed yet)
-- END LOCK (important)
END;$$
DELIMITER ;
-- evt creation succeeded by passing Syntax Error check
用户与 table appIdMetrics
互动。当我有机会时,我会调整提到的锁定方案。用户的用户体验应该是瞬间的。数据刷新间隔由 OP 根据陈旧因素调整table。根据我的经验,该事件将在第一个时间间隔 之后 首次触发。所以这意味着 5 分钟。
稍后我会提供一个link用于事件管理。 编辑:是的。必须启用事件。
以下查询执行时间减少到 5 秒。你们能解释一下为什么从 DATE_FORMAT() 更改为 DATE() 会带来如此巨大的改进吗?
SELECT p.app_id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE(p.created_date)= CURDATE() THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE(p.created_date) = DATE(DATE_SUB(NOW(),INTERVAL 1 DAY)) THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count,
COUNT('x') AS total_users_count
FROM person p
INNER JOIN project p2 ON p.app_id = p2.id
GROUP BY p.app_id
另一种优化思想...在子查询中,计算一次多少天前created_date
,作为一个整数。然后在外部查询中做更有效的
age <= 365 AS year_install_count,
age <= 7 AS weel_install_count,
...
请注意,x <= y
是一个 "Boolean",它显示为“1”表示真,“0”表示假。因此,无需重复 DATE_SUB
、DATE_FORMAT
或更晚的 COALESCE
等
要获得 age
,请尝试 DATEDIFF(created_date, CURRDATE)
或尝试 TO_DAYS(CURRDATE()) - TO_DAYS(CURRDATE())
。警告:它可能会关闭 1.
完成以下查询需要将近 14 秒。我有一个人 table,里面有 1M 个条目。谁能建议我如何加快查询速度并减少执行时间,例如 1、2 或 3 秒?我在下面附上详细说明。
SELECT p.id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') = DATE_FORMAT(NOW(),'%Y-%m-%d') THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') = DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 DAY), '%Y-%m-%d') THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count,
COUNT('x') AS total_users_count
FROM person p
WHERE p.app_id IN (SELECT p2.id FROM project p2 ) GROUP BY p.app_id
返回 239 行
执行时间:13.504 秒 传输时间:0.001 秒 总时间:13.505 秒
shw 为个人和项目创建 table
person CREATE TABLE `person` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`device_push_token` longtext NOT NULL,
`created_date` datetime NOT NULL,
`since_last_login` datetime NOT NULL,
`platform` smallint(6) NOT NULL,
`hwid` varchar(255) NOT NULL,
`app_id` bigint(20) NOT NULL,
`since_last_push` datetime NOT NULL,
`no_of_pushes` smallint(6) NOT NULL DEFAULT '0',
`language` varchar(50) DEFAULT NULL,
`timezone` bigint(20) DEFAULT '0',
`since_last_hour_push` datetime DEFAULT NULL,
`version` bigint(20) NOT NULL DEFAULT '1',
`active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
UNIQUE KEY `hwid` (`hwid`,`app_id`),
KEY `fk_person_platform` (`platform`),
KEY `fk_person_project` (`app_id`),
CONSTRAINT `fk_person_platform` FOREIGN KEY (`platform`) REFERENCES `platform` (`id`),
CONSTRAINT `fk_person_project` FOREIGN KEY (`app_id`) REFERENCES `project` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1310384 DEFAULT CHARSET=latin1
project CREATE TABLE `project` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`unique_id` varchar(300) NOT NULL,
`name` longtext NOT NULL,
`description` longtext,
`ios_configure` bigint(20) DEFAULT NULL,
`android_configure` bigint(20) DEFAULT NULL,
`freq_push` bigint(20) DEFAULT NULL,
`hour_push` bigint(20) DEFAULT NULL,
`push_sent` bigint(20) DEFAULT '0',
`push_opened` bigint(20) DEFAULT '0',
`version` bigint(20) NOT NULL DEFAULT '1',
`created_date` datetime NOT NULL,
`updated_date` datetime NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`project_apprater` bigint(20) DEFAULT NULL,
`type` smallint(6) NOT NULL DEFAULT '1',
`status` bigint(20) DEFAULT '1',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_id` (`unique_id`),
KEY `fk_project_ios_config` (`ios_configure`),
KEY `fk_project_android_config` (`android_configure`),
KEY `fk_project_freq_push` (`freq_push`),
KEY `fk_project_hour_push` (`hour_push`),
KEY `fk_project_apprater` (`project_apprater`),
KEY `fk_project_platform` (`type`),
KEY `name` (`status`),
CONSTRAINT `fk_project_android_config` FOREIGN KEY (`android_configure`) REFERENCES `project_configure_android` (`id`),
CONSTRAINT `fk_project_apprater` FOREIGN KEY (`project_apprater`) REFERENCES `project_apprater` (`id`),
CONSTRAINT `fk_project_freq_push` FOREIGN KEY (`freq_push`) REFERENCES `freq_push` (`id`),
CONSTRAINT `fk_project_hour_push` FOREIGN KEY (`hour_push`) REFERENCES `hour_push` (`id`),
CONSTRAINT `fk_project_ios_config` FOREIGN KEY (`ios_configure`) REFERENCES `project_configure_ios` (`id`),
CONSTRAINT `fk_project_platform` FOREIGN KEY (`type`) REFERENCES `platform` (`id`),
CONSTRAINT `name` FOREIGN KEY (`status`) REFERENCES `project_status` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=313 DEFAULT CHARSET=latin1
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY p index \N fk_person_project 8 \N 1158770 Using where
2 DEPENDENT SUBQUERY p2 unique_subquery PRIMARY PRIMARY 8 func 1 Using index
已更新完整查询
SELECT
p3.id AS id,
COALESCE(pug.active_users_count, 0) AS userCount,
p3.unique_id AS uniqueId,
p3.name,
p3.description,
DATE_FORMAT(p3.created_date, '%m-%d-%Y %T') AS createdDate,
p3.android_configure AS androidConfigure,
p3.ios_configure AS iosConfigure,
(SELECT
fp.active
FROM
freq_push fp
WHERE fp.id = p3.freq_push) AS freqActive,
(SELECT
hp.active
FROM
hour_push hp
WHERE hp.id = p3.hour_push) AS hourActive,
COALESCE(pug.total_users_count, 0) AS totalUserCount,
COALESCE(pug.today_install_count, 0) AS todayInstallCount,
COALESCE(pug.yesterday_install_count, 0) AS yesterdayInstallCount,
COALESCE(pug.month_install_count, 0) AS monthInstallCount,
COALESCE(pug.year_install_count, 0) AS yearInstallCount,
COALESCE(pug.week_install_count, 0) AS weekInstallCount,
(SELECT
plat.name
FROM
platform plat
WHERE plat.id = p3.type) AS project_type ,
ps.name
FROM
(SELECT
p.app_id,
COUNT(
CASE
WHEN p.active = 1
THEN 1
END) AS active_users_count,
COUNT(
CASE
WHEN DATE(p.created_date) = CURDATE()
THEN 1
END
) AS today_install_count,
COUNT(
CASE
WHEN DATE(p.created_date) = DATE(DATE_SUB(NOW(), INTERVAL 1 DAY))
THEN 1
END
) AS yesterday_install_count,
COUNT(
CASE
WHEN DATE(p.created_date) BETWEEN DATE_SUB(
CURDATE(),
INTERVAL DAY(LAST_DAY(NOW())) DAY
)
AND CURDATE()
THEN 1
END
) AS month_install_count,
COUNT(
CASE
WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(), INTERVAL 1 YEAR)
AND CURDATE()
THEN 1
END
) AS year_install_count,
COUNT(
CASE
WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY)
AND CURDATE()
THEN 1
END
) AS week_install_count,
COUNT('x') AS total_users_count
FROM
person p
INNER JOIN project p2
ON p.app_id = p2.id
GROUP BY p.app_id) AS pug
RIGHT JOIN project p3
ON p3.id = pug.app_id
INNER JOIN project_status ps
ON p3.status = ps.id
ORDER BY userCount DESC,
createdDate DESC
也许你可以尝试加入table。但是我不确定这是否可以将执行时间减少到 3 秒。
SELECT p.id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') = DATE_FORMAT(NOW(),'%Y-%m-%d') THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') = DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 DAY), '%Y-%m-%d') THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count,
COUNT('x') AS total_users_count
FROM person p
INNER JOIN project p2 ON p.app_id = p2.id
GROUP BY p.app_id
在这个查询中,我的理解是,您需要过去一年的所有计数,而不用担心非常旧的数据。在那种情况下,如果你的项目中有一个 project_date table 那么你可以在子查询中限制 ids,这可能有助于比旧的更好地执行。
SELECT p.id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') =DATE_FORMAT(NOW(),'%Y-%m-%d') THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') =DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 DAY), '%Y-%m-%d') THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE_FORMAT(p.created_date,'%Y-%m-%d') BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count
FROM person p
WHERE p.app_id IN (SELECT p2.id FROM project p2 AND p2.project_date > DATE_SUB(CURDATE(),INTERVAL 1 YEAR))
GROUP BY p.app_id;
现在可以单独取每个项目Id的总计数,并与上面的计数合并。
SELECT p.id, COUNT('x') AS total_users_count
FROM person p
WHERE p.app_id IN (SELECT p2.id FROM project p2)
GROUP BY p.app_id;
希望这会有所帮助。
修改以前版本的这个答案。基本上 Mark B 是对的
这是一种方法。
一个人使用 Create Event 创建一个事件,该事件在他选择的每个 nnn(时间段)Interval
自动触发。该事件更新了他的最终用户访问的 table。该事件本身根据他的答案运行他的查询,您将看到嵌入在下面的事件中。
架构更改
create table appIdMetrics
( -- this is the table Users hit against
appId int not null primary key,
active_users_count int not null,
today_install_count int not null,
yesterday_install_count int not null,
month_install_count int not null,
year_install_count int not null,
week_install_count int not null,
total_users_count int not null
);
create table evt_appIdMetrics
( -- this is the worktable that only the Event uses
-- while it puts together the refreshed data
-- perhaps once every 5 minutes
appId int not null primary key,
active_users_count int not null,
today_install_count int not null,
yesterday_install_count int not null,
month_install_count int not null,
year_install_count int not null,
week_install_count int not null,
total_users_count int not null
);
活动创建
drop event updateAppIdMetrics;
DELIMITER $$
CREATE EVENT updateAppIdMetrics
ON SCHEDULE
EVERY 5 MINUTE
DO BEGIN
truncate table evt_appIdMetrics; -- this is the table that only the evt has access to
-- time to refresh this table (approx 6 seconds)
-- 280 rows (count as per OP comments)
insert into evt_appIdMetrics
(appId,active_users_count,today_install_count,yesterday_install_count,
month_install_count,year_install_count,week_install_count,total_users_count)
select p.app_id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE(p.created_date)= CURDATE() THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE(p.created_date) = DATE(DATE_SUB(NOW(),INTERVAL 1 DAY)) THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count,
COUNT('x') AS total_users_count
FROM person p
INNER JOIN project p2 ON p.app_id = p2.id
GROUP BY p.app_id;
-- BEGIN LOCK (important)
-- figure out a locking scheme (work-in-progress, not completed yet)
truncate table appIdMetrics; -- this is the table users access
-- the following should take a split second on the approximately 280 rows (count as per OP comments)
insert into appIdMetrics
(appId,active_users_count,today_install_count,yesterday_install_count,
month_install_count,year_install_count,week_install_count,total_users_count)
select appId,active_users_count,today_install_count,yesterday_install_count,
month_install_count,year_install_count,week_install_count,total_users_count
from evt_appIdMetrics;
-- complete locking schema (work-in-progress, not completed yet)
-- END LOCK (important)
END;$$
DELIMITER ;
-- evt creation succeeded by passing Syntax Error check
用户与 table appIdMetrics
互动。当我有机会时,我会调整提到的锁定方案。用户的用户体验应该是瞬间的。数据刷新间隔由 OP 根据陈旧因素调整table。根据我的经验,该事件将在第一个时间间隔 之后 首次触发。所以这意味着 5 分钟。
稍后我会提供一个link用于事件管理。 编辑:
以下查询执行时间减少到 5 秒。你们能解释一下为什么从 DATE_FORMAT() 更改为 DATE() 会带来如此巨大的改进吗?
SELECT p.app_id,
COUNT(CASE WHEN p.active=1 THEN 1 END) AS active_users_count,
COUNT(CASE WHEN DATE(p.created_date)= CURDATE() THEN 1 END) AS today_install_count,
COUNT(CASE WHEN DATE(p.created_date) = DATE(DATE_SUB(NOW(),INTERVAL 1 DAY)) THEN 1 END) AS yesterday_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL DAY(LAST_DAY(NOW())) DAY) AND CURDATE() THEN 1 END) AS month_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL 1 YEAR) AND CURDATE() THEN 1 END) AS year_install_count,
COUNT(CASE WHEN DATE(p.created_date) BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE() THEN 1 END) AS week_install_count,
COUNT('x') AS total_users_count
FROM person p
INNER JOIN project p2 ON p.app_id = p2.id
GROUP BY p.app_id
另一种优化思想...在子查询中,计算一次多少天前created_date
,作为一个整数。然后在外部查询中做更有效的
age <= 365 AS year_install_count,
age <= 7 AS weel_install_count,
...
请注意,x <= y
是一个 "Boolean",它显示为“1”表示真,“0”表示假。因此,无需重复 DATE_SUB
、DATE_FORMAT
或更晚的 COALESCE
等
要获得 age
,请尝试 DATEDIFF(created_date, CURRDATE)
或尝试 TO_DAYS(CURRDATE()) - TO_DAYS(CURRDATE())
。警告:它可能会关闭 1.