Selecting MySQL Rows Into Columns
Sometimes it’s nice to combine multiple database queries into one to save on round-trips, or for any other reason fetch results from separate tables in a single query without actually joining each of the tables involved. This can be done by effectively selecting the results from the rows of one query into the columns of another.
First, an example table setup; let’s say we’re collecting videos and screenshots for various websites:
CREATE TABLE IF NOT EXISTS `screenshots` (
`id` int(11) NOT NULL,
`site_id` int(11) NOT NULL,
`title` varchar(255) NOT NULL
);
INSERT INTO `screenshots` (`id`, `site_id`, `title`) VALUES(1, 1, 'Homepage');
INSERT INTO `screenshots` (`id`, `site_id`, `title`) VALUES(2, 2, 'Homepage');
INSERT INTO `screenshots` (`id`, `site_id`, `title`) VALUES(3, 1, 'Search Results');
-- --------------------------------------------------------
CREATE TABLE IF NOT EXISTS `sites` (
`id` int(11) NOT NULL,
`url` varchar(255) NOT NULL
);
INSERT INTO `sites` (`id`, `url`) VALUES(1, 'http://google.com/');
INSERT INTO `sites` (`id`, `url`) VALUES(2, 'http://yahoo.com/');
-- --------------------------------------------------------
CREATE TABLE IF NOT EXISTS `videos` (
`id` int(11) NOT NULL,
`site_id` int(11) NOT NULL,
`duration` int(11) NOT NULL
);
INSERT INTO `videos` (`id`, `site_id`, `duration`) VALUES(1, 1, 532);
INSERT INTO `videos` (`id`, `site_id`, `duration`) VALUES(1, 2, 331);
And now, the magic query, which selects the number of screenshots and the number of videos for a single site in a single query:
select
max(if(type='screenshots',num,'')) as num_screenshots,
max(if(type='videos',num,'')) as num_videos
from (
(select count(s.id) as num, 'screenshots' as type from screenshots s where s.site_id = 1)
UNION ALL
(select count(v.id) as num, 'videos' as type from videos v where v.site_id = 1)
) as foo
If you want this information for, say, all sites, we could say:
select
site_id,
max(if(type='screenshots',num,'')) as num_screenshots,
max(if(type='videos',num,'')) as num_videos
from (
(select s.site_id, count(s.id) as num, 'screenshots' as type from screenshots s where s.site_id in (1,2,3) group by site_id)
UNION ALL
(select v.site_id, count(v.id) as num, 'videos' as type from videos v where v.site_id in (1,2,3) group by site_id)
) as foo group by site_id
In Oracle something similar can be achieved using the decode or case operators.
Is there any way to do this dynamically?
Instead of this:
…
from (
(select count(s.id) as num, ’screenshots’ as type from screenshots s where s.site_id = 1)
UNION ALL
(select count(v.id) as num, ‘videos’ as type from videos v where v.site_id = 1)
) as foo
…
Do it with the records grouped from a table.
That would be cool if there were… could you give an example?
After searching for like 5 hours and playing around I have found a small way to generate it… See the following snippet:
SELECT DISTINCT
CONCAT(‘,’,build_field_name,’ AS ‘,build_field_name)
AS pivotarg
FROM aspin_build_fields
WHERE build_field_name IS NOT NULL
This generates the select statement fields so i dont see why you cant just wrapper the other sql statement with it.