我有两张表有以下结构
-
visitors
CREATE TABLE IF NOT EXISTS `visitors` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`a_id` int(11) DEFAULT NULL,
`visited_by` int(11) DEFAULT '0',
`ip` varchar(30) DEFAULT NULL,
`browser_name` varchar(255) DEFAULT NULL,
`browser_short_name` varchar(20) DEFAULT NULL,
`browser_version` varchar(20) DEFAULT NULL,
`os_platform` varchar(20) DEFAULT NULL,
`visited_time` datetime DEFAULT NULL,
`is_visited` tinyint(4) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM
numbers
CREATE TABLE IF NOT EXISTS `numbers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
表格访问者的总记录为172,153,而表格编号的总记录为5,896
我正在尝试使用以下查询获取最近30天的记录
select
x.ts AS timestamp,
COUNT( y.`id`) as no_of_visitors,
DATE( y.`visited_time`) as visited_date,
MONTH( y.`visited_time`) as month_visit,
MONTHNAME( y.`visited_time`) as visit_month_name,
WEEKOFYEAR( y.`visited_time`) as visit_week_no,
YEAR( y.`visited_time`) as year_of_visit
from
(SELECT date(DATE_ADD( CURDATE( ) , INTERVAL CAST(n.id as SIGNED) - 30 DAY)) AS ts
FROM numbers n
WHERE DATE_ADD(CURDATE( ), INTERVAL CAST(n.id as SIGNED) - 30 DAY) <= CURDATE( )) x
LEFT JOIN
visitors y
ON
date(y.`visited_time`) = x.ts
GROUP BY DATE(x.ts)
order by DATE( x.ts) desc
在localhost中执行需要4.7833秒。我在查询中做错了什么? 我如何快速执行查询?请提供建议
答案 0 :(得分:1)
有两个考虑因素
DATE(y.visited_time)
= x.ts
如果你确实加快了查询速度,我建议添加额外的字段来存储visited_time的DATE部分并使其成为INDEXed。 e.g
CREATE TABLE IF NOT EXISTS `visitors` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`a_id` int(11) DEFAULT NULL,
`visited_by` int(11) DEFAULT '0',
`ip` varchar(30) DEFAULT NULL,
`browser_name` varchar(255) DEFAULT NULL,
`browser_short_name` varchar(20) DEFAULT NULL,
`browser_version` varchar(20) DEFAULT NULL,
`os_platform` varchar(20) DEFAULT NULL,
`visited_time` datetime DEFAULT NULL,
`visited_time_dt` DATE,
`is_visited` tinyint(4) DEFAULT '0',
INDEX(visited_time_dt),
PRIMARY KEY (`id`)
) ENGINE=MyISAM
然后您的最终查询如下所示并且更快(我猜)
SELECT
x.ts AS timestamp,
COUNT( y.`id`) as no_of_visitors,
DATE( y.`visited_time`) as visited_date,
MONTH( y.`visited_time`) as month_visit,
MONTHNAME( y.`visited_time`) as visit_month_name,
WEEKOFYEAR( y.`visited_time`) as visit_week_no,
YEAR( y.`visited_time`) as year_of_visit
FROM
(
SELECT
date(DATE_ADD( CURDATE( ) , INTERVAL CAST(n.id as SIGNED) - 30 DAY)) AS ts
FROM
numbers n
WHERE
DATE_ADD(CURDATE( ), INTERVAL CAST(n.id as SIGNED) - 30 DAY) <= CURDATE( )
) x LEFF JOIN visitors y ON y.`visited_time_dt` = x.ts
GROUP BY y.visited_time_dt
ORDER BY y.visited_time_dt desc
这个怎么样? z
表仅返回带有MIN,MAX值的1条记录。此MIN / MAX值与visitors.visited_date_dt
SELECT
x.ts AS timestamp,
COUNT( y.`id`) as no_of_visitors,
DATE( y.`visited_time`) as visited_date,
MONTH( y.`visited_time`) as month_visit,
MONTHNAME( y.`visited_time`) as visit_month_name,
WEEKOFYEAR( y.`visited_time`) as visit_week_no,
YEAR( y.`visited_time`) as year_of_visit
FROM numbers n
WHERE
(
SELECT
date(DATE_ADD(CURDATE(), INTERVAL CAST(n.id as SIGNED) - 30 DAY)) AS ts
FROM
numbers n
WHERE
CURDATE() >= DATE_ADD(CURDATE(), INTERVAL CAST(n.id as SIGNED) - 30 DAY
) x
LEFF JOIN visitors y ON y.`visited_time_dt` = x.ts
INNER JOIN
(
SELECT
MAX(DATE_ADD(CURDATE(), INTERVAL CAST(n.id as SIGNED) - 30 DAY)) AS max_ts,
MAX(DATE_ADD(CURDATE(), INTERVAL CAST(n.id as SIGNED) - 30 DAY)) AS min_ts,
FROM
numbers n
WHERE
CURDATE() >= DATE_ADD(CURDATE(), INTERVAL CAST(n.id as SIGNED) - 30 DAY
) z ON y.visited_time_dt BETWEEN z.min_ts AND z.max_ts
GROUP BY y.visited_time_dt
ORDER BY y.visited_time_dt desc
答案 1 :(得分:1)
你不只是想要:
select
COUNT( y.`id`) as no_of_visitors,
DATE( y.`visited_time`) as visited_date,
MONTH( y.`visited_time`) as month_visit,
MONTHNAME( y.`visited_time`) as visit_month_name,
WEEKOFYEAR( y.`visited_time`) as visit_week_no,
YEAR( y.`visited_time`) as year_of_visit
from
visitors y
where y.`visited_time` > date_add(curdate(), interval -30 day)
group by DATE(y.`visited_time`)
order by DATE(y.`visited_time`) desc
使用数字表的整个事情会导致一个非常大的连接操作,这是低效的,我认为是不必要的,尽管我真的不明白你在用表号做什么......
修改强> 如果您想要所有日期,我会在上述查询结束后加入:
select * from
all_dates a left join
(select
COUNT( y.`id`) as no_of_visitors,
DATE( y.`visited_time`) as visited_date,
MONTH( y.`visited_time`) as month_visit,
MONTHNAME( y.`visited_time`) as visit_month_name,
WEEKOFYEAR( y.`visited_time`) as visit_week_no,
YEAR( y.`visited_time`) as year_of_visit
from
visitors y
where y.`visited_time` > date_add(curdate(), interval -30 day)
group by DATE(y.`visited_time`)
order by DATE(y.`visited_time`) desc) b
on a.date = b.visited_date;
显然all_dates是你的日期表。
答案 2 :(得分:0)
在visited_time
CREATE INDEX `visited_time_ix` on visitors( `visited_time` );
并尝试此查询:
select
x.ts AS timestamp,
COUNT( y.`id`) as no_of_visitors,
DATE( y.`visited_time`) as visited_date,
MONTH( y.`visited_time`) as month_visit,
MONTHNAME( y.`visited_time`) as visit_month_name,
WEEKOFYEAR( y.`visited_time`) as visit_week_no,
YEAR( y.`visited_time`) as year_of_visit
from
(SELECT date(DATE_ADD( CURDATE( ) , INTERVAL CAST(n.id as SIGNED) - 30 DAY)) AS ts
FROM numbers n
WHERE DATE_ADD(CURDATE( ), INTERVAL CAST(n.id as SIGNED) - 30 DAY) <= CURDATE( )) x
LEFT JOIN
visitors y
ON
date(y.`visited_time`) = x.ts
WHERE y.`visited_time` >= ( SELECT min( date(DATE_ADD( CURDATE( ) , INTERVAL CAST(n.id as SIGNED) - 30 DAY)) )
FROM numbers n
WHERE DATE_ADD(CURDATE( ), INTERVAL CAST(n.id as SIGNED) - 30 DAY) <= CURDATE( )
)
AND y.`visited_time` <= ( SELECT max( date(DATE_ADD( CURDATE( ) , INTERVAL CAST(n.id as SIGNED) - 29 DAY)) )
FROM numbers n
WHERE DATE_ADD(CURDATE( ), INTERVAL CAST(n.id as SIGNED) - 30 DAY) <= CURDATE( )
)
GROUP BY DATE(x.ts)
order by DATE( x.ts) desc
;