<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>iamtgc &#187; MySQL</title>
	<atom:link href="http://iamtgc.com/category/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://iamtgc.com</link>
	<description></description>
	<lastBuildDate>Wed, 21 Jul 2010 12:13:46 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Implementing Zip Code Proximity Functions in MySQL and&#160;PostgreSQL</title>
		<link>http://iamtgc.com/2009/01/14/implementing-zip-code-proximity-functions-in-mysql-and-postgresql/</link>
		<comments>http://iamtgc.com/2009/01/14/implementing-zip-code-proximity-functions-in-mysql-and-postgresql/#comments</comments>
		<pubDate>Wed, 14 Jan 2009 17:26:08 +0000</pubDate>
		<dc:creator>tgc</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Postgres]]></category>

		<guid isPermaLink="false">http://iamtgc.com/?p=95</guid>
		<description><![CDATA[On most retail and social networking websites (along with many others), you&#8217;ll have the capability to search for people, businesses, store locations, etc within a given distance of your location. This can be implemented in a number of ways both mathematically and programmatically. In an attempt to reduce the amount of code I (or others) [...]]]></description>
			<content:encoded><![CDATA[<p>On most retail and social networking websites (along with many others), you&#8217;ll have the capability to search for people, businesses, store locations, etc within a given distance of your location.  This can be implemented in a number of ways both mathematically and programmatically.  In an attempt to reduce the amount of code I (or others) have to write, be it in PHP, Python, or any number languages that may interface with our database, I have chosen to implement these zip code proximity and distance functions as stored procedures in the database.<br />
<span id="more-95"></span><br />
To start, you may already have your database with Zip Code coordinates, if you are looking for one, Team RedLine offers an excellent <a href="http://teamredline.com/zc">Zip Code Database</a> for $5 US that can be easily imported into the database of your choice.</p>
<p>The table that we will be using was created as follows:<br />
<code>CREATE TABLE zipcodes (
    zip varchar(5),
    lat double precision,
    lon double precision,
    city varchar(30),
    state varchar(30),
    state_abbrev varchar(2));</code></p>
<p>I use <strong>57.2958</strong> as a constant for <strong>180 / &pi;</strong> to convert between degrees and radians, beyond this I will focus on the implementation and point you to the <a href="http://en.wikipedia.org/wiki/Haversine_formula">Haversine Formula</a> and the <a href="http://en.wikipedia.org/wiki/Law_of_cosines_(spherical)">Law of Consines</a> if you wish to read more on the math.</p>
<p>First we will examine how these functions are written for PostgreSQL.  (MySQL functions are further down)</p>
<p>The first function implements the Law of Consines, and allows the user to specify which measurement (miles or kilometers) to use, to determine the distance between two coordinates.<br />
<code>CREATE FUNCTION calculate_distance(varchar, double precision, double precision, double precision, double precision) RETURNS double precision
    AS $_$
   DECLARE
      earth_radius double precision;
   BEGIN
      IF $1 = 'mi' THEN
         earth_radius := 3959.0;
      ELSIF $1 = 'km' THEN
         earth_radius := 6371.0;
      END IF;
      RETURN earth_radius * acos(sin($2 / 57.2958) * sin($4 / 57.2958) + cos($2/ 57.2958) * cos($4 / 57.2958) * cos(($5 / 57.2958) - ($3 / 57.2958)));
   END;
   $_$
    LANGUAGE plpgsql;</code><br />
The calculate_distance function can certainly be used stand alone, but in our example it is called exclusively from our zip_proximity function below.</p>
<p>zip_proximity takes a <a href="http://www.postgresql.org/docs/8.1/static/plpgsql-cursors.html">refcursor</a>, a zipcode, a distance, and a distance metric (&#8216;mi&#8217; or &#8216;km&#8217;).</p>
<p>First we retrieve the coordinates for the &#8220;home&#8221; zipcode, and query the databases, performing calculate_distance on each entry in the database, capturing only those that fall withing the given distance.  We add a field to the cursor we return, which is the distance from the home zipcode, to the zipcode which fell within our provided distance.</p>
<p><code>CREATE FUNCTION zip_proximity(refcursor, character, double precision, varchar) RETURNS refcursor
    AS $_$
   DECLARE
      home_lat float;
      home_lon float;
   BEGIN
      SELECT lat, lon INTO home_lat, home_lon FROM zipcodes WHERE zip = $2;
      OPEN $1 FOR
         SELECT zip, lat, lon, city, state, state_abbrev, calculate_distance($4, home_lat, home_lon, lat, lon) AS distance FROM zipcodes
             WHERE calculate_distance($4, home_lat, home_lon, lat, lon) &lt; $3 ORDER BY distance;
      RETURN $1;
   END;
   $_$
    LANGUAGE plpgsql;</code></p>
<p>Now we will query all zipcodes within a 3 mile radius of Mountain View, California 94043.<br />
Here is how we call our new function(s).  Since we are using cursors, we need to be in a transaction.<br />
<code>testdb=# BEGIN;
BEGIN
testdb=# SELECT zip_proximity('zc', '94043', 3, 'mi');
 zip_proximity
---------------
 zc
(1 row)

testdb=# FETCH ALL FROM zc;
  zip  |   lat    |    lon     |     city      |   state    | state_abbrev |     distance
-------+----------+------------+---------------+------------+--------------+-------------------
 94043 | 37.42337 | -122.07981 | MOUNTAIN VIEW | CALIFORNIA | CA           |                 0
 94039 | 37.41884 | -122.09124 | MOUNTAIN VIEW | CALIFORNIA | CA           | 0.701004112864842
 94035 | 37.41753 | -122.05283 | MOUNTAIN VIEW | CALIFORNIA | CA           |  1.53459076775345
 94042 | 37.39314 | -122.07827 | MOUNTAIN VIEW | CALIFORNIA | CA           |  2.09052870375317
 94041 | 37.38961 | -122.07715 | MOUNTAIN VIEW | CALIFORNIA | CA           |  2.33729808540454
 94306 | 37.41478 | -122.12139 | PALO ALTO     | CALIFORNIA | CA           |  2.35776644118448
 94303 | 37.44424 | -122.11736 | PALO ALTO     | CALIFORNIA | CA           |  2.51480871338068
(7 rows)

testdb=# END;
COMMIT</code></p>
<p>Now let&#8217;s take a look at the MySQL functions.  The calculate_distance function is essentially identical to the PostgreSQL function above.<br />
<code>DELIMITER //
CREATE FUNCTION calculate_distance(measurement varchar(2), base_lat double precision, base_lon double precision, lat double precision, lon double precision) RETURNS double precision
   BEGIN
      DECLARE earth_radius double precision;
      IF measurement = 'km' THEN
         SET earth_radius = 6371.0;
      ELSEIF measurement = 'mi' THEN
         SET earth_radius = 3959.0;
      END IF;
      RETURN earth_radius * ACOS(SIN(base_lat / 57.2958) * SIN(lat / 57.2958) + COS(base_lat / 57.2958) * COS(lat / 57.2958) * COS((lon / 57.2958) - (base_lon / 57.2958)));
   END //
DELIMITER ;</code></p>
<p>This function differs slightly from it&#8217;s Postgres counterpart.  Since MySQL does not currently support functions that return a cursor, we will create a procedure which will execute the same query that we would have returned in the cursor.<br />
<code>DELIMITER //
CREATE PROCEDURE zip_proximity(zipcode varchar(5), radius double precision, measurement varchar(2))
   BEGIN
   DECLARE base_lat double precision;
   DECLARE base_lon double precision;
   SELECT lat, lon INTO base_lat, base_lon FROM zipcodes WHERE zip = zipcode;
   SELECT zip, lat, lon, city, state, state_abbrev, calculate_distance(measurement, base_lat, base_lon, lat, lon) AS distance FROM zipcodes
      WHERE calculate_distance(measurement, base_lat, base_lon, lat, lon) &lt; radius ORDER BY distance;
   END //
DELIMITER ;</code></p>
<p>Now here is how we would call the procedure, again we are querying for all zip codes within a three mile radius of 94043.<br />
<code>mysql&gt; call zip_proximity('94043', 3, 'mi');
+-------+----------+------------+---------------+------------+--------------+-------------------+
| zip   | lat      | lon        | city          | state      | state_abbrev | distance          |
+-------+----------+------------+---------------+------------+--------------+-------------------+
| 94043 | 37.42337 | -122.07981 | MOUNTAIN VIEW | CALIFORNIA | CA           |                 0 |
| 94039 | 37.41884 | -122.09124 | MOUNTAIN VIEW | CALIFORNIA | CA           | 0.701004115082249 |
| 94035 | 37.41753 | -122.05283 | MOUNTAIN VIEW | CALIFORNIA | CA           |  1.53459076784732 |
| 94042 | 37.39314 | -122.07827 | MOUNTAIN VIEW | CALIFORNIA | CA           |  2.09052870372395 |
| 94041 | 37.38961 | -122.07715 | MOUNTAIN VIEW | CALIFORNIA | CA           |  2.33729808557031 |
| 94306 | 37.41478 | -122.12139 | PALO ALTO     | CALIFORNIA | CA           |  2.35776644108718 |
| 94303 | 37.44424 | -122.11736 | PALO ALTO     | CALIFORNIA | CA           |  2.51480871282271 |
+-------+----------+------------+---------------+------------+--------------+-------------------+
7 rows in set (0.75 sec)

Query OK, 0 rows affected (0.75 sec)</code></p>
<p>Feel free to leave a comment with any questions or suggestions.</p>
]]></content:encoded>
			<wfw:commentRss>http://iamtgc.com/2009/01/14/implementing-zip-code-proximity-functions-in-mysql-and-postgresql/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Exploring Stored Procedures in MySQL and&#160;PostgreSQL</title>
		<link>http://iamtgc.com/2008/12/19/exploring-stored-procedures-in-mysql-and-postgresql/</link>
		<comments>http://iamtgc.com/2008/12/19/exploring-stored-procedures-in-mysql-and-postgresql/#comments</comments>
		<pubDate>Fri, 19 Dec 2008 14:38:57 +0000</pubDate>
		<dc:creator>tgc</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Postgres]]></category>

		<guid isPermaLink="false">http://iamtgc.com/?p=69</guid>
		<description><![CDATA[The PostgreSQL PL/pgSQL procedural language is well documented here and the MySQL Reference Manual is available here. The MySQL documentation is, in my opinion, lacking, however the MySQL Stored Procedure Forum helps a great deal in making up for the lack of documentation. Now, let&#8217;s take for example this simplified user table. In reality it [...]]]></description>
			<content:encoded><![CDATA[<p>The PostgreSQL PL/pgSQL procedural language is well documented <a href="http://www.postgresql.org/docs/8.3/static/plpgsql.html">here</a> and the MySQL Reference Manual is available <a href="http://dev.mysql.com/doc/refman/5.1/en/stored-routines.html">here</a>.  The MySQL documentation is, in my opinion, lacking, however the <a href="http://forums.mysql.com/list.php?98">MySQL Stored Procedure Forum</a> helps a great deal in making up for the lack of  documentation.<br />
<span id="more-69"></span><br />
Now, let&#8217;s take for example this simplified user table.  In reality it would contain additional information, a hashed password, real name, who knows, but for the sake of our example, we&#8217;ll keep it minimal.  Once the table is created, we will want to create a stored procedure that automatically expires accounts that have not logged on in more than a month.<br />
<code>CREATE TABLE users (
   username varchar(12),
   last_login timestamp,
   expired boolean
);</code></p>
<p>Now, we could create a stored procedure that expires all accounts that have not logged on in a month, but to make it more versatile, and to demonstrate more of the capabilities, we will allow the user to define the number of days since last logged on before the account expires.</p>
<p>First, let&#8217;s see how this we could write this for PostgreSQL, will will be using PL/pgSQL procedural language.<br />
<code>CREATE OR REPLACE FUNCTION mark_expired(days INT) RETURNS VOID AS
$$
DECLARE 
   delta BIGINT; 
BEGIN
   delta := $1*86400;
   UPDATE users SET expired=true WHERE EXTRACT(epoch from age(CURRENT_TIMESTAMP, last_login)) &gt; delta;
END;
$$
LANGUAGE plpgsql;</code></p>
<p>Let&#8217;s look at the user table before running the stored procedure<br />
<code>username  |     last_login      | expired
----------+---------------------+---------
 homer    | 2008-12-18 00:00:00 | f
 marge    | 2008-11-18 00:00:00 | f
 bart     | 2008-12-14 00:00:00 | f
 lisa     | 2008-12-17 00:00:00 | f
 maggie   | 2008-12-19 00:00:00 | f</code></p>
<p>Here is how you would execute the stored procedure, with our user defined 30 day argument.<br />
<code>testdb=# select mark_expired(30);
testdb=# select * from users;
 username |     last_login      | expired
----------+---------------------+---------
 homer    | 2008-12-18 00:00:00 | f
 bart     | 2008-12-14 00:00:00 | f
 lisa     | 2008-12-17 00:00:00 | f
 maggie   | 2008-12-19 00:00:00 | f
 marge    | 2008-11-18 00:00:00 | t</code></p>
<p>Success!, Marge&#8217;s account has now been marked expired, since she has not logged on in the last 30 days.</p>
<p>Your first inclination may be to extract the day from age, but this does not work as one may think.  Take for example the following&#8230;<br />
<code>testdb=# select age(now(), CURRENT_DATE-31);
             age
-----------------------------
 1 mon 1 day 13:02:30.065623
(1 row)</code></p>
<p>Now if we extract day, this is what we get&#8230;<br />
<code>testdb=# select extract(day from age(now(), CURRENT_DATE-31));
 date_part
-----------
         1
(1 row)</code></p>
<p>This is not in fact what we were looking for, if we were to use this method to expire accounts, this account appears to have been logged in as recently as one day ago, when in fact 31 days have elapsed.  So this is why we chose to use epoch, which in this case will get total elapsed seconds.</p>
<p>Now, let&#8217;s review how you might write this for MySQL.<br />
<code>DELIMITER //
CREATE PROCEDURE mark_expired (days INT) 
BEGIN 
   UPDATE users SET expired = true 
              WHERE last_login &lt; SUBDATE(CURRENT_TIMESTAMP, INTERVAL days DAY); 
END //
DELIMITER ;</code><br />
In this example we take a slightly different approach, we subtract the days argument from the current timestamp, and see if the last login timestamp is older than this.  This leverages MySQL&#8217;s SUBDATE function and avoids having to convert days into seconds.</p>
<p>Let&#8217;s take a look at our table again, before calling the procedure<br />
<code>mysql&gt; select * from users;
+----------+---------------------+---------+
| username | last_login          | expired |
+----------+---------------------+---------+
| homer    | 2008-12-18 00:00:00 |       0 |
| marge    | 2008-11-18 00:00:00 |       0 |
| bart     | 2008-12-14 00:00:00 |       0 |
| lisa     | 2008-12-17 00:00:00 |       0 |
| maggie   | 2008-12-19 00:00:00 |       0 |
+----------+---------------------+---------+</code></p>
<p>And how you would call the procedure in MySQL.<br />
<code>mysql&gt; CALL mark_expired(30);
mysql&gt; select * from users;
+----------+---------------------+---------+
| username | last_login          | expired |
+----------+---------------------+---------+
| homer    | 2008-12-18 00:00:00 |       0 |
| marge    | 2008-11-18 00:00:00 |       1 |
| bart     | 2008-12-14 00:00:00 |       0 |
| lisa     | 2008-12-17 00:00:00 |       0 |
| maggie   | 2008-12-19 00:00:00 |       0 |
+----------+---------------------+---------+</code></p>
<p>Again, success, Marge&#8217;s account has been expired.</p>
]]></content:encoded>
			<wfw:commentRss>http://iamtgc.com/2008/12/19/exploring-stored-procedures-in-mysql-and-postgresql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to Fix or Remove Uncooperative Widgets in&#160;WordPress</title>
		<link>http://iamtgc.com/2008/12/03/how-to-fix-or-remove-uncooperative-widgets-in-wordpress/</link>
		<comments>http://iamtgc.com/2008/12/03/how-to-fix-or-remove-uncooperative-widgets-in-wordpress/#comments</comments>
		<pubDate>Wed, 03 Dec 2008 05:19:41 +0000</pubDate>
		<dc:creator>tgc</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">http://iamtgc.com/?p=33</guid>
		<description><![CDATA[Recently, I was working with php-code-widget to include some custom PHP code in a WordPress widget. Unfortunately, and much to my surprise, the custom code did not work flawlessly the first time and I was left staring at errors like: &#8220;Fatal error: Call to undefined function test(); in wp-content/plugins/php-code-widget /execphp.php(37): eval()&#8217;d code on line 1&#8243;. [...]]]></description>
			<content:encoded><![CDATA[<p>Recently, I was working with <a href="http://wordpress.org/extend/plugins/php-code-widget/">php-code-widget</a> to include some custom PHP code in a WordPress widget.  </p>
<p>Unfortunately, and much to my surprise, the custom code did not work flawlessly the first time and I was left staring at errors like:</p>
<p><strong>&#8220;Fatal error: Call to undefined function test(); in wp-content/plugins/php-code-widget /execphp.php(37): eval()&#8217;d code on line 1&#8243;.</strong><br />
<span id="more-33"></span><br />
Unfortunately, as this error shows up in the dashboard where I would normally edit the widgets content, this is not as straight forward to fix as one would hope.  So&#8230; after diving into the database and some trial and error I found that the widgets reside in the wp_options table, as this table is quite large, and contains more than just widget information, I had to narrow down what I was looking for.  I ended up with this query:</p>
<p><code>mysql&gt; select option_name from wp_options where option_name like 'widget_%';
+------------------------+
| option_name            |
+------------------------+
| widget_akismet         |
| widget_archives        |
| widget_calendar        |
| widget_categories      |
| widget_execphp         |
| widget_meta            |
| widget_pages           |
| widget_recent_comments |
| widget_recent_entries  |
| widget_rss             |
| widget_tag_cloud       |
| widget_text            |
+------------------------+</code></p>
<p>In this case, it is content in the php-code-widget that is causing our errors, as there is no widget_php-code-widget, it seems that the widget_execphp is the next closest thing.  So let&#8217;s inspect the widget_execphp entry.</p>
<p><code>mysql&gt; select * from wp_options where option_name='widget_execphp';
+-----------+---------+----------------+-----------------------------------------------------------------------------------+----------+
| option_id | blog_id | option_name    | option_value                                                                      | autoload |
+-----------+---------+----------------+-----------------------------------------------------------------------------------+----------+
|       347 |       0 | widget_execphp | a:1:{i:291302011;a:2:{s:5:"title";s:4:"Test";s:4:"text";s:16:"&lt;?php test();?&gt;";}} | yes      |
+-----------+---------+----------------+-----------------------------------------------------------------------------------+----------+</code></p>
<p>Bingo&#8230; here we can see the code that was entered into the widget.  In this case the test() function does not exist.</p>
<p>The first, and arguably the simplest option is to simply delete the entry,  we delete it using the option_id value we obtained above.  The option_id value is also used later in the article, in the event you chose to update the entry as opposed to removing it.<br />
<code>mysql&gt; delete from wp_options where option_id=347;
Query OK, 1 row affected (0.29 sec)</code></p>
<p>At this point you can simply go back into the dashboard and recreate your widget, more careful not to include non existent functions this time.  But that would be too easy, a more interesting  alternative is to modify the existing entry.</p>
<p><code>mysql&gt; update wp_options set option_value = "a:1:{i:291302011;a:2:{s:5:\"title\";s:4:\"Test\";s:4:\"text\";s:20:\"&lt;?php new_test(); ?&gt;\";}}" where option_id=347;                   
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0</code></p>
<p>Let&#8217;s look at this more closely, the above query updates option_value to:<br />
<br />
a:1:{i:291302011;a:2:{s:5:&#8221;title&#8221;;s:4:&#8221;Test&#8221;;s:4:&#8221;text&#8221;;<strong>s:20:&#8221;&lt;?php new_test(); ?&gt;&#8221;</strong>;}}</p>
<p>If you notice, two things have changed from the previous option_value value.  </p>
<p>The first change is to the string length variable, represented by <i>s</i>, which precedes the string.  In our example s:16 becomes s:20.  More simply put <strong>&lt;?php test(); ?&gt;</strong> (16 characters in length) becomes <strong>&lt;?php new_test(); ?&gt;</strong> (20 characters in length).</p>
<p>The necessary changes is to the string itself, and probably the reason you are getting the undesirable behavior in the first place, is left up to the reader.  It is my hope that this sets you in the right direction.</p>
]]></content:encoded>
			<wfw:commentRss>http://iamtgc.com/2008/12/03/how-to-fix-or-remove-uncooperative-widgets-in-wordpress/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Moving Comments in&#160;WordPress</title>
		<link>http://iamtgc.com/2008/07/23/moving-comments-in-wordpress/</link>
		<comments>http://iamtgc.com/2008/07/23/moving-comments-in-wordpress/#comments</comments>
		<pubDate>Thu, 24 Jul 2008 01:10:10 +0000</pubDate>
		<dc:creator>tgc</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">http://iamtgc.com/?p=23</guid>
		<description><![CDATA[I find that often times comments on blogs are left for the wrong post. Whether by mistake, laziness, carelessness or otherwise on the part of the commenter, this can lead to potentially pertinent information being in the wrong place. Whatever the cause, the following article will describe in detail how you can easily manipulate your [...]]]></description>
			<content:encoded><![CDATA[<p>I find that often times comments on blogs are left for the wrong post.  Whether by mistake, laziness, carelessness or otherwise on the part of the commenter, this can lead to potentially pertinent information being in the wrong place. </p>
<p>Whatever the cause, the following article will describe in detail how you can easily manipulate your wordpress database to move the misplaced comments you have to the post they belong under.<br />  <span id="more-23"></span><br />
This article will accomplish this by using the command line <i>mysql</i> utility.  Depending on where you have your wordpress blog hosted, you may not have access to the database at all, or if you do you may have to access it using a tool such as <a href="http://www.phpmyadmin.net/home_page/index.php">phpmyadmin</a>.</p>
<p>But before I get started, here is a list of what you will need to complete this process:<br />
* mysql username<br />
* mysql password<br />
* mysql wordpress database name</p>
<p>First you&#8217;ll need to connect to mysql and select your database<br />
<code>$ mysql -u wordpress -p
Enter Password: ********

mysql&gt; use my-wordpress-database;
Database changed
mysql&gt;</code></p>
<p>Now, there are two tables that are of primary interest, <b>wp_posts</b> and <b>wp_comments</b>.<br />
Let&#8217;s start with <b>wp_posts</b>, the columns of interest are <i>ID</i> and <i>post_title</i>.<br />
<code>mysql&gt;select ID, post_title from wp_posts;
+----+------------------------------------------------+
| ID | post_title                                     |
+----+------------------------------------------------+
|  4 | Bob Dylan                                      |
|  6 | Johnny Cash                                    |
+----+------------------------------------------------+</code></p>
<p>Now let&#8217;s look at the comments that have been left, they reside in the <b>wp-comments</b> table.<br />
Here, the columns of interest are <i>comment_ID</i>, <i>comment_post_ID</i>, and <i>comment_content</i>.<br />
<code>mysql&gt;select comment_ID, comment_post_ID, comment_content from wp_comments;
+------------+-----------------+-------------------------------------------+
| comment_ID | comment_post_ID | comment_content                           |
+------------+-----------------+-------------------------------------------+
|          1 |               4 | I really like Bob Dylan, nice article     |
|          2 |               4 | Johnny Cash is the best                   |
|          3 |               6 | Johnny Cash the man in black              |
+------------+-----------------+-------------------------------------------+</code></p>
<p>Note, in <b>wp_comments</b>, <i>comment_post_ID</i> ties in to <b>wp_posts</b> <i>ID</i>.  So that means there are two comments for the Bob Dylan post, and one comment for the Johnny Cash post.</p>
<p>But wait&#8230;</p>
<p>The second comment, with <i>comment_ID</i> 2 looks like it is supposed to be a comment for the Johnny Cash post.  Let&#8217;s see how we can move it.<br />
<code>mysql&gt; update wp_comments set comment_post_ID=6 where comment_ID=2;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0</code><br />
and verify that it was moved<br />
<code>mysql&gt;select comment_ID, comment_post_ID, comment_content from wp_comments;
+------------+-----------------+-------------------------------------------+
| comment_ID | comment_post_ID | comment_content                           |
+------------+-----------------+-------------------------------------------+
|          1 |               4 | I really like Bob Dylan, nice article     |
|          2 |               6 | Johnny Cash is the best                   |
|          3 |               6 | Johnny Cash the man in black              |
+------------+-----------------+-------------------------------------------+</code></p>
<p>Good, the comment was successfully moved, but there is still a problem.<br />
<code>mysql&gt; select comment_count from wp_posts where ID=6;
+---------------+
| comment_count |
+---------------+
|             1 |
+---------------+</code><br />
There are supposed to be two comments, so let&#8217;s update the comment_count for both the post that the comment was moved from, and where it was moved to.<br />
<code>mysql&gt; update wp_posts set comment_count=2 where ID=6;

mysql&gt; select comment_count from wp_posts where ID=4;
+---------------+
| comment_count |
+---------------+
|             2 |
+---------------+

mysql&gt; update wp_posts set comment_count=1 where ID=4;

mysql&gt; select post_title, comment_count from wp_posts where ID in (4,6);
+------------------------------+---------------+
| post_title                   | comment_count |
+------------------------------+---------------+
| Bob Dylan                    |             1 |
| Johnny Cash                  |             2 |
+------------------------------+---------------+</code><br />
And that&#8217;s it.  Now your comments are moved and all of the counters have been updated to reflect the moved comments.</p>
]]></content:encoded>
			<wfw:commentRss>http://iamtgc.com/2008/07/23/moving-comments-in-wordpress/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Importing XML into a database with Python and&#160;SQLAlchemy</title>
		<link>http://iamtgc.com/2008/01/29/importing-xml-into-a-database-with-python-and-sqlalchemy/</link>
		<comments>http://iamtgc.com/2008/01/29/importing-xml-into-a-database-with-python-and-sqlalchemy/#comments</comments>
		<pubDate>Tue, 29 Jan 2008 21:19:47 +0000</pubDate>
		<dc:creator>tgc</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Postgres]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://iamtgc.com/2008/01/29/importimg-xml-into-a-database-with-python-and-sqlalchemy/</guid>
		<description><![CDATA[Let&#8217;s begin by analyzing the XML we want to import into our database, it consists of a book&#8217;s ISBN, title, and author. &#60;!-- books.xml --&#62; &#60;catalog&#62; &#60;book isbn="1-880985-26-8"&#62; &#60;title&#62;The Consumer&#60;/title&#62; &#60;author&#62;M. Gira&#60;/author&#62; &#60;/book&#62; &#60;book isbn="0-679775-43-9"&#62; &#60;title&#62;The Wind-Up Bird Chronicle&#60;/title&#62; &#60;author&#62;Haruki Murakami&#60;/author&#62; &#60;/book&#62; &#60;!-- imagine more entries here... --&#62; &#60;/catalog&#62; Now we can create the database [...]]]></description>
			<content:encoded><![CDATA[<p>Let&#8217;s begin by analyzing the XML we want to import into our database, it consists of a book&#8217;s ISBN, title, and author.<br />
<code>&lt;!-- books.xml --&gt;
&lt;catalog&gt;
  &lt;book isbn="1-880985-26-8"&gt;
    &lt;title&gt;The Consumer&lt;/title&gt;
    &lt;author&gt;M. Gira&lt;/author&gt;
  &lt;/book&gt;
  &lt;book isbn="0-679775-43-9"&gt;
    &lt;title&gt;The Wind-Up Bird Chronicle&lt;/title&gt;
    &lt;author&gt;Haruki Murakami&lt;/author&gt;
  &lt;/book&gt;
  &lt;!-- imagine more entries here... --&gt;
&lt;/catalog&gt;</code></p>
<p><span id="more-13"></span><br />
Now we can create the database table.<br />
<code>create table books
(
isbn varchar(14) primary key not null,
title varchar(50),
author varchar(50)
);</code></p>
<p>Our example depends on <a href="http://sqlalchemy.org">SQLAlchemy</a>, which is a &#8220;Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL.&#8221;</p>
<p>While our example leverages a Postgres database, given SQLAlchemy&#8217;s database agnostic approach, it can trivially be modified to access a MySQL database.</p>
<p>This code is derived from a SAX parser originally found in <a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&#038;location=http%3A%2F%2Fwww.amazon.com%2FProgramming-Python-Mark-Lutz%2Fdp%2F0596009259%3Fie%3DUTF8%26s%3Dbooks%26qid%3D1189212366%26sr%3D8-1&#038;tag=iamtgc-20&#038;linkCode=ur2&#038;camp=1789&#038;creative=9325">Programming Python 3rd Edition</a><img src="http://www.assoc-amazon.com/e/ir?t=iamtgc-20&amp;l=ur2&amp;o=1" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />.</p>
<p><code># bookhandler.py
from sqlalchemy import *
from sqlalchemy.orm import *

import xml.sax.handler

pg_db = create_engine('postgres:///testdb?user=homer')

metadata = MetaData(pg_db)

books_table = Table('books', metadata, autoload=True)

class Book(object):
    pass

mapper(Book, books_table)

class BookHandler(xml.sax.handler.ContentHandler):
    def __init__(self):
        self.buffer = ""
        self.inField = 0
        self.session = create_session(bind=pg_db)

    def startElement(self, name, attributes):
        if name == "book":
            self.isbn = attributes["isbn"]
        elif name == "title":
            self.inField = 1
        elif name == "author":
            self.inField = 1

    def characters(self, data):
        if self.inField:
            self.buffer += data

    def endElement(self, name):
        if name == "book":
            self.session.begin()
            self.newbook = Book()
            self.newbook.isbn = self.isbn
            self.newbook.title = self.title
            self.newbook.author = self.author
            self.session.save(self.newbook)
            self.session.commit()
        elif name == "title":
            self.inField = 0
            self.title = self.buffer
        elif name == "author":
            self.inField = 0
            self.author = self.buffer
        self.buffer = ""</code><br />
Now that we&#8217;ve set up our sax parser and handler to parse and load the entries from books.xml into the table, lets set up a small script to drive it:<br />
<code># runit.py
import bookhandler
import xml.sax

parser = xml.sax.make_parser()
handler = bookhandler.BookHandler()
parser.setContentHandler(handler)
parser.parse("books.xml")</code></p>
<p>Now let&#8217;s see if it works:<br />
<code>$ ls
bookhandler.py  books.xml       runit.py
$ python ./runit.py
$ psql testdb

testdb=# select * from books;
     isbn      |           title            |     author
---------------+----------------------------+-----------------
 1-880985-26-8 | The Consumer               | M. Gira
 0-679775-43-9 | The Wind-Up Bird Chronicle | Haruki Murakami
(2 rows)</code></p>
]]></content:encoded>
			<wfw:commentRss>http://iamtgc.com/2008/01/29/importing-xml-into-a-database-with-python-and-sqlalchemy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
