簡體   English   中英

Mysql查詢執行時間取第二個結果?

[英]Mysql Query Execution time takes second to result?

我有兩個表有以下結構

-

- 表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秒。 我在查詢中做錯了什么? 我如何快速執行查詢? 好心的建議

有兩個考慮因素

  • DATE(y.visited_time) = x.ts
    • 即使visited_time是INDEXed,也不能使用INDEX。
  • GROUP BY DATE(x.ts)ORDER BY(x.ts)
    • 即使GROUP BY DATE(y.visited_date)也不能使用索引

如果你真的加快了查詢速度,我建議添加額外的字段來存儲visited_time的DATE部分並使其成為INDEXed。 例如

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

UPDATE

那這個呢? 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

你不只是想要:

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是你的日期表。

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
;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM