<?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>techfounder &#187; Open Source</title>
	<atom:link href="http://www.techfounder.net/category/os/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.techfounder.net</link>
	<description>Blog about web development and Internet entrepreneurship</description>
	<lastBuildDate>Mon, 21 Jun 2010 19:41:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Optimizing OR (union) operations in MySQL</title>
		<link>http://www.techfounder.net/2008/10/15/optimizing-or-union-operations-in-mysql/</link>
		<comments>http://www.techfounder.net/2008/10/15/optimizing-or-union-operations-in-mysql/#comments</comments>
		<pubDate>Wed, 15 Oct 2008 06:12:32 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=126</guid>
		<description><![CDATA[In my last post on database optimization, I focused on improving query performance by optimizing schema - exploring indexing strategies by reading the execution plan. In this post I'll show how different query structures can also have a major impact on performance. Dealing with OR operators in a WHERE clause of an SQL statement in [...]]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://www.techfounder.net/2008/10/12/profiling-queries-with-zend_db-and-optimizing-them-by-hand/" title="Profiling MySQL queries with Zend_Db, optimizing by hand">last post</a> on database optimization, I focused on improving query performance by optimizing schema - exploring indexing strategies by reading the execution plan. In this post I'll show how different query structures can also have a major impact on performance.<br />
<span id="more-126"></span><br />
Dealing with OR operators in a WHERE clause of an SQL statement in MySQL can be tricky. Up until recently, MySQL could only use one index per table referenced in a query. A multi-column index can be used for  filtering conditions with an AND operator (which is more restrictive by nature), but a condition added by OR must use a separate index because of the logical nature of the opertaor (a <a href="http://en.wikipedia.org/wiki/Union_(set_theory)" target="_blank">union</a>, as opposed to an <a href="http://en.wikipedia.org/wiki/Intersection_(set_theory)">intersection</a> that the AND represents).</p>
<p>MySQL 5.0 added the <a href="http://dev.mysql.com/doc/refman/5.0/en/index-merge-optimization.html" target="_blank">index_merge</a> select type, which allows the query optimizer to possibly select several indexes from a single table and merge them to improve query performance. I say possibly, since leaving such decisions to the optimizer is risky at best. In fact, as I will show next, you are sometimes left with no indexes selected out of several possible options, resulting in a full table scan.</p>
<p>Continuing from my last post, I'll use a real-world example to show the different paths the queries optimizer can take when preparing an execution plan for our queries.</p>
<p>I'll actually be working with the same query I profiled last time, with a minor change. The relevant table structure is as follows:</p>
<pre class="sql"><span style="color: #808080; font-style: italic;">--</span>
<span style="color: #808080; font-style: italic;">-- Table structure for table `tasks`</span>
<span style="color: #808080; font-style: italic;">--</span>
&nbsp;
<span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> <span style="color: #993333; font-weight: bold;">IF</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">EXISTS</span> <span style="color: #ff0000;">`tasks`</span> <span style="color: #66cc66;">&#40;</span>
  <span style="color: #ff0000;">`id`</span> int<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">13</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">AUTO_INCREMENT</span>,
  <span style="color: #ff0000;">`list_id`</span> int<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">13</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span>,
  <span style="color: #ff0000;">`user_id`</span> int<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">13</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span>,
  <span style="color: #ff0000;">`task`</span> varchar<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">255</span><span style="color: #66cc66;">&#41;</span> collate utf8_unicode_ci <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span>,
  <span style="color: #ff0000;">`due`</span> timestamp <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">DEFAULT</span> <span style="color: #993333; font-weight: bold;">NULL</span>,
  <span style="color: #ff0000;">`created`</span> timestamp <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">DEFAULT</span> CURRENT_TIMESTAMP,
  <span style="color: #ff0000;">`checked`</span> timestamp <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">DEFAULT</span> <span style="color: #993333; font-weight: bold;">NULL</span>,
  <span style="color: #ff0000;">`assigned`</span> int<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">13</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">DEFAULT</span> <span style="color: #993333; font-weight: bold;">NULL</span>,
  <span style="color: #ff0000;">`done`</span> enum<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'1'</span><span style="color: #66cc66;">&#41;</span> collate utf8_unicode_ci <span style="color: #993333; font-weight: bold;">DEFAULT</span> <span style="color: #993333; font-weight: bold;">NULL</span>,
  <span style="color: #993333; font-weight: bold;">PRIMARY</span> <span style="color: #993333; font-weight: bold;">KEY</span>  <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">`id`</span><span style="color: #66cc66;">&#41;</span>,
  <span style="color: #993333; font-weight: bold;">KEY</span> <span style="color: #ff0000;">`user_id`</span> <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">`user_id`</span>,<span style="color: #ff0000;">`due`</span><span style="color: #66cc66;">&#41;</span>,
  <span style="color: #993333; font-weight: bold;">KEY</span> <span style="color: #ff0000;">`assigned`</span> <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">`assigned`</span>,<span style="color: #ff0000;">`due`</span><span style="color: #66cc66;">&#41;</span>,
  <span style="color: #993333; font-weight: bold;">KEY</span> <span style="color: #ff0000;">`due`</span> <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">`due`</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#41;</span> ENGINE=InnoDB</pre>
<p>And the query itself:</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`id`</span>,
           <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`task`</span>,
           UNIX_TIMESTAMP<span style="color: #66cc66;">&#40;</span>due<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`time`</span>,
           <span style="color: #ff0000;">`lists`</span>.<span style="color: #ff0000;">`name`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`list_name`</span>,
           <span style="color: #ff0000;">`lists`</span>.<span style="color: #ff0000;">`id`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`list_id`</span>
<span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #ff0000;">`tasks`</span>
<span style="color: #993333; font-weight: bold;">INNER</span> <span style="color: #993333; font-weight: bold;">JOIN</span> <span style="color: #ff0000;">`lists`</span> <span style="color: #993333; font-weight: bold;">ON</span> lists.id=tasks.list_id
<span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #66cc66;">&#40;</span>tasks.user_id=<span style="color: #ff0000;">'1'</span> <span style="color: #993333; font-weight: bold;">OR</span> assigned=<span style="color: #ff0000;">'1'</span><span style="color: #66cc66;">&#41;</span>
     <span style="color: #993333; font-weight: bold;">AND</span> <span style="color: #66cc66;">&#40;</span>tasks.done <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">&#41;</span>
     <span style="color: #993333; font-weight: bold;">AND</span> <span style="color: #66cc66;">&#40;</span>tasks.due <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">ORDER</span> <span style="color: #993333; font-weight: bold;">BY</span> <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`due`</span> <span style="color: #993333; font-weight: bold;">ASC</span></pre>
<p>This query is almost the same as the one I worked with last time. The only difference being the first statement in the WHERE clause, involving an OR operator:</p>
<blockquote><p>(tasks.user_id='1' OR assigned='1')</p></blockquote>
<p>Just to understand what I'm doing here - The query selects from the tasks table with several filtering criteria. The last OR statement conveys the condition that the tasks selected either belong to specific user (i.e created by him) or assigned to him (those two possibly coincide).</p>
<p>For testing purposes I will be running the queries against a testing database I set up with plenty of mock data. The database is around 1Gb in total size, with the tasks table at about 1.8 million rows. It's not very large, but enough for significant data to be obtained while allowing relatively online tampering with schema (modifying keys takes <em>only</em> around 4 minutes to complete).</p>
<p>Running in original form computes as (average of 10 queries):</p>
<blockquote><p>(304 total, Query took 6.84 sec)</p></blockquote>
<p>6.84 sec is way too long for running a query in a typical web application. If you'd recall, I last got this query running at <strong>0.0008 sec</strong> without the additional OR condition, meaning it is running at around 7,000 times slower (yikes). In such a case you would suspect the indexes are not selective enough, and sure enough an EXPLAIN reveals:</p>
<table class="data" border="0">
<thead>
<tr>
<th>id</th>
<th>select_type</th>
<th>table</th>
<th>type</th>
<th>possible_keys</th>
<th>key</th>
<th>key_len</th>
<th>ref</th>
<th>rows</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>tasks</td>
<td>index</td>
<td>user_id,assigned,due</td>
<td>due</td>
<td>5</td>
<td><em>NULL</em></td>
<td class="nowrap" align="right">1875432</td>
<td>Using where</td>
</tr>
<tr class="even">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>lists</td>
<td>eq_ref</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>4</td>
<td>tasks.list_id</td>
<td class="nowrap" align="right">1</td>
<td></td>
</tr>
</tbody>
</table>
<p>MySQL is performing a regular index select and not an index_merge as we'd like, and doing that it selects the worst possible index - the only one that doesn't filter the result set. Sure enough, all 1.8M rows are scanned and the query is underperforming badly. Trying to force the issue, I first add an IGNORE INDEX(due) to remove it from the equation:</p>
<blockquote><p>(304 total, Query took 1.41 sec)</p></blockquote>
<table class="data" border="0">
<thead>
<tr>
<th>id</th>
<th>select_type</th>
<th>table</th>
<th>type</th>
<th>possible_keys</th>
<th>key</th>
<th>key_len</th>
<th>ref</th>
<th>rows</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>tasks</td>
<td>ALL</td>
<td>user_id,assigned</td>
<td><em>NULL</em></td>
<td><em>NULL</em></td>
<td><em>NULL</em></td>
<td class="nowrap" align="right">1870838</td>
<td>Using where; Using filesort</td>
</tr>
<tr class="even">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>lists</td>
<td>eq_ref</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>4</td>
<td>tasks.list_id</td>
<td class="nowrap" align="right">1</td>
<td></td>
</tr>
</tbody>
</table>
<p>The query optimizer has decided not to use an index at all, despite several good candidates. However, query performance is improved since a full table scan with no index is performed straight up. It's still way too slow, and we'd like it to use a filtering index so it can avoid a full table scan. Trying to force the other indexes doesn't work so we're left with no choice but to try alternative query structures.</p>
<p>Our first candidate is replacing our OR condition with two UNION'ed select statements (the inspiration is from a <a href="http://www.mysqlperformanceblog.com/2007/09/18/possible-optimization-for-sort_merge-and-union-order-by-limit/" target="_blank">couple</a> of <a href="http://www.mysqlperformanceblog.com/2007/10/05/union-vs-union-all-performance/" target="_blank">posts</a> over at the MySQL performance blog). Breaking the original query into a UNION form results in:</p>
<pre class="sql"><span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`id`</span>,
            <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`task`</span>,
            UNIX_TIMESTAMP<span style="color: #66cc66;">&#40;</span>due<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`time`</span>,
            <span style="color: #ff0000;">`lists`</span>.<span style="color: #ff0000;">`name`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`list_name`</span>,
            <span style="color: #ff0000;">`lists`</span>.<span style="color: #ff0000;">`id`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`list_id`</span>
<span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #ff0000;">`tasks`</span>
<span style="color: #993333; font-weight: bold;">INNER</span> <span style="color: #993333; font-weight: bold;">JOIN</span> <span style="color: #ff0000;">`lists`</span> <span style="color: #993333; font-weight: bold;">ON</span> lists.id=tasks.list_id
<span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #66cc66;">&#40;</span>tasks.done <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">&#41;</span>
    <span style="color: #993333; font-weight: bold;">AND</span> <span style="color: #66cc66;">&#40;</span>tasks.due <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">&#41;</span>
    <span style="color: #993333; font-weight: bold;">AND</span> <span style="color: #66cc66;">&#40;</span>tasks.user_id=<span style="color: #ff0000;">'1'</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#41;</span> UNION <span style="color: #993333; font-weight: bold;">ALL</span> <span style="color: #66cc66;">&#40;</span>
 <span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`id`</span>,
            <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`task`</span>,
            UNIX_TIMESTAMP<span style="color: #66cc66;">&#40;</span>due<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`time`</span>,
            <span style="color: #ff0000;">`lists`</span>.<span style="color: #ff0000;">`name`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`list_name`</span>,
            <span style="color: #ff0000;">`lists`</span>.<span style="color: #ff0000;">`id`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`list_id`</span>
<span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #ff0000;">`tasks`</span>
<span style="color: #993333; font-weight: bold;">INNER</span> <span style="color: #993333; font-weight: bold;">JOIN</span> <span style="color: #ff0000;">`lists`</span> <span style="color: #993333; font-weight: bold;">ON</span> lists.id=tasks.list_id
<span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #66cc66;">&#40;</span>tasks.done <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">&#41;</span>
    <span style="color: #993333; font-weight: bold;">AND</span> <span style="color: #66cc66;">&#40;</span>tasks.due <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">&#41;</span>
    <span style="color: #993333; font-weight: bold;">AND</span> <span style="color: #66cc66;">&#40;</span>assigned=<span style="color: #ff0000;">'1'</span> <span style="color: #993333; font-weight: bold;">AND</span> tasks.user_id!=<span style="color: #ff0000;">'1'</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">ORDER</span> <span style="color: #993333; font-weight: bold;">BY</span> <span style="color: #ff0000;">`time`</span> <span style="color: #993333; font-weight: bold;">ASC</span>
&nbsp;</pre>
<p>This unsightly looking query looks much more complex, but actually gets the job done:</p>
<blockquote><p>(304 total, Query took 0.0021 sec)</p></blockquote>
<p>A big improvement to say the least (~3500 faster than the original query).<br />
The explain shows why:</p>
<table class="data" border="0">
<tbody>
<tr class="odd">
<td class="nowrap" align="right">1</td>
<td>PRIMARY</td>
<td>tasks</td>
<td>range</td>
<td>user_id,due</td>
<td>user_id</td>
<td>9</td>
<td><em>NULL</em></td>
<td class="nowrap" align="right">304</td>
<td>Using where</td>
</tr>
<tr class="even">
<td class="nowrap" align="right">1</td>
<td>PRIMARY</td>
<td>lists</td>
<td>eq_ref</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>4</td>
<td>tasks.list_id</td>
<td class="nowrap" align="right">1</td>
<td></td>
</tr>
<tr class="odd">
<td class="nowrap" align="right">2</td>
<td>UNION</td>
<td>tasks</td>
<td>range</td>
<td>user_id,assigned,due</td>
<td>assigned</td>
<td>10</td>
<td><em>NULL</em></td>
<td class="nowrap" align="right">1</td>
<td>Using where</td>
</tr>
<tr class="even">
<td class="nowrap" align="right">2</td>
<td>UNION</td>
<td>lists</td>
<td>eq_ref</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>4</td>
<td>tasks.list_id</td>
<td class="nowrap" align="right">1</td>
<td></td>
</tr>
<tr class="odd">
<td align="right"><em>NULL</em></td>
<td>UNION RESULT</td>
<td>&lt;union1,2&gt;</td>
<td>ALL</td>
<td><em>NULL</em></td>
<td><em>NULL</em></td>
<td><em>NULL</em></td>
<td><em>NULL</em></td>
<td align="right"><em>NULL</em></td>
<td>Using filesort</td>
</tr>
</tbody>
</table>
<p>Only 304 rows are scanned (as opposed to the entire 1.8M table before). Despite the filesort, we are in the area of usability for this query.</p>
<p>Another approach would be to determine why the index_merging isn't take place and how can we enforce it. By reducing the WHERE clause to include only the columns covered by an index, index_merging kicks in resulting in a similar performance as the union, albeit with less selective filtering:</p>
<blockquote><p>(368 total, Query took 0.0018 sec)</p></blockquote>
<p>We could filter our result set in the application, but I'd like to keep it in the SQL where it belongs. In order to integrate the rest of the filtering statements, I use an IN condition on another index I have available - the primary key. This results in:</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`id`</span>,
           <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`task`</span>,
           UNIX_TIMESTAMP<span style="color: #66cc66;">&#40;</span>due<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`time`</span>,
           <span style="color: #ff0000;">`lists`</span>.<span style="color: #ff0000;">`name`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`list_name`</span>,
           <span style="color: #ff0000;">`lists`</span>.<span style="color: #ff0000;">`id`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`list_id`</span>
<span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #ff0000;">`tasks`</span>
<span style="color: #993333; font-weight: bold;">INNER</span> <span style="color: #993333; font-weight: bold;">JOIN</span> <span style="color: #ff0000;">`lists`</span> <span style="color: #993333; font-weight: bold;">ON</span> lists.id=tasks.list_id
<span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #66cc66;">&#40;</span>tasks.user_id=<span style="color: #ff0000;">'1'</span> <span style="color: #993333; font-weight: bold;">OR</span> assigned=<span style="color: #ff0000;">'1'</span><span style="color: #66cc66;">&#41;</span>
   <span style="color: #993333; font-weight: bold;">AND</span> tasks.id <span style="color: #993333; font-weight: bold;">IN</span>
     <span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> id
      <span style="color: #993333; font-weight: bold;">FROM</span> tasks
      <span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #66cc66;">&#40;</span>tasks.done <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">&#41;</span>
        <span style="color: #993333; font-weight: bold;">AND</span> <span style="color: #66cc66;">&#40;</span>tasks.due <span style="color: #993333; font-weight: bold;">IS</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span><span style="color: #66cc66;">&#41;</span>
     <span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">ORDER</span> <span style="color: #993333; font-weight: bold;">BY</span> <span style="color: #ff0000;">`tasks`</span>.<span style="color: #ff0000;">`due`</span> <span style="color: #993333; font-weight: bold;">ASC</span>
&nbsp;</pre>
<p>Which brings us back to the performance levels of the UNION form:</p>
<blockquote><p>(304 total, Query took 0.0021 sec)</p></blockquote>
<p>And we can see in the EXPLAIN that it is indeed an index_merge operation:</p>
<table class="data" border="0">
<thead>
<tr>
<th>id</th>
<th>select_type</th>
<th>table</th>
<th>type</th>
<th>possible_keys</th>
<th>key</th>
<th>key_len</th>
<th>ref</th>
<th>rows</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="nowrap" align="right">1</td>
<td>PRIMARY</td>
<td>tasks</td>
<td>index_merge</td>
<td>user_id,assigned</td>
<td>user_id,assigned</td>
<td>4,5</td>
<td><em>NULL</em></td>
<td class="nowrap" align="right">369</td>
<td>Using sort_union(user_id,assigned); Using where; Using filesort</td>
</tr>
<tr class="even">
<td class="nowrap" align="right">1</td>
<td>PRIMARY</td>
<td>lists</td>
<td>eq_ref</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>4</td>
<td>tasks.list_id</td>
<td class="nowrap" align="right">1</td>
<td></td>
</tr>
<tr class="odd">
<td class="nowrap" align="right">2</td>
<td>DEPENDENT SUBQUERY</td>
<td>tasks</td>
<td>unique_subquery</td>
<td>PRIMARY,due</td>
<td>PRIMARY</td>
<td>4</td>
<td>func</td>
<td class="nowrap" align="right">1</td>
<td>Using where</td>
</tr>
</tbody>
</table>
<p>So there you have it. From 6.83 seconds to 0.0021 seconds with some tweaking to the query structure. I am still not completely satisfied with the fact that it's using filesort, but I couldn't get it to use another index for the operation. Without the sorting the query is twice as fast:</p>
<blockquote><p>(304 total, Query took 0.0010 sec)</p></blockquote>
<p>So if it ever becomes an issue I could move sorting to the application code. Hopefully by then merge_index is better implemented in MySQL.</p>
<p>Regarding the query strucutre - personally I use the subquery format since it is more compact and maintainable. It is always good however to be familiar with all the alternatives.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=126" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F10%2F15%2Foptimizing-or-union-operations-in-mysql%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F10%2F15%2Foptimizing-or-union-operations-in-mysql%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/10/15/optimizing-or-union-operations-in-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Profiling queries with Zend_Db and optimizing them by hand</title>
		<link>http://www.techfounder.net/2008/10/12/profiling-queries-with-zend_db-and-optimizing-them-by-hand/</link>
		<comments>http://www.techfounder.net/2008/10/12/profiling-queries-with-zend_db-and-optimizing-them-by-hand/#comments</comments>
		<pubDate>Sun, 12 Oct 2008 05:42:55 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=125</guid>
		<description><![CDATA[Database performance is one of the major bottlenecks for most web applications. Most web developers are not database experts (and I'm no exception), there are however several basic methods to analyze and optimize database performance without resorting to expert consultants (such as those, whose founders blogs are an invaluable source of MySQL knowledge). The Performance [...]]]></description>
			<content:encoded><![CDATA[<p>Database performance is one of the major bottlenecks for most web applications. Most web developers are not database experts (and I'm no exception), there are however several basic methods to analyze and optimize database performance without resorting to expert consultants (such as <a href="http://www.percona.com/">those</a>, whose founders blogs are an <a href="http://www.mysqlperformanceblog.com/">invaluable</a> <a href="http://www.xaprb.com/blog/">source</a> of MySQL knowledge).<br />
<span id="more-125"></span></p>
<h3>The Performance Equation</h3>
<p>Database performance is affected by many different variables - the running machine specs, OS, database engine and configuration, table schema and the queries running against it. Since I'm dealing mostly (only?) with MySQL, this article covers it mainly (though it is probably relevant to a large degree for other engines). As for OS and machine specs, I'll take them out of the equation as I'm interested in optimizing <strong>relative</strong> performance on the same machine.</p>
<p>Basically I'm interested in optimizing:</p>
<ul>
<li>The structure of my database tables (schema)</li>
<li>The structure of my application-level queries (SELECT, UPDATE, INSERT, DELETE)</li>
</ul>
<h3>Profiling Database Performance</h3>
<p>Blindly optimizing queries and database schema is counter-productive.  First we should <em>know</em> what we should be optimizing, and for that we need data. The act of gathering data for optimization is called profiling or <a href="http://en.wikipedia.org/wiki/Performance_analysis">performance analysis</a>.</p>
<p>The first step I take when profiling database performance for a web app is to measure the running time of all the queries running in it. Absolute run time of a query is not necessarily a good measure of how optimized / performant it is, since some queries are naturally more complex or pull more data - It will give me a good idea however of where to start improving the response time of the application I'm optimizing. My main goal is not specific query performance, but overall system performance.</p>
<p>Measuring the run time of a query can be done with simple timers using <a title="PHP: microtime()" href="http://www.php.net/microtime">microtime()</a> calls (Check out <a href="http://www.coderholic.com/php-profile-class/">this post</a> for an abstraction), running it in a tool that automatically provides such statistics (phpMyAdmin for example) or using an integrated profiler.</p>
<p>I am using the <a title="Zend_Db_Profiler" href="http://framework.zend.com/manual/en/zend.db.profiler.html">Zend_Db_Profiler,</a> which is convenient for me since I'm using the Zend Framework and all database access converges to a Zend_Db_Adapter connection. The profiler basically uses the microtime() approach but integrates it transparently into all of the queries without me having to wrap them one by one.</p>
<p>Usage is pretty simple. First you need to pass an extra parameter to your Zend_Db_Adapter instance to activate profiling:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$params</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #ff0000;">'host'</span>     =&gt; <span style="color: #ff0000;">'localhost'</span>,</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #ff0000;">'username'</span> =&gt; <span style="color: #ff0000;">'dbusername'</span>,</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #ff0000;">'password'</span> =&gt; <span style="color: #ff0000;">'dbpassword'</span>,</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #ff0000;">'dbname'</span>   =&gt; <span style="color: #ff0000;">'dbname'</span>,</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #ff0000;">'profiler'</span> =&gt; <span style="color: #000000; font-weight: bold;">true</span>  <span style="color: #808080; font-style: italic;">// turn on profiler, disabled by default</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$db</span> = Zend_Db::<span style="color: #006600;">factory</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'PDO_MYSQL'</span>, <span style="color: #0000ff;">$params</span><span style="color: #66cc66;">&#41;</span>;</div></li></ol></pre>
<p>The Zend Framework manual shows some advanced usage (such as profiling directly into firebug, which is very convenient), however for our purposes we simply want to dump the queries and their run time.</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$profiler</span> = <span style="color: #0000ff;">$db</span> -&gt; <span style="color: #006600;">getProfiler</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$profile</span> = <span style="color: #ff0000;">''</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #b1b100;">foreach</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$profiler</span> -&gt; <span style="color: #006600;">getQueryProfiles</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #b1b100;">as</span> <span style="color: #0000ff;">$query</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	<span style="color: #0000ff;">$profile</span> .= <span style="color: #0000ff;">$query</span> -&gt; <span style="color: #006600;">getQuery</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> . <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	     . <span style="color: #ff0000;">'Time: '</span> . <span style="color: #0000ff;">$query</span> -&gt; <span style="color: #006600;">getElapsedSecs</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #66cc66;">&#125;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><a href="http://www.php.net/echo"><span style="color: #000066;">echo</span></a> <span style="color: #0000ff;">$profile</span>;</div></li></ol></pre>
<p>This snippet goes of course after the view has been rendered and all queries executed.</p>
<h3>Using a relevant dataset</h3>
<p>It's important to know whether the data you are profiling against is relevant for when you believe you will start hitting performance issues. Granted, you can never really know when will that be, however it is always preferable to work with a dataset that resembles your (projected) production environment.</p>
<p>I will walk through a specific use-case I recently went through while preparing for the beta release of my startup, <a title="Octabox web platform" href="http://www.octabox.com" target="_blank" title="Business Platform Octabox">business platform Octabox</a>.</p>
<p>Running the profiler on one of the views in the application produced the following output on my development machine:</p>
<blockquote><p>SELECT `history`.`id`, `history`.`type`, `history`.`action`, `history`.`value`, UNIX_TIMESTAMP(history.logged_on) AS `time`, `history`.`user_id`, `history`.`user_name`, `history`.`parent_id`, `history`.`parent_type`, `history`.`parent_name`, `history`.`list_id`, `history`.`list_type`, `history`.`list_name`, `history`.`logging_user_id`, `tags`.`color`, `tags`.`id` AS `tag_id` FROM `history` LEFT JOIN `tags` ON tags.id=history.tag_id AND tags.user_id='1' WHERE (history.user_id=1) AND (history.logged_on &gt;= '2008-09-27 00:00:00') ORDER BY `history`.`logged_on` DESC<br />
<strong>Time: 0.0026991367340088</strong></p>
<p>SELECT `tasks`.`id`, `tasks`.`task`, UNIX_TIMESTAMP(due) AS `time`, `lists`.`name` AS `list_name`, `lists`.`id` AS `list_id` FROM `tasks` INNER JOIN `lists` ON lists.id=tasks.list_id WHERE (tasks.done IS NULL) AND (tasks.due IS NOT NULL) AND (tasks.user_id='1') ORDER BY `tasks`.`due` ASC<br />
<strong>Time: 0.00059294700622559</strong></p>
<p>SELECT COUNT(*) AS `count` FROM `dispatch` WHERE (dispatch.user_id='1') AND (dispatch.status='0')<br />
<strong>Time: 0.00044488906860352</strong></p>
<p>SELECT COUNT(*) AS `count` FROM `conversations_to_users` WHERE (user_id='1' AND status='0')<br />
<strong>Time: 0.00038599967956543</strong></p></blockquote>
<p>At first sight it would appear that the first query would be our immediate suspect for optimization, though the gains would not be great (completes in just over 0.002 seconds). Switching to a database I've prepared before hand with plenty of dummy data (~1.1Gb, some tables over 5M rows) the output looks very different:</p>
<blockquote><p>&nbsp;</p></blockquote>
<p>That's right, there was no output. My development machine was hanging on this particular view when switched to the dummy database (I confirmed this was database related by running the queries independently in the MySQL command line). I switched over to our staging server to see if it can handle rendering this view against the dummy database. Luckily, it could and the suspect query immediately showed up in the output:</p>
<blockquote><p>SELECT `history`.`id`, `history`.`type`, `history`.`action`, `history`.`value`, UNIX_TIMESTAMP(history.logged_on) AS `time`, `history`.`user_id`, `history`.`user_name`, `history`.`parent_id`, `history`.`parent_type`, `history`.`parent_name`, `history`.`list_id`, `history`.`list_type`, `history`.`list_name`, `history`.`logging_user_id`, `tags`.`color`, `tags`.`id` AS `tag_id` FROM `history`<br />
LEFT JOIN `tags` ON tags.id=history.tag_id AND tags.user_id='1' WHERE (history.user_id=1) AND (history.logged_on &gt;= '2008-09-27 00:00:00') ORDER BY `history`.`logged_on` DESC<br />
<strong>Time: 0.00042605400085449</strong></p>
<p>SELECT `tasks`.`id`, `tasks`.`task`, UNIX_TIMESTAMP(due) AS `time`, `lists`.`name` AS `list_name`, `lists`.`id` AS `list_id` FROM `tasks`<br />
INNER JOIN `lists` ON lists.id=tasks.list_id WHERE (tasks.done IS NULL) AND (tasks.due IS NOT NULL) AND (tasks.user_id='1') ORDER BY `tasks`.`due` ASC<br />
<strong>Time: 58.160161972046</strong> <em>//OMFG! what gives...</em></p>
<p>SELECT COUNT(*) AS `count` FROM `dispatch` WHERE (dispatch.user_id='1') AND (dispatch.status='0')<br />
<strong>Time: 0.00032281875610352</strong></p>
<p>SELECT COUNT(*) AS `count` FROM `conversations_to_users` WHERE (user_id='1' AND status='0')<br />
<strong>Time: 0.00026297569274902</strong></p></blockquote>
<p>A query that took just over 0.0005 seconds on a small database, took almost a minute to complete on a bigger one. Surprisingly enough the other queries remained blazingly fast (even faster than on my development machine with a small database, which is a credit to our server's power).</p>
<h3>Optimizing The Errant Query</h3>
<p>So I've found a very problematic query to say the least. Taking 58.1 seconds to complete is obviously not acceptable for any real time application. Running EXPLAIN against the query revealed the following execution plan:</p>
<table class="data" border="0">
<thead>
<tr>
<th>id</th>
<th>select_type</th>
<th>table</th>
<th>type</th>
<th>possible_keys</th>
<th>key</th>
<th>key_len</th>
<th>ref</th>
<th>rows</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>tasks</td>
<td>index</td>
<td>user_id</td>
<td>user_id</td>
<td>9</td>
<td><em>NULL</em></td>
<td class="nowrap" align="right">1868081</td>
<td>Using where</td>
</tr>
<tr class="even">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>lists</td>
<td>eq_ref</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>4</td>
<td>tasks.list_id</td>
<td class="nowrap" align="right">1</td>
<td></td>
</tr>
</tbody>
</table>
<p>The EXPLAIN results show the most immediate problem with the query - it was performing a join against every row in a 1.8M row table. </p>
<p>Examining the details of the query plan, it appears both selects are using an index, the first one limited by a WHERE (on the surface at least - since all rows were scanned). Since despite using an index the entire table was scanned very slowly, I tried to see the query will respond if I removed the index. Using IGNORE INDEX(user_id), I re-ran the query (this time in the Mysql command line):</p>
<blockquote><p>304 rows in set (<strong>1.53 sec</strong>)</p></blockquote>
<p>Wow, that's a big difference <strong>for not using</strong> an index. Running EXPLAIN on this reveals:</p>
<table class="data" border="0">
<thead>
<tr>
<th>id</th>
<th>select_type</th>
<th>table</th>
<th>type</th>
<th>possible_keys</th>
<th>key</th>
<th>key_len</th>
<th>ref</th>
<th>rows</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>tasks</td>
<td>ALL</td>
<td><em>NULL</em></td>
<td><em>NULL</em></td>
<td><em>NULL</em></td>
<td><em>NULL</em></td>
<td class="nowrap" align="right">1866244</td>
<td>Using where; Using filesort</td>
</tr>
<tr class="even">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>lists</td>
<td>eq_ref</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>4</td>
<td>tasks.list_id</td>
<td class="nowrap" align="right">1</td>
<td></td>
</tr>
</tbody>
</table>
<p>Despite using filesort to sort the query, it was running almost <strong>30 times</strong> faster than before. By forcing it not to use an index which was not selective enough (i.e. not at all), the query ran a full table scan instead and completed much faster due to a better execution plan.</p>
<p>So we've improved greatly, but still not enough for my needs. Having one query taking over a second to complete when all the others are completing in sub 0.01 second times means it will be a bottleneck as the database grows (remember, the same query completed in just over 0.0005 seconds on a much smaller database).</p>
<p>Examining the structure of the table in question (tasks) revealed another interesting revealation - there is <strong>no index</strong> on the user_id column. So what happened? it appears there used to be an index on the user_id column, however it was removed just before all the dummy data was inserted into the database. Running ANALYZE TABLE tasks repaired the key information to the current state (no index on user_id).</p>
<p>I wanted to try the original query, this time with an actual index on the filtering column (user_id). After adding the index, I re-ran the query:</p>
<blockquote><p>(304 total, Query took <strong>0.0021 sec</strong>)</p></blockquote>
<p>Much better! but I was not done. Running EXPLAIN yet again reveals the following execution plan:</p>
<table class="data" border="0">
<thead>
<tr>
<th>id</th>
<th>select_type</th>
<th>table</th>
<th>type</th>
<th>possible_keys</th>
<th>key</th>
<th>key_len</th>
<th>ref</th>
<th>rows</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>tasks</td>
<td>ref</td>
<td>user_id</td>
<td>user_id</td>
<td>4</td>
<td>const</td>
<td class="nowrap" align="right">368</td>
<td>Using where; Using filesort</td>
</tr>
<tr class="even">
<td class="nowrap" align="right">1</td>
<td>SIMPLE</td>
<td>lists</td>
<td>eq_ref</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>4</td>
<td>tasks.list_id</td>
<td class="nowrap" align="right">1</td>
<td></td>
</tr>
</tbody>
</table>
<p>So indeed, using <strong>an actual index</strong> was a big help, filtering the set to only 368 rows before processing the rest of the query. However, you'd notice it is running a filesort which we'd like to avoid. Preferably, I could set up an index combination that both filters and sorts using an index.</p>
<p>Creating an index composed of both the 'user_id' and 'due' columns got it right on the head, leading to:</p>
<blockquote><p>(304 total, Query took <strong>0.0008 sec</strong>)</p></blockquote>
<p>Not as dramatic as previous improvements, but still three times better than the last iteration.</p>
<h3>Final Words</h3>
<p>The entire process took several hours of head scratching to arrive at all the results I've shown here. I managed to take a query that would cripple our server (58 seconds for one completion!) to an extremely fast one (0.0008 seconds against a 1.8M row table), through the use of some basic profiling and examining the execution plan.</p>
<p>Being an edge case (a non existent index used to filter against a 1.8M row table...), I gained valuable experience on potential pitfalls and on reading between the lines in the execution plan.</p>
<p>Some technical gibberish:</p>
<p>All queries were run with SQL_NO_CACHE to prevent caching from tampering with the results. I ran each query at least 5 times to make sure I was getting the right readings.</p>
<p>The specs of the server machine that ran the queries are as follows:</p>
<ul>
<li>Intel \ 2.4 GHz 1066FSB - Conroe \ Xeon 3060 (Dual Core)</li>
<li>2 x Generic \ 1024 MB \ DDR2 667 ECC</li>
<li>2 x Maxtor \ 146GB:SAS:10K RPM \ Atlas 10K - SAS</li>
<li>CentOS Enterprise Linux - x86_64 - OS ES 5.0</li>
<li>Running PHP 5.2.6 with MySQL 5.0.45 on Apache 2.2.4</li>
</ul>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=125" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F10%2F12%2Fprofiling-queries-with-zend_db-and-optimizing-them-by-hand%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F10%2F12%2Fprofiling-queries-with-zend_db-and-optimizing-them-by-hand%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/10/12/profiling-queries-with-zend_db-and-optimizing-them-by-hand/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Chrome is out. Google has my vote</title>
		<link>http://www.techfounder.net/2008/09/04/chrome-is-out-google-has-my-vote/</link>
		<comments>http://www.techfounder.net/2008/09/04/chrome-is-out-google-has-my-vote/#comments</comments>
		<pubDate>Thu, 04 Sep 2008 21:36:24 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[The Webs]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=120</guid>
		<description><![CDATA[So Google Chrome was unleashed on the unsuspecting public yesterday with very little preceding hype. It enters a market that has thus far has had only two major players - Mozilla and Microsoft. Backed by marketing power that is unrivaled in the online world, it is strongly positioned to take both on (and especially Microsoft). [...]]]></description>
			<content:encoded><![CDATA[<p>So Google Chrome was unleashed on the unsuspecting public yesterday with very little preceding hype. It enters a market that has thus far has had only two major players - Mozilla and Microsoft. Backed by marketing power that is unrivaled in the online world, it is strongly positioned to take both on (and especially Microsoft).</p>
<p>A web browser built on the webkit engine (same as Safari), Chrome offers a simple UI and extensive support for web technologies. Having used it for a couple of days now, it is striking to me how obvious it is that Google is a web company - in bold contrast to another software giant currently pushing for their next-gen browser.</p>
<p><span id="more-120"></span></p>
<p>There is a lot to like about Chrome: <strong>It is open-source</strong>. The tab oriented UI is a great innovation. The simple UI allows for an impressively large work area in the browser. It comes with a built in DOM inspector and Javascript debugger (almost rivaling Firebug for functionality). The tab/process manager is an awesome feature (though <a title="John Resign on Chrome process manager" href="http://ejohn.org/blog/google-chrome-process-manager" target="_blank">a possible source of contention</a>). And most importantly - aside from minor inconsistencies from the standards (shared by Safari), all web sites render perfectly under Chrome.</p>
<p>I was somehow surprised though it only reaches a score of 79 on the acid3 test, since it is based on an engine that already scored a perfect 100 (webkit). For comparison, Firefox 3.0.1 achieves a score of 71 (though with less graphical glitches), IE7 achieves a measly score of 13 in about triple the time, and IE8 beta 2 just barely beats that with a very low 21.</p>
<p>It is my personal hope that Chrome steals enough market share from Microsoft to help push out older IE versions, thus catapulting the web forward. This has been a fantastic move by Google, and the online world is watching to see how the market will respond. Is a Google OS next on the agenda?</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=120" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F09%2F04%2Fchrome-is-out-google-has-my-vote%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F09%2F04%2Fchrome-is-out-google-has-my-vote%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/09/04/chrome-is-out-google-has-my-vote/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Handling mail and mime in PHP using the Zend Framework</title>
		<link>http://www.techfounder.net/2008/07/18/handling-mail-and-mime-in-php-using-the-zend-framework/</link>
		<comments>http://www.techfounder.net/2008/07/18/handling-mail-and-mime-in-php-using-the-zend-framework/#comments</comments>
		<pubDate>Fri, 18 Jul 2008 04:43:40 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=97</guid>
		<description><![CDATA[Handling mail is a very common requirement in web applications. Even the most basic sites usually have a contact form that sends a mail through the server instead of putting a contact mail address for spam-spiders to find. Using PHP's built in function (aptly named mail() ) is relatively straightforward - until you need slightly [...]]]></description>
			<content:encoded><![CDATA[<p>Handling mail is a very common requirement in web applications. Even the most basic sites usually have a contact form that sends a mail through the server instead of putting a contact mail address for spam-spiders to find. Using PHP's built in function (aptly named <a target="_blank" href="http://www.php.net/function.mail">mail()</a> ) is relatively straightforward - until you need slightly more advanced features, such as adding and encoding email headers or sending multiple mails efficiently.<br />
<span id="more-97"></span><br />
Fortunately, the Zend Framework comes with a very capable mail component called <a href="http://framework.zend.com/manual/en/zend.mail.html" target="_blank">Zend_Mail</a>. Zend_Mail abstracts some of the more tedious aspects of mail handling with PHP, including:</p>
<ol>
<li>Adding and encoding email headers</li>
<li>Protection against <a href="http://www.php-security.org/MOPB/MOPB-34-2007.html" target="_blank">header injection</a></li>
<li>Multiple mail transports (SMTP is useful for sending multiple mails)</li>
<li>Creating and handling mime-compliant multipart messages</li>
<li>Composing HTML emails</li>
<li>Reading mail boxes</li>
</ol>
<h2>Sending mails</h2>
<p>Using Zend_Mail is extremely straightforward: (Taken from the ZF documentation)</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"> <span style="color: #b1b100;">require_once</span> <span style="color: #ff0000;">'Zend/Mail.php'</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$mail</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">setBodyText</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'This is the text of the mail.'</span><span style="color: #66cc66;">&#41;</span>; <span style="color: #808080; font-style: italic;">//Alternatively using setBodyHtml for HTML messages</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">setFrom</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'somebody@example.com'</span>, <span style="color: #ff0000;">'Some Sender'</span><span style="color: #66cc66;">&#41;</span>; <span style="color: #808080; font-style: italic;">//Adds a 'from' header</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">addTo</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'somebody_else@example.com'</span>, <span style="color: #ff0000;">'Some Recipient'</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">setSubject</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'TestSubject'</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">send</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></li></ol></pre>
<p>Simple.</p>
<p>For sending multiple mails it is better to use the SMTP protocol for performance reasons. Doing this manually with PHP is <a href="http://www.mustap.com/phpzone_post_95_sending-email-in-php-the-hac" target="_blank">quite elaborate</a> however, but with Zend_Mail it is almost as simple as before:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"> <span style="color: #b1b100;">require_once</span> <span style="color: #ff0000;">'Zend/Mail.php'</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #808080; font-style: italic;">// Create transport</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #b1b100;">require_once</span> <span style="color: #ff0000;">'Zend/Mail/Transport/Smtp.php'</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$transport</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail_Transport_Smtp<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'localhost'</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #808080; font-style: italic;">// Sending out multiple mails at once</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #b1b100;">for</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$i</span> = <span style="color: #cc66cc;">0</span>; <span style="color: #0000ff;">$i</span> &gt; <span style="color: #cc66cc;">5</span>; <span style="color: #0000ff;">$i</span>++<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #0000ff;">$mail</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">addTo</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'studio@peptolab.com'</span>, <span style="color: #ff0000;">'Test'</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">setFrom</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'studio@peptolab.com'</span>, <span style="color: #ff0000;">'Test'</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">setSubject</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'Demonstration - Sending Multiple Mails per SMTP Connection'</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">setBodyText</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'...Your message here...'</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">send</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$transport</span><span style="color: #66cc66;">&#41;</span>; <span style="color: #808080; font-style: italic;">//Using the SMTP transport for sending the mail</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #66cc66;">&#125;</span></div></li></ol></pre>
<p>* SMTP connections <a href="http://framework.zend.com/manual/en/zend.mail.smtp-secure.html">can be secured with SSL or TLS</a> for sending sensitive mails.</p>
<p>This procedure reuses the SMTP connection for the lifetime of the entire process which is much better than sending out mails using the mail() function.</p>
<h2>Reading mail boxes</h2>
<p>Reading mail boxes is similarly simple. Zend_Mail supports POP3 and IMAP for remote connections, and Mbox and Maildir for local connections (mail boxes residing on the same server). Sample usage:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$mail</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail_Storage_Pop3<span style="color: #66cc66;">&#40;</span> <span style="color: #808080; font-style: italic;">//Configuring with host and credentials</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	<a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'host'</span> =&gt; <span style="color: #ff0000;">'example.com'</span>,</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	      <span style="color: #ff0000;">'user'</span> =&gt; <span style="color: #ff0000;">'test'</span>,</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	      <span style="color: #ff0000;">'password'</span> =&gt; <span style="color: #ff0000;">'test'</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	<span style="color: #66cc66;">&#41;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><a href="http://www.php.net/echo"><span style="color: #000066;">echo</span></a> <span style="color: #0000ff;">$mail</span>-&gt;<span style="color: #006600;">countMessages</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> . <span style="color: #ff0000;">&quot; messages found<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>; <span style="color: #808080; font-style: italic;">//Outputting message count in mail box</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #b1b100;">foreach</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$mail</span> <span style="color: #b1b100;">as</span> <span style="color: #0000ff;">$message</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span> <span style="color: #808080; font-style: italic;">//Outputting message subjects</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <a href="http://www.php.net/echo"><span style="color: #000066;">echo</span></a> <span style="color: #ff0000;">&quot;Mail from &quot;</span> . <span style="color: #0000ff;">$message</span>-&gt;<span style="color: #006600;">from</span> . <span style="color: #ff0000;">&quot;: &quot;</span> . <span style="color: #0000ff;">$message</span>-&gt;<span style="color: #006600;">subject</span> . <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #66cc66;">&#125;</span></div></li></ol></pre>
<p>Using the different storage options is very similar. Messages returned are instances of the Zend_Mail_Message class, which provides an API for manipulating mail messages.</p>
<h2>Advanced Usage</h2>
<p>Working with multipart MIME messages is very tedious. The <a href="http://www.faqs.org/rfcs/rfc1521.html" target="_blank">MIME specifications</a> are rather complex and MIME messages aren't very readable to humans, and therefor we would like to be able to use Zend_Mail_Message API to reduce the pains of dealing with MIME messages. There are several general cases when working with MIME messages:</p>
<ol>
<li>Working with messages which are stored as strings in a database</li>
<li>Working with messages which are read from files</li>
<li>Working with incoming messages directly (from an <a href="http://www.php.net/wrappers.php" target="_blank">input stream</a>)</li>
</ol>
<p>Zend_Mail_Messsage can receive either a raw message string in its constructor, a file name or a file handle. Examples of instancing a Zend_Mail_Message object with all three:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #808080; font-style: italic;">//From a raw string</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$message</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail_Message<span style="color: #66cc66;">&#40;</span><a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'raw'</span> =&gt; <span style="color: #0000ff;">$rawMessage</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>; </div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #808080; font-style: italic;">//From a file name</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$message</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail_Message<span style="color: #66cc66;">&#40;</span><a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'file'</span> =&gt; <span style="color: #ff0000;">'/path/to/mail/message'</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #808080; font-style: italic;">//From a file handle</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$handle</span> = <a href="http://www.php.net/fopen"><span style="color: #000066;">fopen</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'/path/to/mail/message'</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$message</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail_Message<span style="color: #66cc66;">&#40;</span><a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'file'</span> =&gt; <span style="color: #0000ff;">$handle</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div></li></ol></pre>
<p>The last one might seem a bit redundant. However it opens up creative ways to intercept mail directly using the input wrapper:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$handle</span> = <a href="http://www.php.net/fopen"><span style="color: #000066;">fopen</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;php://stdin&quot;</span>, <span style="color: #ff0000;">&quot;r&quot;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$message</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail_Message<span style="color: #66cc66;">&#40;</span><a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'file'</span> =&gt; <span style="color: #0000ff;">$handle</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div></li></ol></pre>
<p>The tricky part is directing the mail message stream into the script. You can read more about that in this <a href="http://www.evolt.org/article/Incoming_Mail_and_PHP/18/27914/index.html" target="_blank">nice tutorial from evolt</a>.</p>
<h2>Some minor bugs and fixes</h2>
<p>Nothing is perfect however. Current version of Zend_Mail (shipped with the Zend_Framework ver. 1.5.2) has a buggy implementation of the headers encoding. This will become noticeable when working with UTF encoded mails. Fortunately there is a <a href="http://framework.zend.com/issues/browse/ZF-3641">quick fix for that</a> posted in the ZF issue tracker (and hopefully will be tested and integrated in the next release). </p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=97" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F07%2F18%2Fhandling-mail-and-mime-in-php-using-the-zend-framework%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F07%2F18%2Fhandling-mail-and-mime-in-php-using-the-zend-framework%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/07/18/handling-mail-and-mime-in-php-using-the-zend-framework/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Content syndication needs rebranding</title>
		<link>http://www.techfounder.net/2008/07/01/content-syndication-needs-rebranding/</link>
		<comments>http://www.techfounder.net/2008/07/01/content-syndication-needs-rebranding/#comments</comments>
		<pubDate>Tue, 01 Jul 2008 02:04:59 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[The Webs]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=52</guid>
		<description><![CDATA[Content syndication or Feeds, is a mashup of technologies that provides an easy way to keep track of updates from multiple content sources. Despite being very useful, it has yet to find widespread acceptance amongst Internet users. Direct usage statistics are hard to ascertain with great precision, but they revolve around 4%-6% of the total [...]]]></description>
			<content:encoded><![CDATA[<p>Content syndication or <a title="Wikipedia: Web Feeds" href="http://en.wikipedia.org/wiki/Web_feed" target="_blank">Feeds</a>, is a mashup of technologies that provides an easy way to keep track of updates from multiple content sources. Despite being very useful, it has yet to find widespread acceptance amongst Internet users.</p>
<p>Direct usage statistics are hard to ascertain with great precision, but they revolve around 4%-6% of the total Internet population, which is not much. More feeds are being consumed indirectly by aggregation sites, such as my yahoo and iGoogle (as this <a title="Yahoo! on RSS feeds" href="http://publisher.yahoo.com/rss/RSS_whitePaper1004.pdf " target="_blank">Yahoo! paper</a> shows), which shows that there is market ready to consume more feeds.</p>
<p>So why feed usage isn't more widespread?<span id="more-52"></span></p>
<p>1. Lack of awareness<br />
Most people are simply unaware of the benefits of using feeds. When asked about what are feeds, only 12% of the Internet public were aware of its meaning. I will admit myself to be one of the ignorant up until not too long ago (about 6 months). For me that is mind boggling - I consider myself an extremely technical user and I love the Internet. How was I not aware of how useful this technology is?</p>
<p>2. Confusing terminology<br />
What is a feed? syndication? aggregators? to someone hearing those terms for the first time it can be very discouraging. There is alternative terminology such as subscriptions and readers which is more similar to natural language but it isn't used enough to explain the technology.</p>
<p>3. Competing standards<br />
<a title="Wikipedia: Atom\" href="http://en.wikipedia.org/wiki/Atom_%28standard%29" target="_blank">Atom</a> and <a title="Wikipedia: RSS" href="http://en.wikipedia.org/wiki/RSS_(file_format)" target="_blank">RSS</a> are competing standards for web syndication. Both are offering basically the same solution, so why haven't they merged yet? (actually <a title="Wikipedia: Barriers to adoption" href="http://en.wikipedia.org/wiki/Atom_%28standard%29#Barriers_to_adoption" target="_blank">there are reasons</a>, but not very good ones). Most sites now offer both, which only add to the confusion as the names of the different formats are sometimes use interchangeably with the term Feed.</p>
<p>Content syndication is just waiting to erupt. Someone needs to take initiative and promote this technology to the masses:</p>
<p>1. Create awareness<br />
The same way open standards such as OpenID made a splash some time ago, content syndication needs visibility. And in contrast to open standards, web syndication is useful now.</p>
<p>2. Improve description<br />
Hand in hand in creating visibility, a low-tech explanation of the benefits of syndication should be drafted and used liberally. This will go a long way to break the barriers of adoption.</p>
<p>3. Unify standards<br />
One to rule them all as they say. Since this is an open standard and certainly not for profit, the only real consideration would be backwards compatibility. Declare RSS officially as the standby for compatibility and Atom as the active standard for future development.</p>
<p>4. Do not call the technology by its standards name!<br />
Stop using RSS or Atom feeds as the name for the subscription feature. People are much more likely to hit 'Subscribe' than 'Join my RSS feeds'...</p>
<p>P.S.<br />
Syndication always reminds me of the PC classic '<a title="Home of the underdogs: Syndicate" href="http://www.the-underdogs.info/game.php?gameid=1916" target="_blank">Syndicate</a>' in which rival clans fight for world domination in a futuristic setting using cyborg agents. Good times...</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=52" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F07%2F01%2Fcontent-syndication-needs-rebranding%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F07%2F01%2Fcontent-syndication-needs-rebranding%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/07/01/content-syndication-needs-rebranding/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jQuery and Prototype the choice of top websites</title>
		<link>http://www.techfounder.net/2008/06/14/jquery-and-prototype-the-choice-of-top-websites/</link>
		<comments>http://www.techfounder.net/2008/06/14/jquery-and-prototype-the-choice-of-top-websites/#comments</comments>
		<pubDate>Sat, 14 Jun 2008 00:54:59 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=68</guid>
		<description><![CDATA[Pingdom has complied a list of the Javascript frameworks used by the top sites on the web (top 100 in Alexa US, Webware's top 100 web apps). jQuery and Prototype are the top choices, getting 11 and 13 respectively. It's interesting to note that Dojo is not even featured on this list, though if you [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://royal.pingdom.com/?p=305">Pingdom has complied a list</a> of the Javascript frameworks used by the top sites on the web (top 100 in Alexa US, Webware's top 100 web apps). jQuery and Prototype are the top choices, getting 11 and 13 respectively. </p>
<p>It's interesting to note that Dojo is not even featured on this list, though if you check the comments you'll see that it is used partially at the apple site (in the apple store). This makes me wonder even more regarding Zend's latest decision to <a href="http://weierophinney.net/matthew/archives/176-Zend-Framework-Dojo-Integration.html">integrate Dojo into their framework</a>. As I commented in that release statement (comment #6 is mine), I feel that Dojo isn't appropriate as the default for the framework as it is more complex and much less documented than the other top frameworks. </p>
<p>As a long time jQuery developer I have no intentions of integrating a new Javascript library into my development environment, so I'm obviously biased. I still feel as though Zend missed an opportunity here to better cater to the needs of a broader user base and instead chose to prioritize its partners best interests.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=68" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F06%2F14%2Fjquery-and-prototype-the-choice-of-top-websites%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F06%2F14%2Fjquery-and-prototype-the-choice-of-top-websites%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/06/14/jquery-and-prototype-the-choice-of-top-websites/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Semi-colon mystery explained, jQuery UI released</title>
		<link>http://www.techfounder.net/2008/06/11/semi-colon-mystery-explained-jquery-ui-released/</link>
		<comments>http://www.techfounder.net/2008/06/11/semi-colon-mystery-explained-jquery-ui-released/#comments</comments>
		<pubDate>Wed, 11 Jun 2008 01:33:50 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=65</guid>
		<description><![CDATA[Javascript is a very mysterious language. Its prototypical inheritance structure and its function == object == function concepts are quite different compared to standard OO languages. As I did with PHP, I try my best to learn best good practices by studying frameworks I like, and in Javascript's case that would be jQuery. I had [...]]]></description>
			<content:encoded><![CDATA[<p>Javascript is a very mysterious language. Its <a title="Wikipedia: Prototypical Inheritance" href="http://en.wikipedia.org/wiki/Prototype-based_programming" target="_blank">prototypical inheritance</a> structure and its function == object == function concepts are quite different compared to standard OO languages. As I did with PHP, I try my best to learn <span style="text-decoration: line-through;">best</span> good practices by studying frameworks I like, and in Javascript's case that would be <a title="jQuery" href="http://jquery.com" target="_blank">jQuery</a>.</p>
<p>I had believed I figured out most of the conventions used in the jQuery source code, however a recent addition has been bugging me and I could not find a reasonable explanation for it - I'm talking about the mysterious semi-colons appearing at the beginning of some of the source files in the library. What is its purpose? Does it make the closure invisible to giant robots from outer space? I had no leads to go on.</p>
<p><a title="jQuery Rule 1.0 released" href="http://flesler.blogspot.com/2008/01/jqueryrule-10-released.html" target="_blank">This blog post</a> by the jQuery.rule team however, reveals the truth about the semi-colon debacle - apparently they're used for safe file concatenation (string join). Well that's a load off my chest. You learn something new every day.</p>
<p>In related news, jQuery UI 1.5 has been officially released, says the <a title="jQuery 1.5 released" href="http://jquery.com/blog/2008/06/09/jquery-ui-v15-released-focus-on-consistent-api-and-effects/" target="_blank">jQuery enquirer</a>. jQuery 1.5 is an extensive UI oriented extension to jQuery, and version 1.5 bring forth many improvements such as a tighter API, an effects library called enchant, a skinning mechanism and plenty of bug fixes. I'm just excited they finally updated their <a title="jQuery UI documentation" href="http://docs.jquery.com/UI" target="_blank">documentation</a>, as I've been using it for a while going only by source code.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=65" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F06%2F11%2Fsemi-colon-mystery-explained-jquery-ui-released%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F06%2F11%2Fsemi-colon-mystery-explained-jquery-ui-released%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/06/11/semi-colon-mystery-explained-jquery-ui-released/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pagination with Zend_Db_Select</title>
		<link>http://www.techfounder.net/2008/06/11/pagination-with-zend_db_select/</link>
		<comments>http://www.techfounder.net/2008/06/11/pagination-with-zend_db_select/#comments</comments>
		<pubDate>Wed, 11 Jun 2008 01:02:39 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=63</guid>
		<description><![CDATA[A common (web) interface feature is to divide a long list of items into numbered pages, a technique called pagination. I'll describe in brief some shortcuts I use with Zend_Db_Select to retrieve the row count and calculate the number of pages for complicated queries. Pagination of long database result sets is done by retrieving a [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.techfounder.net/wp-content/uploads/2008/06/pages.gif" alt="Pages, by google" class="header" />A common (web) interface feature is to divide a long list of items into numbered pages, a technique called <a title="Wikipedia: Pagination" href="http://en.wikipedia.org/wiki/Pagination" target="_blank">pagination</a>. I'll describe in brief some shortcuts I use with Zend_Db_Select to retrieve the row count and calculate the number of pages for complicated queries.<br />
<span id="more-63"></span><br />
Pagination of long database result sets is done by retrieving a limited number of rows from a specific offset. For example, if we want each page to display 10 rows, the offset for page 3 would be 21 (page 1 contains rows 1-10, page 2 containts rows 11-20 and so forth. In MySQL, the first row has an offset of 0 (zero), so for the previous example a page 3 in a 10 rows-per-page pagination system would be queried by an offset of 20.</p>
<p>Paginated queries typically look like this:</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">SELECT</span> * <span style="color: #993333; font-weight: bold;">FROM</span> articles <span style="color: #993333; font-weight: bold;">LIMIT</span> <span style="color: #cc66cc;">20</span>, <span style="color: #cc66cc;">10</span> //Selecting page <span style="color: #cc66cc;">3</span> <span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #993333; font-weight: bold;">TABLE</span> articles <span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">10</span> rows per page<span style="color: #66cc66;">&#41;</span></pre>
<p>In order to generate page navigation, we would need to know the number of pages. We count the total rows that our query generates without the limit clause:</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">SELECT</span> COUNT<span style="color: #66cc66;">&#40;</span>*<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">FROM</span> articles //Count <span style="color: #993333; font-weight: bold;">ALL</span> rows <span style="color: #993333; font-weight: bold;">IN</span> <span style="color: #993333; font-weight: bold;">TABLE</span> articles</pre>
<p>And then we can perform simple arithmetics to calculate the number of pages.</p>
<p>It's relatively straightforward when dealing with simple queries, however for complicated queries (with multiple JOIN clauses) it can be quite a chore.</p>
<p>Lets switch to the Zend Framework and compose ourselves a somewhat elaborate query using Zend_Db_Select:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$select</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Db_Select<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$select</span> -&gt; <span style="color: #006600;">from</span><span style="color: #66cc66;">&#40;</span><a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'pil'</span> =&gt; <span style="color: #ff0000;">'project_item_list'</span><span style="color: #66cc66;">&#41;</span>,<a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'*'</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	-&gt; <a href="http://www.php.net/join"><span style="color: #000066;">join</span></a><span style="color: #66cc66;">&#40;</span><a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'pt'</span> =&gt; <span style="color: #ff0000;">'project_tasks'</span><span style="color: #66cc66;">&#41;</span>,<span style="color: #ff0000;">'pt.id=pil.task_id'</span>,<a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'task'</span>,<span style="color: #ff0000;">'ptid'</span> =&gt; <span style="color: #ff0000;">'id'</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	-&gt; <a href="http://www.php.net/join"><span style="color: #000066;">join</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'projects'</span>,<span style="color: #ff0000;">'projects.id=pt.project_id'</span>,<a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	-&gt; <a href="http://www.php.net/join"><span style="color: #000066;">join</span></a><span style="color: #66cc66;">&#40;</span><a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'ptu'</span> =&gt; <span style="color: #ff0000;">'projects_to_users'</span><span style="color: #66cc66;">&#41;</span>,<span style="color: #ff0000;">'ptu.project_id=projects.id'</span>,<a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	-&gt; <a href="http://www.php.net/join"><span style="color: #000066;">join</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'users'</span>,<span style="color: #ff0000;">'users.id=pil.assigned_to'</span>,<a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'assignee'</span> =&gt; <span style="color: #ff0000;">'name'</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	-&gt; <span style="color: #006600;">where</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;pt.project_id='&quot;</span> . <span style="color: #0000ff;">$projectId</span> . <span style="color: #ff0000;">&quot;'&quot;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	-&gt; <span style="color: #006600;">where</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;ptu.user_id='&quot;</span> . <span style="color: #0000ff;">$userId</span> . <span style="color: #ff0000;">&quot;'&quot;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	-&gt; <span style="color: #006600;">where</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;task_id='&quot;</span> . <span style="color: #0000ff;">$taskId</span> . <span style="color: #ff0000;">&quot;'&quot;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	-&gt; <span style="color: #006600;">where</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;pil.assigned_to='&quot;</span> . <span style="color: #0000ff;">$assignedTo</span> . <span style="color: #ff0000;">&quot;'&quot;</span><span style="color: #66cc66;">&#41;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	-&gt; <span style="color: #006600;">limitPage</span><span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">3</span>,<span style="color: #cc66cc;">10</span><span style="color: #66cc66;">&#41;</span>;</div></li></ol></pre>
<p>Plenty of JOIN goodness. You'll notice the limitPage() call at the end, which is a nice Zend_Db_Select helper to add a limit clause by a page number (in this case it means limit by page 3, 10 rows per page). </p>
<p>So how do we count the number of rows? We'd want to have a COUNT() function returned from the base table (the one that appears at the from clause), however if we mix that with other columns, we'd have to use GROUP BY which would mess up our result-set. </p>
<p>There are two ways to handle this - generating a similar query sans the columns and the limit clauses, or using the SQL_CALC_FOUND_ROWS() function. The first approach results in two seperate queries while the second is just one query, however it is not that obvious which <a href="http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/">one is better for performance</a>. For our case we'll use the first method, duplicating the original query while stripping out whats needed.</p>
<p>We could recompose the entire query using copy-paste methods, removing the columns and the limit clauses, but we would like a more general solution. I extend Zend_Db_Select and add a simple utility method:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #000000; font-weight: bold;">class</span> Techfounder_Db_Select <span style="color: #000000; font-weight: bold;">extends</span> Zend_Db_Select <span style="color: #66cc66;">&#123;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> prepareForCount<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$countColName</span> = <span style="color: #ff0000;">'count'</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">		<span style="color: #0000ff;">$this</span> -&gt; <a href="http://www.php.net/reset"><span style="color: #000066;">reset</span></a><span style="color: #66cc66;">&#40;</span>Zend_Db_Select::<span style="color: #006600;">COLUMNS</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">		<span style="color: #0000ff;">$this</span> -&gt; <a href="http://www.php.net/reset"><span style="color: #000066;">reset</span></a><span style="color: #66cc66;">&#40;</span>Zend_Db_Select::<span style="color: #006600;">LIMIT_COUNT</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">		<span style="color: #0000ff;">$this</span> -&gt; <a href="http://www.php.net/reset"><span style="color: #000066;">reset</span></a><span style="color: #66cc66;">&#40;</span>Zend_Db_Select::<span style="color: #006600;">LIMIT_OFFSET</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">		<span style="color: #0000ff;">$table</span> = <a href="http://www.php.net/key"><span style="color: #000066;">key</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$this</span> -&gt; _parts<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'from'</span><span style="color: #66cc66;">&#93;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">		<span style="color: #0000ff;">$this</span> -&gt; _parts<span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'columns'</span><span style="color: #66cc66;">&#93;</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">			<span style="color: #cc66cc;">0</span> =&gt; <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">				<span style="color: #cc66cc;">0</span> =&gt; <span style="color: #0000ff;">$table</span>,</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">				<span style="color: #cc66cc;">1</span> =&gt; <span style="color: #000000; font-weight: bold;">new</span> Zend_Db_Expr<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'COUNT(*)'</span><span style="color: #66cc66;">&#41;</span>,</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">				<span style="color: #cc66cc;">2</span> =&gt; <span style="color: #0000ff;">$countColName</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">			<span style="color: #66cc66;">&#41;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">		<span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">		<span style="color: #b1b100;">return</span> <span style="color: #0000ff;">$this</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">	<span style="color: #66cc66;">&#125;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #66cc66;">&#125;</span></div></li></ol></pre>
<p>The prepareForCount() method modifies a select object to strip all previously set columns and LIMIT clauses, and to set a COUNT() function on the base table in the query (the row count is returned as a key named 'count'). We can now use this Select class to do the following:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$select</span> = <span style="color: #000000; font-weight: bold;">new</span> Techfounder_Db_Select<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">... <span style="color: #808080; font-style: italic;">//Multiple JOIN query, as composed previously</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$countSelect</span> = clone <span style="color: #0000ff;">$select</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$countSelect</span> -&gt; <span style="color: #006600;">prepareForCount</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$result</span> = <span style="color: #0000ff;">$db</span> -&gt; <span style="color: #006600;">fetchRow</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$countSelect</span><span style="color: #66cc66;">&#41;</span>; <span style="color: #808080; font-style: italic;">//$db is an instance of Zend_Db_Adapter</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$pageCount</span> = <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#40;</span>int<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$result</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'count'</span><span style="color: #66cc66;">&#93;</span> - <span style="color: #cc66cc;">1</span><span style="color: #66cc66;">&#41;</span> / <span style="color: #0000ff;">$rowCount</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span> + <span style="color: #cc66cc;">1</span>; <span style="color: #808080; font-style: italic;">//Using the number of rows to calculate the number of pages.</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #808080; font-style: italic;">//$rowCount is the number of rows per page</span></div></li></ol></pre>
<p>I go even a step further, and encapsulate this short process in a countPages() method in a custom wrapper for Zend_Db_Select (called Zend_Db_Gateway, for which <a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Db_Gateway">I submitted a proposal</a> and would talk about in my conclusion to my Models in the Zend Framework series).</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=63" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F06%2F11%2Fpagination-with-zend_db_select%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F06%2F11%2Fpagination-with-zend_db_select%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/06/11/pagination-with-zend_db_select/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>jQuery development marches on (1.2.6)</title>
		<link>http://www.techfounder.net/2008/06/04/jquery-development-marches-on-126/</link>
		<comments>http://www.techfounder.net/2008/06/04/jquery-development-marches-on-126/#comments</comments>
		<pubDate>Wed, 04 Jun 2008 21:52:56 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=47</guid>
		<description><![CDATA[jQuery 1.2.6 was released recently, with plenty of bug-fixes and speed improvements. Most notably event handling was sped up 103% and the dimensions plugin was integrated into the framework (the dimensions plugin is an API to calculate elements sizes in a cross-browser and reliable way). In addition, jQuery UI has recently reached release-candidate status, which [...]]]></description>
			<content:encoded><![CDATA[<p>jQuery 1.2.6 was <a title="jQuery 1.2.6 release" href="http://jquery.com/blog/2008/06/04/jquery-126-events-100-faster/" target="_blank">released recently</a>, with plenty of bug-fixes and speed improvements. Most notably event handling was sped up 103% and the dimensions plugin was integrated into the framework (the dimensions plugin is an API to calculate elements sizes in a cross-browser and reliable way).</p>
<p>In addition, <a title="jQuery UI" href="http://ui.jquery.com/" target="_blank">jQuery UI</a> has recently reached <a title="jQuery UI release candidate 1" href="http://jquery.com/blog/2008/06/02/jquery-ui-15-release-candidate-were-getting-excited/" target="_blank">release-candidate</a> status, which means all non minor or trivial issues were solved and it's getting to the point that it will production ready shortly. I have been using its components for a while in anticipation of such release, and hopefully they will have their documentation up soon.</p>
<p>Good times to be jQuery developers <img src='http://www.techfounder.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=47" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F06%2F04%2Fjquery-development-marches-on-126%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F06%2F04%2Fjquery-development-marches-on-126%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/06/04/jquery-development-marches-on-126/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP Versus The World</title>
		<link>http://www.techfounder.net/2008/05/22/php-versus-the-world/</link>
		<comments>http://www.techfounder.net/2008/05/22/php-versus-the-world/#comments</comments>
		<pubDate>Fri, 23 May 2008 01:27:59 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=26</guid>
		<description><![CDATA[Apparently PHP has a lot of functions starting with the letter A, and people find it offensive (Read a pro-PHP response to that article here). I never knew it had so many to be honest. You know why? because I never bothered to go to the function index... PHP.net has a strategically located search bar [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.techfounder.net/wp-content/uploads/2008/05/phpvsworld.gif" alt="php vs world" class="header" />Apparently PHP has a lot of functions starting with the letter A, <a href="http://www.codinghorror.com/blog/archives/001119.html" target="_blank">and people find it offensive</a> (Read a pro-PHP response to that article <a href="http://php100.wordpress.com/2008/05/21/secret-of-php/" target="_blank">here</a>). </p>
<p><span id="more-26"></span>I never knew it had so many to be honest. You know why? because I never bothered to go to the function index... PHP.net has a strategically located search bar in the top-right corner. Every time I needed to find a function I got to it from there or google. And it was always easy finding the right function (I guess the names aren't that bad after all). </p>
<p>Why is function diversity bad? While I agree that the naming conventions could be better, how should this be a problem to anyone? Unless of course you want to browse the function index... </p>
<p>It seems to me non-PHP developers are much more prone to deride PHP than PHP developers are to do the same for other languages. I guess it's because most PHP developers have no experience in other languages (expect maybe C/C++, which are not very relevant for compaison), so they don't have credentials to offer such criticism. Further more, it's more common to hear non-PHP developers complain about their own languages.</p>
<p>I have always maintained that the server-side language is irrelevant in the end. You can implement any web-based service / site / application in any of the modern web languages, it all depends on your development team skills. The user doesn't care what runs on the server, only what he is getting in the browser. And for all you detractors, yes you can build maintainable, secure and elegant applications in PHP quickly if you know what you are doing.</p>
<p>Having said that, in praise of PHP - It's open source and free, easy deployment (low-dependencies, simple packaging), great community (that has produced many professional-grade open-source packages),  great documentation, available on most servers, dynamically typed and is easy to use. Also, from personal experience a PHP-dev in general has better command of client-side markup (HTML and CSS), as PHP blends easily with the markup and such blend is shown often in basic tutorials.</p>
<p>I liked <a href="http://www.oreillynet.com/ruby/blog/2007/09/7_reasons_i_switched_back_to_p_1.html">this account</a> by a guy who went back from ROR to PHP, as it puts the emphasis on the developer's skills instead of the language choice. Let everybody pick their favorite, why can't we all just get along? <img src='http://www.techfounder.net/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>(And by the way... RoR is a <strong>framework</strong> for Ruby, would people stop comparing it to languages? Thank you.)</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=26" width="1" height="1" style="display: none;" /><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F05%2F22%2Fphp-versus-the-world%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.techfounder.net%2F2008%2F05%2F22%2Fphp-versus-the-world%2F" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/05/22/php-versus-the-world/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
