<?xml version="1.0" encoding="utf-8"?><!-- generator="wordpress/2.2.1" -->
<rss version="2.0" 
	xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
	<title>Comments on: MySQL restructuring data for a VIEW</title>
	<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html</link>
	<description>according to my observations, there is now sufficient reasons for greater optimism</description>
	<pubDate>Sun, 20 Jul 2008 15:47:42 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.2.1</generator>

	<item>
		<title>By: Damien Seguy</title>
		<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227448</link>
		<author>Damien Seguy</author>
		<pubDate>Fri, 23 Mar 2007 16:51:50 +0000</pubDate>
		<guid>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227448</guid>
		<description>Here is a way to turn your old table into the new one : 

select uid, 
group_concat(if(keyfld='name',valfld, '') SEPARATOR '' ) as name,
group_concat(if(keyfld='age',valfld, '') SEPARATOR '' ) as age,
group_concat(if(keyfld='iq',valfld, '') SEPARATOR '' ) as iq,
group_concat(if(keyfld='geekfu',valfld, '') SEPARATOR '' ) as geekfu
from aarontbl group by uid;

If you want NULL instead of empty string, an extra IF will do, I guess.

I tried to see if one could export the list of columns into an extra table, but there is no way to dynamically change the requested column with MySQL. Unless there is a trick I've missed.</description>
		<content:encoded><![CDATA[<p>Here is a way to turn your old table into the new one : </p>
<p>select uid,<br />
group_concat(if(keyfld=&#8217;name&#8217;,valfld, &#8221;) SEPARATOR &#8221; ) as name,<br />
group_concat(if(keyfld=&#8217;age&#8217;,valfld, &#8221;) SEPARATOR &#8221; ) as age,<br />
group_concat(if(keyfld=&#8217;iq&#8217;,valfld, &#8221;) SEPARATOR &#8221; ) as iq,<br />
group_concat(if(keyfld=&#8217;geekfu&#8217;,valfld, &#8221;) SEPARATOR &#8221; ) as geekfu<br />
from aarontbl group by uid;</p>
<p>If you want NULL instead of empty string, an extra IF will do, I guess.</p>
<p>I tried to see if one could export the list of columns into an extra table, but there is no way to dynamically change the requested column with MySQL. Unless there is a trick I&#8217;ve missed.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Lorenzo Alberton</title>
		<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227388</link>
		<author>Lorenzo Alberton</author>
		<pubDate>Fri, 23 Mar 2007 15:38:02 +0000</pubDate>
		<guid>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227388</guid>
		<description>Aaron, there's probably more than a way to do what you want, but as Philip said they're all slow as hell.
I'm going to show a possible solution, as a purely academical exercise, but I wouldn't ever use it in a production environment.
You'd better recreate your table with a better normalization. Anyway, here it is:

=================================

DELIMITER //

CREATE PROCEDURE recreate_view()
BEGIN
  DECLARE sql_statement1 TEXT;
  DECLARE sql_statement2 TEXT;
  DECLARE sql_statement3 TEXT;
  DECLARE fld_name VARCHAR(256);
  DECLARE done BOOL DEFAULT FALSE;
  DECLARE aliasname CHAR(1);
  DECLARE aliascnt INTEGER DEFAULT 66;
  DECLARE cur CURSOR FOR SELECT DISTINCT keyfld FROM aarontbl;
  DECLARE CONTINUE HANDLER FOR sqlstate '02000' SET done = TRUE;
  SET sql_statement1 = 'CREATE OR REPLACE VIEW aaronview (uid';
  SET sql_statement2 = ') AS SELECT DISTINCT A.uid';
  SET sql_statement3 = ' FROM aarontbl AS A';
  OPEN cur;
  keysloop: LOOP
    FETCH cur INTO fld_name;
    IF done THEN
      CLOSE cur;
      LEAVE keysloop;
    END IF;
    SELECT CHAR(aliascnt) INTO aliasname;
    SET aliascnt = aliascnt + 1;
    SET sql_statement1 = CONCAT(sql_statement1, ', ', fld_name);
    SET sql_statement2 = CONCAT(sql_statement2, ', ', aliasname, '.valfld', ' AS ', fld_name);
    SET sql_statement3 = CONCAT(sql_statement3, ' LEFT JOIN aarontbl AS ', aliasname,
        ' ON (A.uid = ', aliasname, '.uid AND ', aliasname, '.keyfld = \'', fld_name, '\')');

  END LOOP;
  SET @x = CONCAT(sql_statement1, sql_statement2, sql_statement3);
  PREPARE x from @x;
  EXECUTE x;
  DROP PREPARE x;
END;

//

DELIMITER ;

CALL recreate_view();

SELECT * FROM aaronview;

INSERT INTO aarontbl(uid, keyfld, valfld) VALUES (2, 'test', 'test');

CALL recreate_view();

SELECT * FROM aaronview;

=================================

Since MySQL doesn't allow dynamic SQL in stored procedures or triggers (another reason to use a proper dbms), you can't attach to aarontbl a TRIGGER firing after an INSERT and calling recreate_view(), so you have to do it yourself before your SELECT query.

I'm sure there are better solutions, which are left as an exercise for the reader ;-)</description>
		<content:encoded><![CDATA[<p>Aaron, there&#8217;s probably more than a way to do what you want, but as Philip said they&#8217;re all slow as hell.<br />
I&#8217;m going to show a possible solution, as a purely academical exercise, but I wouldn&#8217;t ever use it in a production environment.<br />
You&#8217;d better recreate your table with a better normalization. Anyway, here it is:</p>
<p>=================================</p>
<p>DELIMITER //</p>
<p>CREATE PROCEDURE recreate_view()<br />
BEGIN<br />
  DECLARE sql_statement1 TEXT;<br />
  DECLARE sql_statement2 TEXT;<br />
  DECLARE sql_statement3 TEXT;<br />
  DECLARE fld_name VARCHAR(256);<br />
  DECLARE done BOOL DEFAULT FALSE;<br />
  DECLARE aliasname CHAR(1);<br />
  DECLARE aliascnt INTEGER DEFAULT 66;<br />
  DECLARE cur CURSOR FOR SELECT DISTINCT keyfld FROM aarontbl;<br />
  DECLARE CONTINUE HANDLER FOR sqlstate &#8216;02000&#8242; SET done = TRUE;<br />
  SET sql_statement1 = &#8216;CREATE OR REPLACE VIEW aaronview (uid&#8217;;<br />
  SET sql_statement2 = &#8216;) AS SELECT DISTINCT A.uid&#8217;;<br />
  SET sql_statement3 = &#8216; FROM aarontbl AS A&#8217;;<br />
  OPEN cur;<br />
  keysloop: LOOP<br />
    FETCH cur INTO fld_name;<br />
    IF done THEN<br />
      CLOSE cur;<br />
      LEAVE keysloop;<br />
    END IF;<br />
    SELECT CHAR(aliascnt) INTO aliasname;<br />
    SET aliascnt = aliascnt + 1;<br />
    SET sql_statement1 = CONCAT(sql_statement1, &#8216;, &#8216;, fld_name);<br />
    SET sql_statement2 = CONCAT(sql_statement2, &#8216;, &#8216;, aliasname, &#8216;.valfld&#8217;, &#8216; AS &#8216;, fld_name);<br />
    SET sql_statement3 = CONCAT(sql_statement3, &#8216; LEFT JOIN aarontbl AS &#8216;, aliasname,<br />
        &#8216; ON (A.uid = &#8216;, aliasname, &#8216;.uid AND &#8216;, aliasname, &#8216;.keyfld = \&#8221;, fld_name, &#8216;\&#8217;)');</p>
<p>  END LOOP;<br />
  SET @x = CONCAT(sql_statement1, sql_statement2, sql_statement3);<br />
  PREPARE x from @x;<br />
  EXECUTE x;<br />
  DROP PREPARE x;<br />
END;</p>
<p>//</p>
<p>DELIMITER ;</p>
<p>CALL recreate_view();</p>
<p>SELECT * FROM aaronview;</p>
<p>INSERT INTO aarontbl(uid, keyfld, valfld) VALUES (2, &#8216;test&#8217;, &#8216;test&#8217;);</p>
<p>CALL recreate_view();</p>
<p>SELECT * FROM aaronview;</p>
<p>=================================</p>
<p>Since MySQL doesn&#8217;t allow dynamic SQL in stored procedures or triggers (another reason to use a proper dbms), you can&#8217;t attach to aarontbl a TRIGGER firing after an INSERT and calling recreate_view(), so you have to do it yourself before your SELECT query.</p>
<p>I&#8217;m sure there are better solutions, which are left as an exercise for the reader <img src='http://www.wormus.com/aaron/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /></p>
]]></content:encoded>
	</item>
	<item>
		<title>By: joel</title>
		<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227353</link>
		<author>joel</author>
		<pubDate>Fri, 23 Mar 2007 15:13:41 +0000</pubDate>
		<guid>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227353</guid>
		<description>The closest you could come is to use group_concat. group_concat is really a pretty awsome little function. make sure that the group_concat_max_len is set correctly, it defaults to 1024. But using that you can get something like:
mysql&#62; select group_concat(keyfld, ':', valfld order by keyfld) as data from aar
ontbl group by uid;
+------------------------------------+
&#124; data                               &#124;
+------------------------------------+
&#124; age:28,iq:50,name:Aaron            &#124;
&#124; age:17,geekfu:100,iq:110,name:John &#124;
+------------------------------------+

which is as close as you can get, but really isn't that bad. you just have to parse it at the first colon.

Pretty cool eh?

cheers,
Joel</description>
		<content:encoded><![CDATA[<p>The closest you could come is to use group_concat. group_concat is really a pretty awsome little function. make sure that the group_concat_max_len is set correctly, it defaults to 1024. But using that you can get something like:<br />
mysql&gt; select group_concat(keyfld, &#8216;:&#8217;, valfld order by keyfld) as data from aar<br />
ontbl group by uid;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| data                               |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| age:28,iq:50,name:Aaron            |<br />
| age:17,geekfu:100,iq:110,name:John |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+</p>
<p>which is as close as you can get, but really isn&#8217;t that bad. you just have to parse it at the first colon.</p>
<p>Pretty cool eh?</p>
<p>cheers,<br />
Joel</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Fabian Koehler</title>
		<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227333</link>
		<author>Fabian Koehler</author>
		<pubDate>Fri, 23 Mar 2007 14:55:29 +0000</pubDate>
		<guid>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227333</guid>
		<description>If the view is peformance critical I'd highly recommend to dynamically do an ALTER TABLE in order to add or remove columns to a normal table, which you then fill with data. A view like the one above will become very very slow.</description>
		<content:encoded><![CDATA[<p>If the view is peformance critical I&#8217;d highly recommend to dynamically do an ALTER TABLE in order to add or remove columns to a normal table, which you then fill with data. A view like the one above will become very very slow.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Aaron Wormus</title>
		<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227329</link>
		<author>Aaron Wormus</author>
		<pubDate>Fri, 23 Mar 2007 14:52:57 +0000</pubDate>
		<guid>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227329</guid>
		<description>Philip, thanks... just for my interest what would be the proper normalization for storing arbitrary data pairs?</description>
		<content:encoded><![CDATA[<p>Philip, thanks&#8230; just for my interest what would be the proper normalization for storing arbitrary data pairs?</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Philip Hofstetter</title>
		<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227265</link>
		<author>Philip Hofstetter</author>
		<pubDate>Fri, 23 Mar 2007 12:28:30 +0000</pubDate>
		<guid>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227265</guid>
		<description>Aaron, I'm sorry to say, but what you want to do is impossible. The concept of a dynamically expanding VIEW just doesn't exist - at least not in PostgreSQL or MySQL.

And Lorenzo's suggestion gets slower and slowere which every key added.

I guess the awkward way of accessing the data in this case is the price you have to pay for not-so-perfect normalization.

Philip</description>
		<content:encoded><![CDATA[<p>Aaron, I&#8217;m sorry to say, but what you want to do is impossible. The concept of a dynamically expanding VIEW just doesn&#8217;t exist - at least not in PostgreSQL or MySQL.</p>
<p>And Lorenzo&#8217;s suggestion gets slower and slowere which every key added.</p>
<p>I guess the awkward way of accessing the data in this case is the price you have to pay for not-so-perfect normalization.</p>
<p>Philip</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Aaron Wormus</title>
		<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227198</link>
		<author>Aaron Wormus</author>
		<pubDate>Fri, 23 Mar 2007 10:51:38 +0000</pubDate>
		<guid>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227198</guid>
		<description>Lorenzo, thanks... My main problem is that I was thinking that the data values were arbitrary and not defined. 

Is there any way I could add a new value row into aarontbl and then have aaronview dynamically change? Or is that just not a good idea.</description>
		<content:encoded><![CDATA[<p>Lorenzo, thanks&#8230; My main problem is that I was thinking that the data values were arbitrary and not defined. </p>
<p>Is there any way I could add a new value row into aarontbl and then have aaronview dynamically change? Or is that just not a good idea.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Lorenzo Alberton</title>
		<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227170</link>
		<author>Lorenzo Alberton</author>
		<pubDate>Fri, 23 Mar 2007 10:28:05 +0000</pubDate>
		<guid>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227170</guid>
		<description>CREATE TABLE aarontbl (
  uid int(11) NOT NULL,
  keyfld varchar(6) DEFAULT NULL,
  valfld varchar(6) DEFAULT NULL
);

INSERT INTO aarontbl (uid, keyfld, valfld) VALUES
    (1,'name','Aaron'),
    (1,'age','28'),
    (1,'iq','50'),
    (2,'name','John'),
    (2,'age','17'),
    (2,'iq','110'),
    (2,'geekfu','100');


CREATE VIEW aaronview (uid, name, age, iq, geekfu) AS
   SELECT DISTINCT a.uid,
          b.valfld AS name,
          c.valfld AS age,
          d.valfld AS iq,
          e.valfld AS geekfu
     FROM aarontbl AS a
LEFT JOIN aarontbl AS b ON (a.uid = b.uid AND b.keyfld = 'name')
LEFT JOIN aarontbl AS c ON (a.uid = c.uid AND c.keyfld = 'age')
LEFT JOIN aarontbl AS d ON (a.uid = d.uid AND d.keyfld = 'iq')
LEFT JOIN aarontbl AS e ON (a.uid = e.uid AND e.keyfld = 'geekfu');</description>
		<content:encoded><![CDATA[<p>CREATE TABLE aarontbl (<br />
  uid int(11) NOT NULL,<br />
  keyfld varchar(6) DEFAULT NULL,<br />
  valfld varchar(6) DEFAULT NULL<br />
);</p>
<p>INSERT INTO aarontbl (uid, keyfld, valfld) VALUES<br />
    (1,&#8217;name&#8217;,'Aaron&#8217;),<br />
    (1,&#8217;age&#8217;,'28&#8242;),<br />
    (1,&#8217;iq&#8217;,'50&#8242;),<br />
    (2,&#8217;name&#8217;,'John&#8217;),<br />
    (2,&#8217;age&#8217;,'17&#8242;),<br />
    (2,&#8217;iq&#8217;,'110&#8242;),<br />
    (2,&#8217;geekfu&#8217;,'100&#8242;);</p>
<p>CREATE VIEW aaronview (uid, name, age, iq, geekfu) AS<br />
   SELECT DISTINCT a.uid,<br />
          b.valfld AS name,<br />
          c.valfld AS age,<br />
          d.valfld AS iq,<br />
          e.valfld AS geekfu<br />
     FROM aarontbl AS a<br />
LEFT JOIN aarontbl AS b ON (a.uid = b.uid AND b.keyfld = &#8216;name&#8217;)<br />
LEFT JOIN aarontbl AS c ON (a.uid = c.uid AND c.keyfld = &#8216;age&#8217;)<br />
LEFT JOIN aarontbl AS d ON (a.uid = d.uid AND d.keyfld = &#8216;iq&#8217;)<br />
LEFT JOIN aarontbl AS e ON (a.uid = e.uid AND e.keyfld = &#8216;geekfu&#8217;);</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: fett</title>
		<link>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227167</link>
		<author>fett</author>
		<pubDate>Fri, 23 Mar 2007 10:24:36 +0000</pubDate>
		<guid>http://www.wormus.com/aaron/stories/2007/03/23/mysql-restructuring-data-for-a-view.html#comment-227167</guid>
		<description>well, you could go with CREATE VIEW, if you have a sufficiently new version of mysql. It's actually quite simple.</description>
		<content:encoded><![CDATA[<p>well, you could go with CREATE VIEW, if you have a sufficiently new version of mysql. It&#8217;s actually quite simple.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
