<?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>No, I am better than that! &#187; SQL</title>
	<atom:link href="http://rickosborne.org/blog/category/programming/sql/feed/" rel="self" type="application/rss+xml" />
	<link>http://rickosborne.org/blog</link>
	<description>Striving to subdue the mediocrity.</description>
	<lastBuildDate>Sun, 21 Aug 2011 23:27:16 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Because no one in Web Dev uses interfaces</title>
		<link>http://rickosborne.org/blog/2011/03/because-no-one-in-web-dev-uses-interfaces/</link>
		<comments>http://rickosborne.org/blog/2011/03/because-no-one-in-web-dev-uses-interfaces/#comments</comments>
		<pubDate>Fri, 25 Mar 2011 00:26:09 +0000</pubDate>
		<dc:creator>Rick Osborne</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[adb]]></category>
		<category><![CDATA[couchdb]]></category>
		<category><![CDATA[mongodb]]></category>
		<guid isPermaLink="false">http://rickosborne.org/blog/?p=1305</guid>
		<description><![CDATA[One of my long-standing assertions is that perfect OOP is amusing and all, but it tends to stumble a bit when it comes to web development. Interfaces are my go-to example for this. How often, in everyday real-world web development, are you suddenly going to need to swap out one Model class for another? &#8220;Well, [...]]]></description>
			<content:encoded><![CDATA[<p>One of my long-standing assertions is that perfect OOP is amusing and all, but it tends to stumble a bit when it comes to web development.  Interfaces are my go-to example for this.  How often, in everyday real-world web development, are you suddenly going to need to swap out one Model class for another?  &ldquo;Well, it&#8217;s Tuesday, so we need to load the Oracle layer instead of the Access layer.&rdquo;</p>
<p>To prove myself wrong, I built interfaces into this term&#8217;s <em>Advanced Database Structures</em> final exam practical.  The course has changed a bit since I started teaching it almost 2 years ago (holy crap!).  The core objectives are the same, but the underlying technology has been migrated to CouchDB and MongoDB.</p>
<p>From the exam instructions:</p>
<blockquote><p>You will be refactoring an existing application to work against a MongoDB database and a CouchDB database.  The codebase has been set up so that the application works as-is against a MySQL database and you can easily switch back and forth between each of the databases without having to modify anything other than the two Model classes you are responsible for.</p></blockquote>
<p class="noindent" style="float:right;margin:0 0 1.5em 1.5em"><a href="/blog/wp-content/uploads/2011/03/fsail-home.png"><img src="/blog/wp-content/uploads/2011/03/fsail-home-300x195.png" title="fsa.il link shortening service" width="300" height="195" class="aligncenter" /></a></p>
<p>The application I give them is a really simple proof-of-concept link shortening service called &ldquo;<kbd>fsa.il</kbd>&rdquo; and works the same way any other does: you input a long URL and your name, and you get a short URL back.  You can also come back later and track click-through and referring URL statistics.</p>
<p>Along the top of the page are tabs for three persistence layer choices: MySQL, MongoDB, and CouchDB.  The code they are given to start has a service that keeps track of your preferred data layer in your Session and handles all of the object creation for you.  The students, who built the MySQL version of the app for last month&#8217;s final exam, are given a working MySQL class, an interface for the persistence layer, and two stubbed-out skeleton classes for the other two layers.  Oh, and data dumps for quick import into all three layers.</p>
<p class="noindent" style="float:right; margin: 0 0 1em 1em;"><a href="/blog/wp-content/uploads/2011/03/php-impl.png"><img src="/blog/wp-content/uploads/2011/03/php-impl-300x149.png" alt="" title="Exam: Build out classes to talk to CouchDB and MongoDB" width="300" height="149" class="aligncenter size-medium wp-image-1314" /></a></p>
<p>That&#8217;s it.  That&#8217;s the exam.  The students just need to make the app work against the other two databases.  The interface definition is only five functions, and ends up yielding ~150-200 lines of code they need to bang out over 3&frac12; hours.</p>
<p>I think it&#8217;s a fun exam, but more importantly it&#8217;s a great piece for each student&#8217;s portfolio.  How much of a rock star would you look like on an interview if you could show a piece of code that worked against both relational and non-relational persistence layers without a truckload of third-party code?</p>
<p>It&#8217;s interesting writing code that will work against all three layers at the same time.  Naming conventions play a big part.  It&#8217;s easy in SQL to write a query that aliases column names to something friendlier to code:</p>
<pre class="sql">SELECT
    id AS linkID,
    long_url AS longURL,
    creator_name AS fullName,
    created_date AS createdDate
FROM link_links
WHERE (id = :linkId)</pre>
<p>And while you <em>could</em> use Map functions in CouchDB and MongoDB to alias your document field names, that seems like a heckuva waste.  Instead, you have to think about it a little bit harder beforehand.  If you don&#8217;t, you end up doing translation of array key names at the Model layer, which again seems sub-optimal.</p>
<p class="noindent" style="float:right; margin: 0 0 1em 1em;"><a href="/blog/wp-content/uploads/2011/03/mongo-console.png"><img src="/blog/wp-content/uploads/2011/03/mongo-console-300x174.png" alt="" title="The MongoDB Console" width="300" height="174" class="aligncenter size-medium wp-image-1310" /></a></p>
<p>The seemingly-minor differences between the implementations of Map/Reduce in CouchDB and MongoDB also some into play.  For example, when using the <kbd>group()</kbd> method in MongoDB you get an array back instead of a cursor, which means you can&#8217;t chain the <kbd>sort()</kbd> or <kbd>limit()</kbd> methods and have to do those in your Model.</p>
<p class="noindent" style="float:right;margin:0 0 1.5em 1.5em;"><a href="/blog/wp-content/uploads/2011/03/couchdb-futon.png"><img src="/blog/wp-content/uploads/2011/03/couchdb-futon-300x169.png" alt="" title="Editing a document in CouchDB Futon" width="300" height="169" class="aligncenter size-medium wp-image-1309" /></a></p>
<p>Sorting in CouchDB can also be tricky.  CouchDB assumes that you want to sort by your key and doesn&#8217;t give you any way to sort by value, making Top 10 lists a real pain to do.  You&#8217;re back to sorting in your Model.</p>
<p>String manipulation functions are another friction point.  SQL has a bevy of them, such as <kbd>LEFT()</kbd> and even the dreaded <kbd>LIKE</kbd>.  No such luck in either MongoDB or CouchDB.  You have to fake them in JavaScript.  Compare:</p>
<pre class="sql">WHERE (LEFT(:link, link_len) = link_start)</pre>
<p>Versus:</p>
<pre class="javascript">function(obj, prev) {
    if("http://".substr(0, obj.linkLen) === obj.linkStart)
        prev.count++;
}</pre>
<p>While that&#8217;s doable, and arguably potentially more powerful, it certainly isn&#8217;t what I would call <em>elegant</em>.  Similarly, CouchDB&#8217;s <kbd>startkey</kbd> and <kbd>endkey</kbd> parameters are nice and speedy, but there are times when I&#8217;d kill for a <kbd>keycontains</kbd> parameter.</p>
<p>More to the point, I think the course is teaching great skills.  Not only do students get to play with a variety of databases<sup>1</sup>, but they also get to see how easy it is to decouple layers of an application.  It&#8217;s nice that they get to muck around in Map/Reduce and REST APIs and what not, but the architectural and problem-solving lessons will take them so much further.</p>
<p>Oh, and just to head anyone off at the pass, the exam is done in PHP because working with MongoDB in PHP is stupid easy, while in ColdFusion it&#8217;s not for the faint of heart<sup>2</sup>.  The underlying Java library is usable, but requires a whole lot of syntactic icing.  That is, a metric ton of <kbd>javaCast</kbd> calls and type paranoia.  While that might be great for the students to encounter on a take-home assignment, it&#8217;s not something they need to be tripping over on a timed exam.  And yes, there are some great MongoDB libraries available for ColdFusion, but the point of the exam is to have the students show that they can do it themselves.</p>
<hr/>
<ol>
<li>
<p>If I had another 3 hours on the exam I would totally make the students add in layers for Sqlite and XML.</p>
</li>
<li>
<p>JSON-related quirks aside, using CouchDB requires an equivalent effort level from both PHP and ColdFusion.  Yay REST API.</p>
</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://rickosborne.org/blog/2011/03/because-no-one-in-web-dev-uses-interfaces/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>HTML5 in-browser SQL database storage</title>
		<link>http://rickosborne.org/blog/2010/03/html5-in-browser-sql-database-storage/</link>
		<comments>http://rickosborne.org/blog/2010/03/html5-in-browser-sql-database-storage/#comments</comments>
		<pubDate>Thu, 01 Apr 2010 04:28:20 +0000</pubDate>
		<dc:creator>Rick Osborne</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[sqlite]]></category>
		<category><![CDATA[storage]]></category>
		<guid isPermaLink="false">http://rickosborne.org/blog/?p=1213</guid>
		<description><![CDATA[I&#8217;ve been working on a new presentation to add to my Advanced Database Structures course: HTML5 Storage. It covers both the Web Storage and Web Database APIs. The former is a key-value store most likely to be used like beefed-up cookies or for form data persistence. The latter one is the more interesting of the [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been working on a new presentation to add to my <em>Advanced Database Structures</em> course: <em>HTML5 Storage</em>.  It covers both the <a href="http://www.w3.org/TR/webstorage/">Web Storage</a> and <a href="http://www.w3.org/TR/webdatabase/">Web Database</a> APIs.  The former is a key-value store most likely to be used like beefed-up cookies or for form data persistence.  The latter one is the more interesting of the two.</p>
<p>The Web SQL Database API is the logical progression from Google Gears: a programmatic way of accessing a SQL database that&#8217;s built into your web browser.  The current crop of browsers also have this all nice and sandboxed, so we shouldn&#8217;t end up suffering from some of the cross-site data access issues that were a worry in the previous generation.</p>
<p>So far, I think the best user-space access to the databases has to go to Safari.  You can get at them in the <em>Security</em> tab of the <em>Preferences</em> window:</p>
<p class="noindent"><img src="/blog/wp-content/uploads/2010/03/sudoku-db-win1.png" alt="" title="Database list in Safari preferences" width="456" height="171" class="aligncenter size-full wp-image-1222 noindent" /></p>
<p>But, even better than that, the Developer tools have an actual browser and query console&mdash;with smart SQL tab-completion!</p>
<p class="noindent"><img src="/blog/wp-content/uploads/2010/03/sudoku-db-console.png" alt="" title="sudoku-db-console" width="549" height="400" class="aligncenter size-full wp-image-1220 noindent" /></p>
<p>(I didn&#8217;t do anything magic to hide that <kbd>rs_hints</kbd> object&mdash;it&#8217;s a view.  I guess the Safari browser doesn&#8217;t show views.)</p>
<p>All of the current browser implementations use <a href="http://www.sqlite.org/">SQLite</a> as their storage engine.  SQLite has robust SQL support&mdash;it&#8217;s not meant to be a data warehouse, but it&#8217;ll probably do pretty much anything you&#8217;d think was sane to do inside a web browser.  I&#8217;ve successfully been able to use views, unions, several types of joins, indexes, and primary and foreign keys.</p>
<p>I&#8217;ve whipped up a quick app that shows off some of the technology: <a href="http://rickosborne.org/html5-dbstorage-sudoku/">Sudoku + HTML5</a>.  The storage layer for the app is done using the SQL engine, as is the business logic (game rules) of the Sudoku game itself.  The presentation layer is, of course, done in JavaScript using the HTML5 <kbd>canvas</kbd> API.  I&#8217;ve tested it in Safari, Opera, and Chrome.  Firefox, as of 3.5, does not yet support the SQL Database API.  I don&#8217;t have access to Internet Explorer to test it, but 8 shouldn&#8217;t work due to the use of the <kbd>canvas</kbd> element.</p>
<p class="noindent"><a href="/blog/wp-content/uploads/2010/03/sudoku-html5.png"><img src="/blog/wp-content/uploads/2010/03/sudoku-html5-300x181.png" alt="" title="Sudoku implemented with the HTML5 SQL Database Storage API" width="300" height="181" class="aligncenter size-medium wp-image-1217 noindent" border="0" /></a></p>
<p>Here&#8217;s a really quick overview of the Web SQL Database API:</p>
<pre class="javascript">var DB_NAME    = "ricko-sudoku";
var DB_VERSION = "1.0";
var DB_TITLE   = "Rick O's SQL Sudoku";
var DB_BYTES   = 500000;
var db = window.openDatabase(DB_NAME, DB_VERSION, DB_TITLE, DB_BYTES);</pre>
<p>The first part is very straightforward: I define a few constants and make the <kbd>openDatabase</kbd> call.  I&#8217;m using the constants to illustrate the variable placement&mdash;your app may just use literals in the function call.</p>
<p>The <kbd>openDatabase</kbd> call takes one additional optional argument that I have elided: a function callback.  If the database needs to be created, this being the first time you&#8217;ve ever tried to open this database, the extra function will be called.  The theory is that you could write all of your table creation and other DDL queries in this callback.  However, since SQLite supports the <kbd>IF EXISTS</kbd> syntax, I find it easier to run my schema setups immediately no matter if it&#8217;s the first time or not.</p>
<p>Before we run any queries, though, it would be helpful to have debugging functions for when our queries go pear-shaped:</p>
<pre class="javascript">function sqlFail(err) { alert("SQL failed: " + err.message); }
function txFail(err) { alert("TX failed: " + err.message); }</pre>
<p>Again, nothing tricky here: we&#8217;ll use these functions as callback error handler in future queries.  For illustrative purposes, we could also have a logger for when our queries succeed:</p>
<pre class="javascript">function sqlWin(tx, response) { console.log("SQL succeeded: " + response.rows.length + " rows."); }
function txWin(tx) { console.log("TX succeeded."); }</pre>
<p>A little trickier, but we&#8217;ll go into more detail on result sets in a bit.  On with the database setup:</p>
<pre class="javascript">db.transaction( function(tx) {
    var queryParams = [];
    tx.executeSql(
        "CREATE TABLE IF NOT EXISTS animals (id INT NOT NULL PRIMARY KEY AUTOINCREMENT, name VARCHAR(20) NOT NULL UNIQUE);",
        queryParams,
        sqlWin,
        sqlFail
    );
}, txFail, txWin);</pre>
<p>Again, I&#8217;m a little verbose and heavy with the variables for illustrative purposes&mdash;you&#8217;d use a literal <kbd>[]</kbd> instead of a <kbd>queryParams</kbd> local variable.  Inside of a <kbd>transaction</kbd> function, your queries are executed atomically and serially, but also <strong>asynchronously</strong> so be careful&mdash;hence all of the callback functions.  The <kbd>transaction</kbd> function even has optional failure/success callbacks of its own, but again be careful: they&#8217;re in the opposite order of the query functions.</p>
<p>If we insert a row, we can get back the ID of an autogenerated number:</p>
<pre class="javascript">db.transaction( function(tx) {
    var animalName = "Cheetah";
    tx.executeSql("INSERT INTO animals (name) VALUES (?);",
        [ animalName ],
        function(tx, results) {
            var animalId     = results.insertId;
            var animalsAdded = results.rowsAffected;
            // for this simple insert, we should only see 1 row
        },
        sqlFail
    );
});</pre>
<p>Using <kbd>SELECT</kbd> queries is just as easy:</p>
<pre class="javascript">db.readTransaction( function(tx) {
    var animalName = "%Lion%";
    tx.executeSql("SELECT id, name FROM animals WHERE (name LIKE ?);",
        [ animalName ],
        function(tx, results) {
            for(var i = 0; i &lt; results.rows.length; i++) {
                var row = results.rows.item(i);
                console.log("Animal[" + row.id + "] = " + row.name);
            } // for i
        },
        sqlFail
});</pre>
<p>Still nothing too tricky here.  Query parameters work just like you&#8217;d expect: replace the parameter with a question mark in the query, then pass it in the array afterward.  The callback here iterates over the records, each of which is a simple object with column names for keys, and prints them on the debugging console.</p>
<p>There&#8217;s not much more to it than that&mdash;it&#8217;s surprisingly easy for something designed by committee!</p>
]]></content:encoded>
			<wfw:commentRss>http://rickosborne.org/blog/2010/03/html5-in-browser-sql-database-storage/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Data Mining the 2010 Gate River Run 15K results</title>
		<link>http://rickosborne.org/blog/2010/03/data-mining-the-2010-gate-river-run-15k-results/</link>
		<comments>http://rickosborne.org/blog/2010/03/data-mining-the-2010-gate-river-run-15k-results/#comments</comments>
		<pubDate>Mon, 15 Mar 2010 18:12:26 +0000</pubDate>
		<dc:creator>Rick Osborne</dc:creator>
				<category><![CDATA[Fitness]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[data mining]]></category>
		<category><![CDATA[running]]></category>
		<guid isPermaLink="false">http://rickosborne.org/blog/?p=1198</guid>
		<description><![CDATA[Random facts learned by data mining the 2010 Gate River Run 15K results: Fastest Father-Son Team: Robert and Harrison Gordon, coming in with a 1:03:15 chip time. Fastest Father-Daughter Team: Greg Lawrence and Amy Eller, coming in with a 1:08:52 chip time. Fastest Husband-Wife Team: Scott and Laura Flint, coming in with a 1:15:02 chip [...]]]></description>
			<content:encoded><![CDATA[<p>Random facts learned by data mining the 2010 Gate River Run 15K results:</p>
<ul>
<li><strong>Fastest Father-Son Team</strong>: Robert and Harrison Gordon, coming in with a 1:03:15 chip time.</li>
<li><strong>Fastest Father-Daughter Team</strong>: Greg Lawrence and Amy Eller, coming in with a 1:08:52 chip time.</li>
<li><strong>Fastest Husband-Wife Team</strong>: Scott and Laura Flint, coming in with a 1:15:02 chip time.</li>
<li><strong>Best Spirit of Cooperation</strong>: Kara Mathias and Manny Gutierrez, a Gator and a &#8216;Nole, running the entire race together and finishing in just under 66 minutes.</li>
<li><strong>Most Supportive Running Group</strong>: Dominic, William, Tim, Jamie, and Brian from Ponte Vedra, FL, coming in at a chip time of 1:46:48.</li>
<li><strong>The Family That Runs Together Stays Together</strong>: The Gobold family of Jacksonville&mdash;Chris and Lesa, with children Jena and Tyler, and friends Christopher and Mary&mdash;staying together and finishing in 2:23:30.</li>
</ul>
<p>I am truly impressed how many husband and wife teams were seeded.  That&#8217;s absolutely awesome.  (And that they can match eachother&#8217;s pace the entire race!)</p>
<p>Despite an enormous number of Father-Son and Husband-Wife teams, Mother-Daughter teams seemed to be thin on the ground.  My best guess?  Jessica and Anna of Jacksonville, FL with a finish time of 1:18:21.  Finding them is harder, because the surnames may not be the same.  The age difference helps, but isn&#8217;t a guarantee.  (Woe unto me if I call out a mother-daughter pair only to be wrong!  And I&#8217;m nowhere near stupid enough to try Father-Daughter or Mother-Son pairs!)</p>
<p>Someone named Nicole Norris of Roswell, GA kept pace with my buddy and I throughout the entire race, crossing each split marker within 3-4 seconds of us each time.  Nicole: we were the guys in the red shirt and hat and yellow shirt (mustard, not the greenish of the official race shirts).  Sorry we didn&#8217;t say hello!</p>
]]></content:encoded>
			<wfw:commentRss>http://rickosborne.org/blog/2010/03/data-mining-the-2010-gate-river-run-15k-results/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

