Question

I'm trying to break down and re-write a view that had been created by a long gone developer. The query takes well over three minuites to access, I'm assuming from all the CONCATs.

CREATE VIEW `active_users_over_time` AS 
select 
   `users_activity`.`date` AS `date`,
   time_format(
        addtime( 
            concat(`users_activity`.`date`,' ',`users_activity`.`time`),
            concat('0 ',sec_to_time(`users_activity`.`duration_checkout`),'.0')
        ),'%H:%i:%s') AS `time`,
   `users_activity`.`username` AS `username`,
   count(addtime(concat(`users_activity`.`date`,' ',`users_activity`.`time`),
   concat('0 ',sec_to_time(`users_activity`.`duration_checkout`),'.0'))) AS `checkouts` 
from `users_activity` 
group by 
   concat(
       addtime(
             concat(`users_activity`.`date`,' ',`users_activity`.`time`),
             concat('0 ',sec_to_time(`users_activity`.`duration_checkout`),'.0')
       ),
       `users_activity`.`username`);

The data comes from the SQL table:

CREATE TABLE `users_activity` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `featureid` smallint(5) unsigned NOT NULL,
  `date` date NOT NULL,
  `time` time NOT NULL,
  `duration_checkout` int unsigned NOT NULL,
  `update_date` date NOT NULL,
  `username`  varchar(255) NOT NULL,
  `checkout` smallint(5) unsigned NOT NULL,
  `licid` smallint(5) unsigned NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `featureid_licid_username` (`featureid`,`licid`,`date`,`time`,`username`),
  FOREIGN KEY(featureid) REFERENCES features(id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

I'm having a hard time decifering what exactly what is needed and what isnt needed.

Anyone have any ideas? Thanks.

Was it helpful?

Solution

I think this does everything that the original query did, skipping a bunch of redundant steps:

select `date`
, `time`
, `username`
, count(1) as `checkouts`
from
(
  select 
   `users_activity`.`date` AS `date`
   ,time_format(
     addtime(`users_activity`.`date`,`users_activity`.`time`)
     + interval `users_activity`.`duration_checkout` second
     ,'%H:%i:%s'
   ) AS `time`
   ,`users_activity`.`username` AS `username`
  from `users_activity` 
) x
group by `username`, `date`, `time`

You may also want to look at what indexes are on the table to see if optimisations can be made elsewhere (e.g. if you don't already have an index on the username and date fields you'd get a lot of benefit for this query by adding one).

OTHER TIPS

You can start from rewriting GROUP BY clase from this:

group by 
   concat(
       addtime(
             concat(`users_activity`.`date`,' ',`users_activity`.`time`),
             concat('0 ',sec_to_time(`users_activity`.`duration_checkout`),'.0')
       ),
       `users_activity`.`username`);

to this one:

GROUP BY `users_activity`.`date`,
         `users_activity`.`time`,
         `users_activity`.`duration_checkout`,
         `users_activity`.`username`

This change should give some slight savings on converting dates to strings and concatenating them,
and the result of the query shouldn't change.

Then you may consider creating a composite index on GROUP BY columns.
According to this link: http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html

The most important preconditions for using indexes for GROUP BY are that all GROUP BY columns reference attributes from the same index

It means, that if we create the following index:

CREATE INDEX idx_name ON `users_activity`(
    `date`,`time`,`duration_checkout`,`username`
);

then MySql might use it to optimize GROUP BY (but there is no guarantee).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top