Warning: Creating default object from empty value in /www/htdocs/v030397/mysql-qotd/wp-content/plugins/sitepress-multilingual-cms/sitepress.class.php on line 4991

Warning: Creating default object from empty value in /www/htdocs/v030397/mysql-qotd/wp-content/plugins/sitepress-multilingual-cms/sitepress.class.php on line 4993
Question 80: How do you read between the rows? – MySQL Question of the Day

Skip to content

By plogi in mysql questions

Imagine you get the logs of a vending-machine.
You want to know the average time between vending-events.
The log table looks like this:

DROP TABLE IF EXISTS vending_log;
CREATE TABLE `vending_log` (
  `sold_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
   ON UPDATE CURRENT_TIMESTAMP,
  `product_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`sold_ts`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- this fills test-data
set @a:='2010-01-01 12:00:00';
insert into vending_log
  select @a:=adddate(@a,interval 1 + rand()*2500 second),
    floor(rand()*100)
  from information_schema.tables v1,
       information_schema.tables v2 limit 1000;

How do you calculate the average time between the vending-events?
 
[Just for fun]

Tags: , ,

Comment Feed

3 Responses

  1. plogi9. July 2010 @ 22:26:52


    Mysql does not have OLAP extensions or windowing functions at this point. So it is not that easy to refer to the previous row.
    Below is one way of solving the problem: With a subquery to retrieve the previous row. It works quite fine and is easy to read, but it is slow.

    select avg(vv1.diff) from
      (select *,unix_timestamp(v1.sold_ts) -
                unix_timestamp((select max(sold_ts)
                                from vending_log
                                where sold_ts < v1 .sold_ts)) as diff
       from vending_log v1) vv1;
    

    The next one is fast, but relies on user variables.

    set @a:=null;
    set @b:=null;
    select avg(vv1.diff) from
      (select @b:=@a,@a:=sold_ts, unix_timestamp(sold_ts) - unix_timestamp(@b) as diff
       from vending_log order by sold_ts) vv1;
    


  2. plogi15. July 2010 @ 11:39:47


    Here is still a variation on the subquery solution…
    Instead of selecting the max(), you can also just select 1 timestamp and limit by 1.
    I’m not fond of “limit 1″ for anything else than testing/debugging, but in this case it is faster than the “select max()” solution.

    select avg(vv1.diff) from
      (select *,unix_timestamp(v1.sold_ts) -
                unix_timestamp((select sold_ts
                                from vending_log
                                where sold_ts < v1 .sold_ts order by sold_ts desc limit 1)) as diff
       from vending_log v1) vv1;
    


  3. plogi15. July 2010 @ 17:25:06


    Aaaaand still a subquery solution:

    select avg(unix_timestamp(v1.sold_ts)-unix_timestamp(v2.sold_ts)) as avg_diff
    from vending_log v1, vending_log v2 where v2.sold_ts =
    (select sold_ts from vending_log
      where sold_ts < v1.sold_ts
      order by 1 desc limit 1 );
    

You must be logged in to post a comment.