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

Warning: Cannot modify header information - headers already sent by (output started at /www/htdocs/v030397/mysql-qotd/wp-content/plugins/sitepress-multilingual-cms/sitepress.class.php:4991) in /www/htdocs/v030397/mysql-qotd/wp-includes/feed-rss2-comments.php on line 8
Comments for MySQL Question of the Day http://mysql-qotd.casperia.net mysql 5.0/5.1 questions for learning purposes Fri, 06 Aug 2010 16:56:36 +0000 http://wordpress.org/?v=abc hourly 1 Comment on Question 85: What is the result of the query? by plogi http://mysql-qotd.casperia.net/archives/562/comment-page-1#comment-98 plogi Fri, 06 Aug 2010 16:56:36 +0000 http://mysql-qotd.casperia.net/?p=562#comment-98 <b> <small><pre> select n,n and 3,n & 3, n mod 4, n and false,n and true from (select 0 n union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select null) v1; +------+-------+------+-------+-------+-------+ | n | n and | n & | n mod | n and | n and | | | 3 | 3 | 4 | false | true | +------+-------+------+-------+-------+-------+ | 0 | 0 | 0 | 0 | 0 | 0 | | 1 | 1 | 1 | 1 | 0 | 1 | | 2 | 1 | 2 | 2 | 0 | 1 | | 3 | 1 | 3 | 3 | 0 | 1 | | 4 | 1 | 0 | 0 | 0 | 1 | | 5 | 1 | 1 | 1 | 0 | 1 | | 6 | 1 | 2 | 2 | 0 | 1 | | 7 | 1 | 3 | 3 | 0 | 1 | | NULL | NULL | NULL | NULL | 0 | NULL | +------+-------+------+-------+-------+-------+ 9 rows in set (0.00 sec) </pre></small> And, did you get it right? "3" is a true value, that makes "n and true" and "n and 3" the same. The modulo and bit-operations are obvious and so is the fact, that an operation with NULL results in NULL. For me the most interesting part is, that TRUE AND NULL is NULL, where as FALSE AND NULL is FALSE. </b>
select n,n and 3,n & 3, n mod 4, n and false,n and true
from (select 0 n union select 1 union select 2 union
      select 3 union select 4 union select 5 union
      select 6 union select 7 union select null) v1;

+------+-------+------+-------+-------+-------+
| n    | n and | n &  | n mod | n and | n and |
|      | 3     | 3    | 4     | false | true  |
+------+-------+------+-------+-------+-------+
|    0 |     0 |    0 |     0 |     0 |     0 |
|    1 |     1 |    1 |     1 |     0 |     1 |
|    2 |     1 |    2 |     2 |     0 |     1 |
|    3 |     1 |    3 |     3 |     0 |     1 |
|    4 |     1 |    0 |     0 |     0 |     1 |
|    5 |     1 |    1 |     1 |     0 |     1 |
|    6 |     1 |    2 |     2 |     0 |     1 |
|    7 |     1 |    3 |     3 |     0 |     1 |
| NULL |  NULL | NULL |  NULL |     0 |  NULL |
+------+-------+------+-------+-------+-------+
9 rows in set (0.00 sec)


And, did you get it right?
“3″ is a true value, that makes “n and true” and “n and 3″ the same. The modulo and bit-operations are obvious and so is the fact, that an operation with NULL results in NULL.
For me the most interesting part is, that TRUE AND NULL is NULL, where as FALSE AND NULL is FALSE.

]]>
Comment on Question 80: How do you read between the rows? by plogi http://mysql-qotd.casperia.net/archives/528/comment-page-1#comment-97 plogi Thu, 15 Jul 2010 16:25:06 +0000 http://mysql-qotd.casperia.net/?p=528#comment-97 <b> Aaaaand still a subquery solution: <pre> 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 ); </pre> <b /></pre></b>
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 );

]]>
Comment on Question 80: How do you read between the rows? by plogi http://mysql-qotd.casperia.net/archives/528/comment-page-1#comment-96 plogi Thu, 15 Jul 2010 10:39:47 +0000 http://mysql-qotd.casperia.net/?p=528#comment-96 <b> 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. <small><pre> 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; </pre></pre></small> </b>
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;


]]>
Comment on Question 84: Which of the following are equivalent? by plogi http://mysql-qotd.casperia.net/archives/541/comment-page-1#comment-95 plogi Tue, 13 Jul 2010 17:21:51 +0000 http://mysql-qotd.casperia.net/?p=541#comment-95 <b> b c </b>
b c

]]>
Comment on Question 83: How to get the correct price? by plogi http://mysql-qotd.casperia.net/archives/537/comment-page-1#comment-94 plogi Mon, 12 Jul 2010 18:08:09 +0000 http://mysql-qotd.casperia.net/?p=537#comment-94 <b> This query left joins the log against the prices using the valid from/to dates. The left join is there to catch occurences, when there is no price. <small><pre> select date(v1.sold_ts) as dd,sum(v2.price),count(*), sum(if(v2.price is null,1,0)) as prices_missing from vending_log v1 left join vending_price v2 on (v1.product_id = v2.product_id and date(v1.sold_ts) between v2.valid_from and v2.valid_to) group by dd; </pre></small> </b>
This query left joins the log against the prices using the valid from/to dates.
The left join is there to catch occurences, when there is no price.

select date(v1.sold_ts) as dd,sum(v2.price),count(*),
       sum(if(v2.price is null,1,0)) as prices_missing
from vending_log v1 left join vending_price v2 on
  (v1.product_id = v2.product_id and
   date(v1.sold_ts) between v2.valid_from and v2.valid_to)
group by dd;


]]>
Comment on Question 82: More solutions for Q80? by plogi http://mysql-qotd.casperia.net/archives/535/comment-page-1#comment-93 plogi Sun, 11 Jul 2010 11:56:36 +0000 http://mysql-qotd.casperia.net/?p=535#comment-93 <b> Yes, another way of solving the problem is using a stored proecedure. There you can use the traditional approach of reading the rows in order and store the previous values in variables. <small><pre> drop procedure if exists proc_q80; delimiter // CREATE PROCEDURE proc_q80 () BEGIN declare v_cnt bigint; declare v_prev_ts timestamp; declare v_ts timestamp; declare v_diff bigint; declare v_s_diff bigint; declare v_done smallint; declare c_vendlog CURSOR FOR SELECT sold_ts FROM vending_log order by sold_ts; DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = 1; OPEN c_vendlog; set v_prev_ts = null; set v_done = 0; set v_cnt = 0; set v_s_diff = 0; REPEAT FETCH c_vendlog INTO v_ts; IF v_done = 0 THEN set v_diff = unix_timestamp(v_ts) - unix_timestamp(v_prev_ts); IF v_diff is not null THEN set v_cnt = v_cnt + 1; set v_s_diff = v_s_diff + v_diff; END IF; set v_prev_ts = v_ts; END IF; UNTIL v_done = 1 END REPEAT; close c_vendlog; SELECT v_s_diff / v_cnt as avg_diff; END // delimiter ; </pre></small> </b>
Yes, another way of solving the problem is using a stored proecedure. There you can use the traditional approach of reading the rows in order and store the previous values in variables.

drop procedure if exists proc_q80;
delimiter //
CREATE PROCEDURE proc_q80 ()
BEGIN
  declare v_cnt bigint;
  declare v_prev_ts timestamp;
  declare v_ts timestamp;
  declare v_diff bigint;
  declare v_s_diff bigint;
  declare v_done smallint;
  declare c_vendlog CURSOR FOR SELECT sold_ts FROM vending_log order by sold_ts;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = 1;

  OPEN c_vendlog;

  set v_prev_ts = null;
  set v_done = 0;
  set v_cnt = 0;
  set v_s_diff = 0;
  REPEAT
    FETCH c_vendlog INTO v_ts;
    IF v_done = 0 THEN
       set v_diff = unix_timestamp(v_ts) - unix_timestamp(v_prev_ts);
       IF v_diff is not null THEN
          set v_cnt = v_cnt + 1;
          set v_s_diff = v_s_diff + v_diff;
       END IF;
       set v_prev_ts = v_ts;
    END IF;
  UNTIL v_done = 1 END REPEAT;
  close c_vendlog;
  SELECT v_s_diff / v_cnt as avg_diff;
END
//
delimiter ;


]]>
Comment on Question 81: More solutions for Q80? by plogi http://mysql-qotd.casperia.net/archives/533/comment-page-1#comment-92 plogi Sat, 10 Jul 2010 18:09:04 +0000 http://mysql-qotd.casperia.net/?p=533#comment-92 <b> Another solution for Q80 is using a helper table and a join. The helper table is created with the correct sort order and a surrogate key is added to support the join between the rows. <small><pre> drop table if exists log1; set @a:=0; create table log1 select @a:=@a+1 as row_id,v1.* from vending_log v1 order by sold_ts; alter table log1 add primary key(row_id); select avg(unix_timestamp(v1.sold_ts)-unix_timestamp(v2.sold_ts)) as avg_diff from log1 v1, log1 v2 where v2.row_id=v1.row_id - 1; </pre></small></b>
Another solution for Q80 is using a helper table and a join.
The helper table is created with the correct sort order and a surrogate key is added to support the join between the rows.

drop table if exists log1;
set @a:=0;
create table log1
  select @a:=@a+1 as row_id,v1.*
  from vending_log v1 order by sold_ts;
alter table log1 add primary key(row_id);

select avg(unix_timestamp(v1.sold_ts)-unix_timestamp(v2.sold_ts)) as avg_diff
from log1 v1, log1 v2
where v2.row_id=v1.row_id - 1;

]]>
Comment on Question 80: How do you read between the rows? by plogi http://mysql-qotd.casperia.net/archives/528/comment-page-1#comment-91 plogi Fri, 09 Jul 2010 21:26:52 +0000 http://mysql-qotd.casperia.net/?p=528#comment-91 <b> 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. <small><pre> 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; </pre></pre></small> The next one is fast, but relies on user variables. <small><pre> 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; </pre></small> </b>
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;


]]>
Comment on Question 79: Which alternatives exist? by plogi http://mysql-qotd.casperia.net/archives/526/comment-page-1#comment-90 plogi Thu, 08 Jul 2010 17:31:08 +0000 http://mysql-qotd.casperia.net/?p=526#comment-90 <b> SQL is very powerful and there are plenty of ways to solve problems. The example below is only one possibility. It uses a join instead of the "or ... like". Instead of the subquery you could use a real table. Also there might be inventive ways of using LOCATE, REGEXP/RLIKE... <small><pre> select v1.* from mysql.help_keyword v1, (select '%TREE%' as sw union select '%TYPE%') v2 where v1.name like v2.sw; +-----------------+-------+ | help_keyword_id | name | +-----------------+-------+ | 87 | RTREE | | 107 | TYPE | | 441 | BTREE | | 445 | TYPES | +-----------------+-------+ 4 rows in set (0.03 sec) </pre></small> </b>
SQL is very powerful and there are plenty of ways to solve problems.
The example below is only one possibility. It uses a join instead of the “or … like”.
Instead of the subquery you could use a real table.
Also there might be inventive ways of using LOCATE, REGEXP/RLIKE…

select v1.* from mysql.help_keyword v1,
  (select '%TREE%' as sw union select '%TYPE%') v2
where v1.name like v2.sw;

+-----------------+-------+
| help_keyword_id | name  |
+-----------------+-------+
|              87 | RTREE |
|             107 | TYPE  |
|             441 | BTREE |
|             445 | TYPES |
+-----------------+-------+
4 rows in set (0.03 sec)


]]>
Comment on Question 78: How do you repair this table? by plogi http://mysql-qotd.casperia.net/archives/524/comment-page-1#comment-89 plogi Wed, 07 Jul 2010 19:11:17 +0000 http://mysql-qotd.casperia.net/?p=524#comment-89 <b> c Only the "USE_FRM" option of the REPAIR will help you now. Use this option only as a last resort. Afterwards, use "SHOW TABLE STATUS" to check the amount of rows and the auto_increment. If necessary, adjust the value with "ALTER TABLE t1 AUTO_INCREMENT=<value>". As with any REPAIR, make a copy of the table-files first - so that you have something left, in case the REPAIR emtpies your table. </value></b>
c

Only the “USE_FRM” option of the REPAIR will help you now.
Use this option only as a last resort.
Afterwards, use “SHOW TABLE STATUS” to check the amount of rows and the auto_increment.
If necessary, adjust the value with “ALTER TABLE t1 AUTO_INCREMENT=“.
As with any REPAIR, make a copy of the table-files first – so that you have something left, in case the REPAIR emtpies your table.

]]>