List:MySQL on Win32« Previous MessageNext Message »
From:Shawn Green Date:September 12 2009 5:10am
Subject:Re: order by i joins
View as plain text  
Hello Jesper,

Jesper Palmqvist [Talarforum i Skandinavien AB] wrote:
> I have a question regarding ORDER BY in JOINs
> 
> I have 3 tables, and a query (simplifed below) where I want to return the latest
> version of tbl2.info, where tbl2.timeofchange is a DATETIME parameter specifying when
> tbl2.info was last edited. The query below does execute, but does not return what I want.
> Also I would like to have the highest value of id3 returned, that doesn't work either.
> What is wrong with the query below.
> 
> SELECT tbl1.id1, tbl2.id2, tbl2.timeofchange2, tbl2.info2, tbl3.id3
> FROM ((tbl1 INNER JOIN tbl2 ON tbl1.id1 = tbl2.id1) INNER JOIN tbl3 ON tbl2.id1 =
> tbl3.id1)
> GROUP BY tbl1.id1 ORDER BY tbl1.id1, tbl3.id3 DESC , tbl2.id2 DESC,
> tbl2.timeofchange2 DESC LIMIT 0 , 2
> 
> Returns
> id1;id2;timeofchange2;info2;id3  
> 1;1;2008-01-01 00:00:00;Foo1;1 
> 2;5;2008-05-01 00:00:00;Foo5;4 
> 
> Expected (but not returned) result:
> 1;4;2008-04-01 00:00:00;Foo4;3
> 2;5;2008-05-01 00:00:00;Foo5;4
> 
> 
> CREATE TABLE IF NOT EXISTS `tbl1` (
>   `id1` int(11) NOT NULL,
>   `name1` varchar(20) NOT NULL,
>   PRIMARY KEY  (`id1`)
> ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
> 
> 
> INSERT INTO `tbl1` (`id1`, `name1`) VALUES
> (1, 'Wolf'),
> (2, 'Dog');
> 
> CREATE TABLE IF NOT EXISTS `tbl2` (
>   `id1` int(11) NOT NULL,
>   `id2` int(11) NOT NULL,
>   `timeofchange2` datetime NOT NULL,
>   `info2` varchar(255) NOT NULL,
>   PRIMARY KEY  (`id2`),
>   KEY `id1` (`id1`),
>   KEY `timeofchange2` (`timeofchange2`)
> ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
> 
> 
> INSERT INTO `tbl2` (`id1`, `id2`, `timeofchange2`, `info2`) VALUES
> (1, 1, '2008-01-01 00:00:00', 'Foo1'),
> (1, 2, '2008-02-01 00:00:00', 'Foo2'),
> (1, 3, '2008-03-01 00:00:00', 'Foo3'),
> (1, 4, '2008-04-01 00:00:00', 'Foo4'),
> (2, 5, '2008-05-01 00:00:00', 'Foo5');
> 
> 
> CREATE TABLE IF NOT EXISTS `tbl3` (
>   `id1` int(11) NOT NULL,
>   `id3` int(11) NOT NULL,
>   `data3` varchar(100) NOT NULL,
>   PRIMARY KEY  (`id3`),
>   KEY `id1` (`id1`)
> ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
> 
> INSERT INTO `tbl3` (`id1`, `id3`, `data3`) VALUES
> (1, 1, 'Data1'),
> (1, 2, 'Data2'),
> (1, 3, 'Data3'),
> (2, 4, 'Data4');
> --
> MySQL Windows Mailing List
> For list archives: http://lists.mysql.com/win32
> To unsubscribe:    http://lists.mysql.com/win32?unsub=1
> 

This is another example of a "groupwize maximum" query pattern. There 
are dozens of ways to solve this. We even list a few in the manual:
http://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html

I prefer to break the problems into stages and use temporary tables for 
each step. But there are ways to write this query as a single statement. 
Warning: the single-statement versions are usually much slower.

# stage 1: find the latest timeofchange2 for each id1 value on tbl2
#
CREATE TEMPORARY TABLE tmp1 ENGINE=MEMORY
SELECT id1, max(timeofchange2) as latest
FROM tbl2
GROUP BY id1;

ALTER TABLE tmp1 ADD KEY(id1,latest);
#
# stage 2: find the highest 1d3 value for each 1d1 value on tbl3
#
CREATE TEMPORARY TABLE tmp2 ENGINE=MEMORY
SELECT id1, max(id3) as max3
FROM tbl3
GROUP BY id1;
#
# stage 3: build the final report based on the values we collected above
#
SELECT tmp1.id1
  , tbl2.id2
  , tmp1.timeofchange2
  , tbl2.info2
  , tmp2.id3
FROM tmp1
INNER JOIN tmp2
   ON tmp1.id1 = tmp2.id1
INNER JOIN tbl2
   ON tmp1.id1 = tbl2.id1
   AND tmp1.latest = tbl2.timeofchange2;
#
# always cleanup after yourself
#
DROP TEMPORARY TABLE tmp1, tmp2;

Let us know if you have problems converting this to work with your real 
data.

-- 
Shawn Green, MySQL Senior Support Engineer
Sun Microsystems, Inc.
Office: Blountville, TN


Thread
mysqld, mysqld-nt, mysqld-debugVaruna Seneviratna5 Oct