<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-497787815177352569</atom:id><lastBuildDate>Wed, 01 Feb 2012 08:01:00 +0000</lastBuildDate><category>server setup and admin</category><category>performance and tuning</category><category>Database Deadpool</category><category>The fRiDaY File</category><category>announcement</category><category>housekeeping</category><category>Foxhound</category><category>SQL code</category><category>SQLA</category><category>Design</category><category>off topic</category><category>Dilbert</category><category>database migration</category><category>latest EBFs</category><category>testimonial</category><category>The Thursday Quote</category><title>SQL Anywhere</title><description>Breck Carter's unofficial blog about &lt;u&gt;SQL Anywhere®&lt;/u&gt;... yes, "SQL Anywhere" is an actual product, like MySQL, Oracle, SQL Server, ASE, only LOTS BETTER :)&lt;br&gt;SQL Anywhere® is a registered trademark of Sybase, Inc. and is used here (I hope) with permission.</description><link>http://sqlanywhere.blogspot.com/</link><managingEditor>noreply@blogger.com (Breck Carter)</managingEditor><generator>Blogger</generator><openSearch:totalResults>536</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/SqlAnywhere" /><feedburner:info uri="sqlanywhere" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-6258906393474358867</guid><pubDate>Wed, 01 Feb 2012 08:01:00 +0000</pubDate><atom:updated>2012-02-01T03:01:00.201-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><category domain="http://www.blogger.com/atom/ns#">Foxhound</category><title>Coding Good Comments: Explanations</title><description>&lt;p&gt;The code comes first: If there's a choice to be made between writing clear, understandable program code and writing comments to explain less-than-readable code, comments should take a back seat.&lt;br /&gt;
&lt;br /&gt;
The reason is simple: It's the code that determines what the program does, not the comments. Comments aren't even compiled, let alone executed. So it shouldn't come as a surprise that maintenance programmers concentrate on the code, often skipping over the comments altogether.&lt;br /&gt;
&lt;br /&gt;
Sadly, however, even readable, understandable program code tells only part of the story: It only answers the question, "How is this program accomplishing its tasks?" Very few executable statements directly address the question, "What is this program doing?", with the possible exception of a CALL statement where the procedure name has been carefully crafted to answer the "What?"&lt;br /&gt;
&lt;br /&gt;
There are no exceptions, however, when it comes to answering the questions, "Why is the program doing this thing?" or the much harder "Why isn't the program doing this other thing?"&lt;br /&gt;
&lt;br /&gt;
So, comments come second, but&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Explanations Are Important&lt;/h3&gt;&lt;br /&gt;
&lt;b&gt;Explanation: A concise comment describing the "What" and "Why" of a section of program code while avoiding the "How" which should be apparent from reading the code itself.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 1&lt;/h3&gt;Here is a short section of simple code (basically a SELECT FROM DUMMY) with several explanations, some good, some not so good:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------
      -- Perform the basic heartbeat or canarian query.
      -- This is done to calculate the heartbeat time as well check the connection.
      -- Note: The target database can be stopped and restarted, and the monitor should just keep trying to connect.
      -- Note: When Foxhound itself is is stopped and restarted, reconnection to target databases *should* be automatic.

      SET @sql = STRING ( 'SELECT dummy_col INTO @dummy_col FROM ', @proxy_owner, '.proxy_DUMMY' );

      SET @canarian_query_started_at  = CURRENT TIMESTAMP;
      SET @canarian_query_finished_at = @canarian_query_started_at;

      EXECUTE IMMEDIATE @sql;

      ROLLBACK; -- release locks, recover from possible failed proxy connection

      SET @connected_ok = 'Y';
      SET @canarian_query_finished_at = CURRENT TIMESTAMP;

      MESSAGE STRING ( 'DIAG ', CURRENT TIMESTAMP, ' ', CONNECTION_PROPERTY ( 'Number' ), 
         ' 204-4 Monitor ', @sampling_id, ': First heartbeat OK' ) TO CONSOLE DEBUG ONLY;

      -- Note: Because the proxy_DUMMY table is the last proxy object created, a 
      -- successful query against proxy_DUMMY may taken as proof that a connection exists
      -- and is usable. This is important because a previous connection attempt
      -- may be dropped (timed out) after it created some but not all necessary proxy objects.
      -- Such a partly-established connection must be treated as no connection at all, and
      -- that is guaranteed by creating proxy_DUMMY last.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
In the comment "This is done..." the phrase "as well as check the connection" provides extra information about the "What?" of this code, so that's OK as an explanation. &lt;br /&gt;
&lt;br /&gt;
The phrase "calculate the heartbeat time" is a bit redundant, however... not wrong, but not really necessary given the preceding section title "Perform the basic heartbeat or canarian query."&lt;br /&gt;
&lt;br /&gt;
The two one-line "Note:" comments are a bit other-worldly because they state true facts that have very little (nothing?) to do with nearby code. These score zero on the Comment Value Scale; not a positive score because they offer no real value, but not a negative score because at least they aren't wrong.&lt;br /&gt;
&lt;br /&gt;
The long "Note: Because the proxy_DUMMY table is ..." paragraph scores very high, however, because it explains a very important "Why?" for this otherwise trivial-looking section of code.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 2&lt;/h3&gt;This example shows two separate explanations that were added to the code at two separate times. &lt;br /&gt;
&lt;br /&gt;
The later paragraph "It is possible..." came first, chronologically speaking, to explain why the unusual step of calling GET_IDENTITY was performed instead of just letting the later INSERT assign the value... so far, so good, the original "Why?" is covered.&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------
-- Calculate candidate primary key for successful sample.

-- Note: The following discussion is moot, but @candidate_sample_set_number is still
--       assigned because later code refers to it.

-- It is possible for this event to take some time before inserting a row
-- in rroad_sample_set. During that time, it is also possible for sampling to be 
-- stopped, and a "sampling stopped" row to be inserted. The row inserted by this
-- event should have a lower primary key than the "sampling stopped" event, so it
-- cannot wait until the actual INSERT rroad_sample_set statement to calculate the
-- value of the autoincrement column sample_set_number. The following statement 
-- pre-assigns a value which will only be used for the INSERT of a "successful" 
-- sample set; if that INSERT is not performed because of some problem, there will
-- be a gap in the primary key sequence.

SET @candidate_sample_set_number = GET_IDENTITY ( 'rroad_sample_set' );
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
The second explanation, the previous "Note: ...", was added later to explain that GET_IDENTITY was being called through laziness rather than real need. This explanation isn't quite as thorough, but it does serve to prevent the new maintenance programmer from searching the code to find the reason GET_IDENTITY was called.&lt;br /&gt;
&lt;br /&gt;
In a perfect world, the call to GET_IDENTITY would have been removed as soon as it became unnecessary. In an imperfect world, it's easier to add the "Note:" when the call became moot... not just because it's less work to write the comment than change the code, but also because comments aren't compiled so the "Note:" doesn't have to be tested.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 3&lt;/h3&gt;The following example almost got left out because at first glance it looked like just another bad explanation: Who needs to be told that sa_locks needs DBA authority? &lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------
   -- Create proxy_sa_locks if necessary.
   -- This is only done after a full connection process.
   -- Note: DBA authority is needed to call sa_locks.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Well, with this application (&lt;a href="http://www.risingroad.com/foxhound/index.html" target="_blank"&gt;Foxhound&lt;/a&gt;) whether the user id has DBA authority or not controls on how much detail the user's going to get, and this comment explains to the maintenance programmer that information about row locks is going to be missing from that detail if the user doesn't have DBA authority.&lt;br /&gt;
&lt;br /&gt;
So, this "Note: DBA authority is needed to call sa_locks" looks like a "How?" but it's really a "What?", albeit on a grander scale: "What does the maintenance programmer need to know about this code?"&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 4&lt;/h3&gt;Here is a breathtakingly-verbose "Note:" that explains a very simple-looking IF NOT EXISTS RETURN control structure:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
-- Step 5 - Check to see if sampling has been turned off. 
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------

IF  @ok = 'Y'  
AND @connected_ok = 'Y' THEN 

   -- Note: This code is an attempt to prevent a successful sample from appearing between 
   --       successive "Sampling stopped at" and "Sampling stopped" lines in the Monitor display.
   --       This process may already be executing the code above this point when sampling is stopped by
   --       a different process, and the @sample_started_at used by this process is calculated in the
   --       next section... which is after the the "Sampling stopped at" point calculated by the other
   --       process but before the eventual "Sampling stopped" point which will be calculated by some
   --       (probably much later) process.
   -- 
   --       In the following example, the sample at Apr 22 5:12:21 PM should not have been recorded:
   --          Apr 22 5:13:35 PM   1m 14.2s    0s / .1s ...
   --          Apr 22 5:13:21 PM   1m 18.8s   -- Sampling stopped --  
   --          Apr 22 5:12:21 PM   0s    0s / 0s    1 ...
   --          Apr 22 5:12:02 PM   1m 24.2s   -- Sampling stopped at --  
   --          Apr 22 5:10:38 PM   1m 10.1s    .1s / .1s ...
   --
   --       In the following example, the sample at 5:11:23 PM should not have been recorded:
   --          5:11:32 PM   8.5s    .1s / 0s ...
   --          5:11:27 PM    ALL CLEAR - Alert # 1: Foxhound has been unable to gather samples for 1m or longer. 
   --          5:11:25 PM   4m 54.7s   -- Sampling stopped --  
   --          5:11:23 PM   0s    0s / .1s ...
   --          5:06:30 PM   0s   -- Sampling stopped at --  
   --          5:06:30 PM   39.6s   -- Foxhound stopped --  
   --          5:05:50 PM   0s   -- Foxhound stopped at --  

   IF NOT EXISTS ( SELECT *
                     FROM rroad_sampling_options
                    WHERE rroad_sampling_options.sampling_id                = @sampling_id
                      AND rroad_sampling_options.sampling_should_be_running = 'Y' ) THEN

      RETURN; -- do not record this sample

   END IF;
&lt;/pre&gt;&lt;br /&gt;
Here's the reason for the long explanation: The bug very hard to find, and the fix so simple that no future maintenance programmer, not even the origial author, would understand the "Why?" of the IF-NOT-EXISTS-RETURN code without some help.&lt;br /&gt;
&lt;br /&gt;
In fact, without the explanation, it's not clear that the code is even correct let alone necessary. Comments don't just save time, they can prevent mistakes (like removing a piece of code because it looks wrong.)&lt;br /&gt;
&lt;br /&gt;
In this case the comment itself wasn't that hard to write: the sample data was gathered as part of the diagnostic effort, and the explanation was worked out carefully and even written down as part of that same effort... it took very little extra effort to copy the explantion into the code as an aid to future developers.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;The Bottom Line&lt;/h3&gt;Explanations belong inside the code, not locked in the fading memory of the original author or lost inside some other document no developer will ever read. &lt;br /&gt;
&lt;br /&gt;
That is, if you think explanations are important. &lt;br /&gt;
&lt;br /&gt;
Folks who don't think explanations are important generally don't think readability's important either. That's why most code without comments is also unreadable... not because there are no comments, but because the author cares more about abstract measurements of "elegance" or believes that somehow readability contradicts correctness.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-6258906393474358867?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/u0YWCEG5NCU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/u0YWCEG5NCU/coding-good-comments-explanations.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/02/coding-good-comments-explanations.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-4976950081942050570</guid><pubDate>Mon, 30 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-30T03:01:01.160-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">latest EBFs</category><category domain="http://www.blogger.com/atom/ns#">announcement</category><title>Latest SQL Anywhere EBFs - 11.0.1 For (Almost) Everyone!</title><description>&lt;p&gt;The three asterisks "***" show what's new since the &lt;a href="http://sqlanywhere.blogspot.com/2012/01/latest-sql-anywhere-ebf-12013537-for.html" target="_blank"&gt;previous list&lt;/a&gt;. &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Only EBFs for the &lt;b&gt;latest fully-supported versions&lt;/b&gt; of SQL Anywhere are shown here: 10.0.1, 11.0.1 and 12.0.1. &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Note:&lt;/b&gt; Some earlier versions are also fully supported but they are not shown here; e.g., the 12.0.0.2758 EBF for Windows was recently released. For more information see &lt;a href="http://www.sybase.com/detail?id=1002288" target="_blank"&gt;SQL Anywhere Supported Platforms and Engineering Support Status&lt;/a&gt;.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Also:&lt;/b&gt; Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more &lt;b&gt;new&lt;/b&gt; EBFs released. For a complete list of everything available for download see &lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Downloads - EBFs / Maintenance&lt;/a&gt;.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Current builds for the most active platforms...&lt;/a&gt;&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX Itanium    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2745 EBF ***   24 Jan 2012 ***
                 10.0.1.3777 EBF       14 Oct 2008

IBM AIX          12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2745 EBF ***   26 Jan 2012 ***

Linux x86/x64    12.0.1.3519 EBF       20 Dec 2011
                 11.0.1.2745 EBF ***   24 Jan 2012 ***
                 10.0.1.4310 EBF       17 Jan 2012

Mac OS           12.0.1.3352 EBF       26 May 2011
                 11.0.1.2449 EBF       29 Jun 2010
                 10.0.1.4042 EBF       01 Oct 2010

Solaris SPARC    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2745 EBF ***   26 Jan 2012 ***
                 10.0.1.4310 EBF       17 Jan 2012

Solaris x64      12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2745 EBF ***   24 Jan 2012 ***
                 10.0.1.3870 EBF       01 Apr 2009

Windows x86/x64  12.0.1.3537 EBF       18 Jan 2012
                 11.0.1.2744 EBF ***   26 Jan 2012 ***
                 10.0.1.4310 EBF       12 Jan 2012
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;] [&lt;a href="http://response.sybase.com/forms/FujiBetaTrial" target="_blank"&gt;Register for the Fuji Beta here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;...and for the other platforms:&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX PA-RISC    10.0.1.3778 EBF                 16 Oct 2008

Linux Itanium    10.0.1.3777 EBF                 14 Oct 2008

NetWare          10.0.1.4036 EBF                 10 Mar 2010

Windows CE       10.0.1.4036 EBF                 09 Mar 2010

Windows Itanium  10.0.1 GA Upgrade from 10.0.0   26 Apr 2007
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-4976950081942050570?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/kMnM8nEtneo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/kMnM8nEtneo/latest-sql-anywhere-ebfs-1101-for.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/latest-sql-anywhere-ebfs-1101-for.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-7347092918165095626</guid><pubDate>Fri, 27 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-27T10:58:20.962-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><title>Coding Good Comments: Exhortations</title><description>&lt;p&gt;Another kind of comment inside program code is the &lt;b&gt;Exhortation: A warning or reminder for the maintenance programmer when making changes to code in the vicinity.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Exhortations may be the only kind of comment that deserves to be decorated with boxes, ALL-CAPS and/or exclamation! marks because they are the only kind of comment that may be more important than the surrounding program code... by definition all other comments are less important than the program code because&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;comments aren't compiled,&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;comments aren't tested,&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;comments aren't executed, so therefore&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;comments can easily be (and often are) wrong.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;Exhortations are a bit different: they offer blunt warnings, not subtle guidance or explanations. When an exhortation becomes out-of-date, and therefore wrong, it's usually easy to detect and easy to resolve by simply deleting the comment.&lt;br /&gt;
&lt;br /&gt;
When an exhortation is valid, however, it is often valuable to both newcomers (as a warning about a possible pitfall) and experienced programmers (as a reminder about that pitfall).&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 1&lt;/h3&gt;Here's an example from inside a SQL script that contains CREATE TABLE and other definitions for tables that are subject to the "Data Upgrade" process when an old copy of the application database is upgraded to a new version: &lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;-- ***********************************************************************
-- ***** EVERY CHANGE DOCUMENTED HERE MUST ALSO BE DOCUMENTED IN     *****
-- ***** 015c_rroad_data_upgrade_startup.sql EVEN IF IT IS           *****
-- ***** NOT SIGNIFICANT TO THE DATA UPGRADE PROCESS.                *****
-- ***********************************************************************
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
What it's saying is that every single schema change made in this module must be checked against code in the other module to see if anything has to be done over there as well. &lt;br /&gt;
&lt;br /&gt;
The "MUST ALSO BE DOCUMENTED" part of the exhortation is saying that a record of that checking must be kept in the other module; in this case, the modification history comments are stored in both locations as evidence the change wasn't forgotten.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 2&lt;/h3&gt;Redundant code is sometimes just easier to deal with than to eliminate with complex logic. Here's an example of an exhortation that warns of the existence of a debugging version of a tble; when the original base table is altered, the debugging version probably needs to be changed as well:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;   -- ********************************************************************************
   -- ***** WHEN MAKING CHANGES HERE, SEE ALSO THE TRACE_* VERSION OF THIS TABLE *****
   -- ********************************************************************************
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;h3&gt;Example 3&lt;/h3&gt;Here's an warning about NULL-versus-non-NULL values that was added to the code after several catastrophic mistakes were made; this warning may only benefit newcomers, but it doesn't hurt for experienced developers to be reminded as well:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;   -- *****************************************************************************************************
   -- ***** DO NOT CHANGE NULL / NOT NULL CONSTRAINTS WITHOUT CHECKING EVERYWHERE THE VALUES ARE USED *****
   -- *****************************************************************************************************
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
This exhortation appears twice in the code, and so does the one from Example 2: both at the beginning and end of the associated CREATE TABLE statement to reduce the chances it'll be missed.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 4&lt;/h3&gt;Here's an example involving the same business rule being implemented by separate UPDATE statements:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;         -- ***** CAUTION ****************************************************************************
         -- *****    When changing the UPDATE statement here, also check the similar UPDATE in   *****
         -- *****    204_rroad_monitor_sample.sql.                                               *****
         -- ******************************************************************************************
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
The business rule's the same, but the syntax and semantics of the UPDATE statements are different, and the logic required to implement a single common UPDATE was deemed not worth the effort, not when the changes are easy to make in two locations and an exhortation takes care of the "oops I missed that" problem.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 5&lt;/h3&gt;Here's an example of two alternate versions of the same table, only one of which exists at run time but both must be maintained... and most changes must be made to both versions.&lt;br /&gt;
&lt;br /&gt;
Once again, the exhortation appears twice inside each table because they're quite long; the ellipsis '...' represents many columns.&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;IF '{DEBUG_MESSAGES}' = 'ON' THEN

   -- Include all constraints to facilitate development testing.

   --------------------------------------------------------------------------------------------
   -- CAUTION: If any changes are made here, make the same changes to the other CREATE TABLE. 
   --------------------------------------------------------------------------------------------

   CREATE TABLE rroad_sampling_options (

      sampling_id                          UNSIGNED INTEGER NOT NULL DEFAULT AUTOINCREMENT,

      ...

      --------------------------------------------------------------------------------------------
      -- CAUTION: If any changes are made here, make the same changes to the other CREATE TABLE. 
      --------------------------------------------------------------------------------------------

      PRIMARY KEY ( sampling_id ),
      UNIQUE ( selected_tab, 
               selected_name ),
      CONSTRAINT "NOT ( sampling_should_be_running = Y AND timed_out = Y )" CHECK ( NOT ( sampling_should_be_running = 'Y' AND timed_out = 'Y' ) ),
      CONSTRAINT "last_sample_started_at &lt;= last_sample_finished_at" CHECK ( last_sample_started_at &lt;= last_sample_finished_at ),
      CONSTRAINT "last_canarian_query_started_at &lt;= last_canarian_query_finished_at" CHECK ( last_canarian_query_started_at &lt;= last_canarian_query_finished_at ) );

ELSE

   -- Omit some constraints to improve performance.
   -- Not all constraints are omitted, just the ones that might affect performance.

   --------------------------------------------------------------------------------------------
   -- CAUTION: If any changes are made here, make the same changes to the other CREATE TABLE. 
   --------------------------------------------------------------------------------------------

   CREATE TABLE rroad_sampling_options (

      sampling_id                          UNSIGNED INTEGER NOT NULL DEFAULT AUTOINCREMENT,
      ...

      --------------------------------------------------------------------------------------------
      -- CAUTION: If any changes are made here, make the same changes to the other CREATE TABLE. 
      --------------------------------------------------------------------------------------------

      PRIMARY KEY ( sampling_id ),
      UNIQUE ( selected_tab, 
               selected_name ) );

END IF;
&lt;/pre&gt;

&lt;h3&gt;The Bottom Line&lt;/h3&gt;Exhortations should be blunt, simple and to the point, and if at all possible they should be positive: They should tell the developer what to do, not what to avoid.

&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-7347092918165095626?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/jOm4oJAjrFU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/jOm4oJAjrFU/coding-good-comments-exhortations.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/coding-good-comments-exhortations.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-894270767168455823</guid><pubDate>Wed, 25 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-26T12:25:35.644-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><title>Coding Good Comments: Section Titles</title><description>&lt;p&gt;There are many kinds of comments inside program code, one of them being &lt;b&gt;Section Titles: Comments that introduce the reader to a block of code with the primary purpose being to answer the question "Is this the code we're looking for?"&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Folks who hate comments might say: "Different sections of code should appear in separate functions or procedures, with the section title becoming the procedure name." &lt;br /&gt;
&lt;br /&gt;
In the real world, however, &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;not every section becomes a procedure,&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;section title comments tend to be longer and more descriptive than procedure names, and&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;applications with extremely high ratios of call-return-interfaces-to-lines-of-worker-code may become hard to understand and follow.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;In other words, every real-world program can use section title comments, and this article is about writing good ones.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 1&lt;/h3&gt;Here's an example of a not-very-good section title; it's not good because it is vague, telling us only that the section involves sample sets rather than some other commonly-used data objects used in the application:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------------------
-- Perform some standard sample set updates as required.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
That section title is not good because the section itself violates &lt;a href="http://en.wikipedia.org/wiki/Cohesion_(computer_science)#Types_of_cohesion" target="_blank"&gt;the rule for high cohesion&lt;/a&gt; as the following nested subsection titles show:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;   ------------------------------------------------------------------------------------------------------------------
   -- Update 1: Fix rroad_sampling_options.rroad_monitor_sample_loop_connection_number if necessary.

   ------------------------------------------------------------------------------------------------------------------
   -- Update 2: Update the last rroad_sample_set.sample_finished_at if necessary.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Each of the subsection titles is pretty good, but neither has much to do with the other except the fact they both mention sample sets... hence the vague section title. The two subsections appear together because they can share one FOR loop; perhaps this is an example of coincidental cohesion (the worst), perhaps it is communicational cohesion (the second best, when parts of a module are grouped because they operate on the same data), or somewhere in between, but it is certainly not functional cohesion (the best): when parts of a module are grouped because they all contribute to a single well-defined task of the module.&lt;br /&gt;
&lt;br /&gt;
In this case, the vague outer section title is as good as it's going to get without changes to the actual code... but maybe it's not so bad after all: the vagueness itself can be taken as a warning that "coincidental cohesion lurks within".&lt;br /&gt;
&lt;br /&gt;
In other words, the code ain't perfect so neither are the section titles.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 2&lt;/h3&gt;Here's a section title that's pretty specific, but not exactly helpful:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------------------
-- Loop through each database that is trying to connect past the connection timeout interval.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Instead of describing what the code's doing, it's concentrating on the how: "loop through each database".  The first line of code is a FOR loop so nobody needs to know the "how", it's the "what" of the section that should be described in the title.&lt;br /&gt;
&lt;br /&gt;
Here's how the section title was revised:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------------------
-- Stop sampling for connection timeouts.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Much better: "stop sampling" is a big deal in the world of database monitoring, and it's the whole reason this code section exists yet it wasn't even mentioned in the original title.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Example 3&lt;/h3&gt;Here's a section title which confuses the "what" and "how" for a long section of code:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------------------------------
-- Process Alert #1: Loop through each database that...
--     should be sampled,
--     or has timed out, 
--     and has not had a recent successful sample.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
The word "process" is unforgivably vague since it could mean "issue", "clear" or "cancel", or even "edit", or some combination of all of them; in this case the action is "issue". Also, "Alert #1" is probably appropriate only for someone already familiar with Foxhound, someone who knows that Alert #1 is "Database Unresponsive".&lt;br /&gt;
&lt;br /&gt;
Here's the revision:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------------------------------
-- Issue Alert #1: Database Unresponsive for each database that...
--     should be sampled,
--     or has timed out, 
--     and has not had a recent successful sample.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;h3&gt;Example 4&lt;/h3&gt;Here is yet another vague "Loop through" section title on a FOR loop exhibiting lower-than-functional cohesion, together with the subsection titles:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------------------
-- Loop through each database that...
--     should be sampled but isn't,  
--     has timed out and should be retried, or
--     still has user_request = 'Stop Sampling' even though sampling has stopped.

   ------------------------------------------------------------------------
   -- Exit immediately if Foxhound is shutting down.

   ------------------------------------------------------------------------
   -- Get the Sample Schedule settings for this moment in time.
   -- This information will be used in later sections.

   ------------------------------------------------------------------------
   -- Start sampling again after sample loop connection number was dropped.

   ------------------------------------------------------------------------
   -- Start sampling for timeout retry.

   ------------------------------------------------------------------------
   -- Check and reset user_request if sampling has already been stopped.

   ------------------------------------------------------------------------
   -- Stop sampling if required to by the sample schedule.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
In this case the associated code was changed and enhanced to provide new features, and the section and subsection titles were changed as well. &lt;br /&gt;
&lt;br /&gt;
The biggest change, however, was to the outer section title to make it clear that the subsections exhibit lower-than-functional cohesion:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;------------------------------------------------------------------------
-- Start or stop sample sessions as required.

   ------------------------------------------------------------------------
   -- Exit immediately if Foxhound is shutting down.

   ------------------------------------------------------------------------
   -- Get the Sample Schedule settings for this moment in time.
   -- This information will be used in later sections.

   ------------------------------------------------------------------------
   -- Start sampling each database that should be sampled but isn't.

   ------------------------------------------------------------------------
   -- Start sampling if the sample schedule requires it.

   ------------------------------------------------------------------------
   -- Start sampling for timeout retry.

   ------------------------------------------------------------------------
   -- Check and reset user_request and schedule_request if sampling has already been stopped.

   ------------------------------------------------------------------------
   -- Stop sampling if required to by the sample schedule.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
Also, the outer section title was stripped of its point-form overview of subsection descriptions: sometimes repetition helps but not here; the subsection titles are all that's needed.&lt;br /&gt;
&lt;br /&gt;
Note that one of the lines above isn't really a section title, it's a warning to the reader:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;-- This information will be used in later sections.
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;h3&gt;The Bottom Line&lt;/h3&gt;The &lt;b&gt;Number One Reason&lt;/b&gt;, perhaps the only reason for Section Title Comments is to answer the question: &lt;b&gt;"Is this the code we're looking for?"&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-894270767168455823?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/PSJfb8oE_mE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/PSJfb8oE_mE/coding-good-comments-section-titles.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/coding-good-comments-section-titles.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-4525503173133933783</guid><pubDate>Mon, 23 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-23T03:01:01.850-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">latest EBFs</category><category domain="http://www.blogger.com/atom/ns#">announcement</category><title>Latest SQL Anywhere EBF - 12.0.1.3537 for Windows</title><description>&lt;p&gt;The three asterisks "***" show what's new since the &lt;a href="http://sqlanywhere.blogspot.com/2012/01/latest-sql-anywhere-ebfs-10014310-for.html" target="_blank"&gt;previous list&lt;/a&gt;. &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Only EBFs for the &lt;b&gt;latest fully-supported versions&lt;/b&gt; of SQL Anywhere are shown here: 10.0.1, 11.0.1 and 12.0.1. &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Note:&lt;/b&gt; Some earlier versions are also fully supported but they are not shown here; e.g., the 12.0.0.2758 EBF for Windows was recently released. For more information see &lt;a href="http://www.sybase.com/detail?id=1002288" target="_blank"&gt;SQL Anywhere Supported Platforms and Engineering Support Status&lt;/a&gt;.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Also:&lt;/b&gt; Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more &lt;b&gt;new&lt;/b&gt; EBFs released. For a complete list of everything available for download see &lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Downloads - EBFs / Maintenance&lt;/a&gt;.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Current builds for the most active platforms...&lt;/a&gt;&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX Itanium    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3777 EBF       14 Oct 2008

IBM AIX          12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011

Linux x86/x64    12.0.1.3519 EBF       20 Dec 2011
                 11.0.1.2724 EBF       14 Dec 2011
                 10.0.1.4310 EBF       17 Jan 2012

Mac OS           12.0.1.3352 EBF       26 May 2011
                 11.0.1.2449 EBF       29 Jun 2010
                 10.0.1.4042 EBF       01 Oct 2010

Solaris SPARC    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.4310 EBF       17 Jan 2012

Solaris x64      12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3870 EBF       01 Apr 2009

Windows x86/x64  12.0.1.3537 EBF ***   18 Jan 2012 ***
                 11.0.1.2724 EBF       20 Dec 2011
                 10.0.1.4310 EBF       12 Jan 2012
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;] [&lt;a href="http://response.sybase.com/forms/FujiBetaTrial" target="_blank"&gt;Register for the Fuji Beta here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;...and for the other platforms:&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX PA-RISC    10.0.1.3778 EBF                 16 Oct 2008

Linux Itanium    10.0.1.3777 EBF                 14 Oct 2008

NetWare          10.0.1.4036 EBF                 10 Mar 2010

Windows CE       10.0.1.4036 EBF                 09 Mar 2010

Windows Itanium  10.0.1 GA Upgrade from 10.0.0   26 Apr 2007
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-4525503173133933783?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/ukQar0hvo1A" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/ukQar0hvo1A/latest-sql-anywhere-ebf-12013537-for.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/latest-sql-anywhere-ebf-12013537-for.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-8692169423927265566</guid><pubDate>Fri, 20 Jan 2012 20:01:00 +0000</pubDate><atom:updated>2012-01-20T15:01:00.972-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">The fRiDaY File</category><category domain="http://www.blogger.com/atom/ns#">Dilbert</category><category domain="http://www.blogger.com/atom/ns#">Foxhound</category><title>The fRiDaY File - The Best Kind Of Error</title><description>Here's the story: The next version of Foxhound is getting a "Sample Schedule" feature whereby the database monitor sampling process can be turned on and off at different times of the day. &lt;br /&gt;
&lt;br /&gt;
To keep it simple, the user input requires a "Y" for each 15-minute interval that samples are to be gathered, with "not Y" (like a dot) for other intervals.&lt;br /&gt;
&lt;br /&gt;
When it came time to start testing (as it did on Monday), setting the schedule on and off every 15 minutes made it easy to see if the transitions were working:&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://1.bp.blogspot.com/-o4MQt_tU7c8/Txb7hoya_CI/AAAAAAAABh4/3wypCLt7ST0/s1600/Sample%2BSchedule%2B900_263.jpg" width="900" height="263" title="Sample Schedule"&gt;&lt;br /&gt;
&lt;br /&gt;
So far so good... and of course there were bugs, lots of fixes, and testing started again on Wednesday.&lt;br /&gt;
&lt;br /&gt;
But then, NOTHING worked... changing the setting to and from "Y" for the current 15-minute interval had NO EFFECT on the sampling process!&lt;br /&gt;
&lt;br /&gt;
Arrrgh! After all that work, it had gotten WORSE!&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Well, actually, no it hadn't...&lt;/h3&gt;&lt;br /&gt;
...because [snork] changing the settings for Monday has no effect on testing done on Wednesday! &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;User Errors Are The Best Kind!&lt;/h3&gt;&lt;br /&gt;
&lt;a href="http://dilbert.com/strips/comic/2006-06-05/" title="Dilbert.com" target="_blank"&gt;&lt;img src="http://dilbert.com/dyn/str_strip/000000000/00000000/0000000/000000/00000/1000/200/1252/1252.strip.gif" border="0" alt="Dilbert.com" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-8692169423927265566?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/JgYV4LiQ_CI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/JgYV4LiQ_CI/friday-file-best-kind-of-error.html</link><author>noreply@blogger.com (Breck Carter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-o4MQt_tU7c8/Txb7hoya_CI/AAAAAAAABh4/3wypCLt7ST0/s72-c/Sample%2BSchedule%2B900_263.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/friday-file-best-kind-of-error.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-3255282959006417297</guid><pubDate>Fri, 20 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-20T03:01:02.839-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">latest EBFs</category><category domain="http://www.blogger.com/atom/ns#">announcement</category><title>Latest SQL Anywhere EBFs - 10.0.1.4310 For Linux and SPARC</title><description>&lt;p&gt;The three asterisks "***" show what's new since the &lt;a href="http://sqlanywhere.blogspot.com/2012/01/latest-sql-anywhere-ebf-10014310-for.html" target="_blank"&gt;previous list&lt;/a&gt;. &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Only EBFs for the &lt;b&gt;latest fully-supported versions&lt;/b&gt; of SQL Anywhere are shown here: 10.0.1, 11.0.1 and 12.0.1. &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Note:&lt;/b&gt; Some earlier versions are also fully supported but they are not shown here; e.g., the 12.0.0.2758 EBF for Windows was recently released. For more information see &lt;a href="http://www.sybase.com/detail?id=1002288" target="_blank"&gt;SQL Anywhere Supported Platforms and Engineering Support Status&lt;/a&gt;.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Also:&lt;/b&gt; Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more &lt;b&gt;new&lt;/b&gt; EBFs released. For a complete list of everything available for download see &lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Downloads - EBFs / Maintenance&lt;/a&gt;.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Current builds for the most active platforms...&lt;/a&gt;&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX Itanium    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3777 EBF       14 Oct 2008

IBM AIX          12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011

Linux x86/x64    12.0.1.3519 EBF       20 Dec 2011
                 11.0.1.2724 EBF       14 Dec 2011
                 10.0.1.4310 EBF ***   17 Jan 2012 ***

Mac OS           12.0.1.3352 EBF       26 May 2011
                 11.0.1.2449 EBF       29 Jun 2010
                 10.0.1.4042 EBF       01 Oct 2010

Solaris SPARC    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.4310 EBF ***   17 Jan 2012 ***

Solaris x64      12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3870 EBF       01 Apr 2009

Windows x86/x64  12.0.1.3519 EBF       20 Dec 2011
                 11.0.1.2724 EBF       20 Dec 2011
                 10.0.1.4310 EBF       12 Jan 2012
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;] [&lt;a href="http://response.sybase.com/forms/FujiBetaTrial" target="_blank"&gt;Register for the Fuji Beta here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;...and for the other platforms:&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX PA-RISC    10.0.1.3778 EBF                 16 Oct 2008

Linux Itanium    10.0.1.3777 EBF                 14 Oct 2008

NetWare          10.0.1.4036 EBF                 10 Mar 2010

Windows CE       10.0.1.4036 EBF                 09 Mar 2010

Windows Itanium  10.0.1 GA Upgrade from 10.0.0   26 Apr 2007
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-3255282959006417297?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/4t9bF4xD6WE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/4t9bF4xD6WE/latest-sql-anywhere-ebfs-10014310-for.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/latest-sql-anywhere-ebfs-10014310-for.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-5070630261614465101</guid><pubDate>Wed, 18 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-18T03:01:02.524-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">testimonial</category><category domain="http://www.blogger.com/atom/ns#">Dilbert</category><title>Develop, Test, Release, ...</title><description>No amount of slick propaganda can rearrange the basic software development life cycle:&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;Develop, Test, Release, Repeat&lt;hr&gt;&lt;/blockquote&gt;No one can put Release before Develop, not unless they have a time machine. It is possible to put Release before Test but that is usually so unsuccessful that an impartial observer might say the Release never happened.&lt;br /&gt;
&lt;br /&gt;
If the same staff are involved in both Develop and Test (as they often are, and perhaps should be) then a big problem can arise when the cycle reaches Repeat:&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;It's hard to go back to development when you've been testing for release.&lt;hr&gt;&lt;/blockquote&gt;During final testing the code is usually frozen and everyone is risk-averse. The only changes allowed are those required to fix problems, and not all problems are fixed... no one wants to make any changes that might break something else. &lt;br /&gt;
&lt;br /&gt;
After the Big Release Day, it's time to Repeat which means go back to Development. For management, that's easy: it's all just one long schedule to them, just one day later, and there's probably been some slippage which means everyone's already late for the next Release.&lt;br /&gt;
&lt;br /&gt;
For developers, it's not so easy... especially if they just came off the death march called Test. They're still risk-averse, and Development requires risks to be embraced: Concentrate on the enhancements without obsessing about the effect they might have on the rest of the program. They're also tired, and Development requires enthusiasm.&lt;br /&gt;
&lt;br /&gt;
What the developers need, and often do not get, is an extra step to decompress and rejuvenate:&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;Develop, Test, Release, Relax, Repeat&lt;hr&gt;&lt;/blockquote&gt;A word to the wise for management: If developers don't get time to Relax, they'll take it anyway... perhaps not consciously, but through bad morale, lost productivity and extra sick time. And it might cost more in the long run.&lt;br /&gt;
&lt;br /&gt;
Or... you could try rewards, see if that works...&lt;br /&gt;
&lt;a href="http://dilbert.com/strips/comic/1996-07-18/" title="Dilbert.com" target="_blank"&gt;&lt;img src="http://dilbert.com/dyn/str_strip/000000000/00000000/0000000/000000/10000/8000/300/18330/18330.strip.gif" border="0" alt="Dilbert.com" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-5070630261614465101?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/jAfhzotr02A" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/jAfhzotr02A/develop-test-release.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>2</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/develop-test-release.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-2945402122246341808</guid><pubDate>Mon, 16 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-16T03:01:00.844-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><title>Let's play Gotcha! 2012 Edition, Round 1</title><description>&lt;p&gt;Welcome to the first round of the 2012 Edition of "Gotcha!", the self-testing quiz game for SQL Anywhere developers.&lt;br /&gt;
&lt;br /&gt;
Let's get started right away...&lt;br /&gt;
&lt;h3&gt;You know all about DEFAULT AUTOINCREMENT, right?&lt;/h3&gt;Here's a warm-up question: &lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;What does t contain after the following INSERT?&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t ( auto UNSIGNED BIGINT NOT NULL DEFAULT AUTOINCREMENT );

INSERT t VALUES ( DEFAULT ); 
&lt;/pre&gt;&lt;hr&gt;&lt;/blockquote&gt;Answer: It contains 1! (you knew that, right?)&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT * FROM t ORDER BY auto;

auto
1
&lt;/pre&gt;Here's another warm-up question:&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;SQL Anywhere 12 stores the most recent DEFAULT AUTOINCREMENT value for each column in SYSTABCOL.max_identity.&lt;br /&gt;
&lt;br /&gt;
What is that value for t.auto?&lt;hr&gt;&lt;/blockquote&gt;Answer: It's 1 again!&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CHECKPOINT;
SELECT max_identity FROM SYSTABCOL WHERE column_name = 'auto';

max_identity
1
&lt;/pre&gt;Note: The CHECKPOINT is sometimes required to force SQL Anywhere to update SYSTABCOL.max_identity. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;The rules for Gotcha!&lt;/h3&gt;You will be asked three skill-testing questions involving SQL Anywhere, and you must answer all three correctly to move on to the next round.&lt;br /&gt;
&lt;br /&gt;
Two out of three right answers don't count, you have to get them all.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;No computers!&lt;/h3&gt;You have to answer all the questions without looking anything up, and without running any actual tests. This is a test of your SQL Anywhere knowledge, not your prowess at Googling the docs.&lt;br /&gt;
&lt;br /&gt;
Here we go...&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;h3&gt;Question One&lt;/h3&gt;&lt;br /&gt;
What does t1 contain after this code runs?&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t1 ( t1_auto UNSIGNED BIGINT NOT NULL DEFAULT AUTOINCREMENT );

INSERT t1 VALUES ( 9223372036854775806 );
INSERT t1 VALUES ( DEFAULT );
&lt;/pre&gt;Listen to this while working on your answer, then scroll down to see if you got it right...&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/vXGhvoekY44" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Here's the answer: While 9,223,372,036,854,775,806 is a pretty big number, the UNSIGNED BIGINT column in t1 can hold more than twice as much (18,446,744,073,709,551,614) so t1 looks like this:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT * FROM t1 ORDER BY t1_auto;

t1_auto
9223372036854775806
9223372036854775807
&lt;/pre&gt;Congratulations!&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;h3&gt;Question Two&lt;/h3&gt;&lt;br /&gt;
What does this code do?&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t2 ( t2_auto UNSIGNED BIGINT NOT NULL DEFAULT AUTOINCREMENT );

INSERT t2 VALUES ( 9223372036854775806 );
INSERT t2 VALUES ( DEFAULT );
INSERT t2 VALUES ( DEFAULT );
&lt;/pre&gt;Listen to this while working on your answer, then scroll down to see if you got it right...&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/vXGhvoekY44" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Careful now!&lt;/h3&gt;The 2012 Edition of Gotcha! is more difficult than it was last year!&lt;br /&gt;
&lt;br /&gt;
Here are some clues:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Table t2 looks exactly the same as t1, only the names have changed.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;The t2_auto column is still UNSIGNED BIGINT, and it will still hold values up to 18,446,744,073,709,551,614, and&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;that's wayyyyy bigger than 9,223,372,036,854,775,806.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/vXGhvoekY44" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Here's the answer: The third INSERT crashes!&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;Could not execute statement.
Column 't2_auto' in table 't2' cannot be NULL
SQLCODE=-195, ODBC 3 State="23000"
Line 34, column 1
&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Whoa!&lt;/h3&gt;We haven't even reached the Gotcha! question yet, what's going on? Inserting NULL is what DEFAULT AUTOINCREMENT tries to do &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/create-table-statement.html" target="_blank"&gt;when it runs out of numbers&lt;/a&gt;:&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;When the next value to be generated exceeds the maximum value that can be stored in the column to which the AUTOINCREMENT is assigned, NULL is returned. If the column has been declared to not allow NULLs, as is true for primary key columns, a SQL error is generated.&lt;br /&gt;
&lt;hr&gt;&lt;/blockquote&gt;Here's the thing, though: UNSIGNED BIGINT works as expected, and so does the following code... &lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t2 ( t2_auto UNSIGNED BIGINT NOT NULL DEFAULT AUTOINCREMENT );

INSERT t2 VALUES ( 9223372036854775806 );
INSERT t2 VALUES ( DEFAULT );
INSERT t2 VALUES ( 9223372036854775808 );

SELECT * FROM t2 ORDER BY t2_auto;

t2_auto
9223372036854775806
9223372036854775807
9223372036854775808
&lt;/pre&gt;...so the problem must be with DEFAULT AUTOINCREMENT.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Yes, indeed...&lt;/h3&gt;...the underlying cause is that SYSTABCOL.max_identity is defined as BIGINT so it can't hold any value larger than 9,223,372,036,854,775,807. Never mind that DEFAULT AUTOINCREMENT can't be used to generate negative values, signed BIGINT was chosen as the data type.&lt;br /&gt;
&lt;br /&gt;
Never mind that the Help says &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/create-table-statement.html" target="_blank"&gt;any old integer will do&lt;/a&gt;, which, presumably, includes UNSIGNED BIGINT: &lt;i&gt;"When using AUTOINCREMENT, the column must be one of the integer data types, or an exact numeric type."&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;h3&gt;Question Three&lt;/h3&gt;&lt;br /&gt;
What does this code do?&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t3 ( t3_auto UNSIGNED BIGINT NOT NULL DEFAULT AUTOINCREMENT );

INSERT t3 VALUES ( DEFAULT );
INSERT t3 VALUES ( DEFAULT );
INSERT t3 VALUES ( 9223372036854775808 );
INSERT t3 VALUES ( DEFAULT );
&lt;/pre&gt;OK, maybe you're nervous now, maybe you didn't get Question 2 right, maybe you need a moment to relax...&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/vXGhvoekY44" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Question 3 is really asking about what the last INSERT is going to do, the first three work just fine:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t3 ( t3_auto UNSIGNED BIGINT NOT NULL DEFAULT AUTOINCREMENT );

INSERT t3 VALUES ( DEFAULT );
INSERT t3 VALUES ( DEFAULT );
INSERT t3 VALUES ( 9223372036854775808 );

SELECT * FROM t3 ORDER BY t3_auto;

t3_auto
1
2
9223372036854775808
&lt;/pre&gt;So what is the next INSERT t3 VALUES ( DEFAULT ) going to do?&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/vXGhvoekY44" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Is it going to insert a row with t3_auto = 3?&lt;br /&gt;
&lt;br /&gt;
Wouldn't that make sense? That's what DEFAULT GLOBAL AUTOINCREMENT does, isn't it? If you explicitly insert a value outside the partition, the next DEFAULT AUTOINCREMENT value is calculated &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbusage/intda.html" target="_blank"&gt;as if the explicit INSERT never happened&lt;/a&gt;: &lt;i&gt;"Default column values are not affected by values in the column outside the current partition"&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/vXGhvoekY44" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
So... what's the answer? &lt;br /&gt;
&lt;br /&gt;
It's going to insert 3, isn't it?&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/vXGhvoekY44" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Gotcha!&lt;/h3&gt;No, it doesn't insert 3, it crashes just like before:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t3 ( t3_auto UNSIGNED BIGINT NOT NULL DEFAULT AUTOINCREMENT );

INSERT t3 VALUES ( DEFAULT );
INSERT t3 VALUES ( DEFAULT );
INSERT t3 VALUES ( 9223372036854775808 );
INSERT t3 VALUES ( DEFAULT );

Could not execute statement.
Column 't3_auto' in table 't3' cannot be NULL
SQLCODE=-195, ODBC 3 State="23000"
Line 36, column 1
&lt;/pre&gt;The reason is that the third INSERT, the one that explicitly inserts 9223372036854775808, sets SYSTABCOL.max_identity to this value:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT max_identity FROM SYSTABCOL WHERE column_name = 't3_auto';

max_identity
9223372036854775807
&lt;/pre&gt;That's NOT the last value that was inserted into t3, it can't store that because it wouldn't fit in a BIGINT. No, it stores &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/bigint.html" target="_blank"&gt;the maximum possible positive BIGINT value&lt;/a&gt;, and that, folks, is the point of this whole game:&lt;br /&gt;
&lt;h3&gt;DEFAULT AUTOINCREMENT is limited to BIGINTs&lt;/h3&gt;&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-2945402122246341808?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/roFmN_sqKaU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/roFmN_sqKaU/lets-play-gotcha-2012-edition-round-1.html</link><author>noreply@blogger.com (Breck Carter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://img.youtube.com/vi/vXGhvoekY44/default.jpg" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/lets-play-gotcha-2012-edition-round-1.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-6579723378472834546</guid><pubDate>Fri, 13 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-13T08:58:24.142-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">latest EBFs</category><category domain="http://www.blogger.com/atom/ns#">announcement</category><title>Latest SQL Anywhere EBF - 10.0.1.4310 For Windows</title><description>&lt;p&gt;The three asterisks "***" show what's new since the &lt;a href="http://sqlanywhere.blogspot.com/2011/12/latest-sql-anywhere-ebfs-santa-claus.html" target="_blank"&gt;previous list&lt;/a&gt;. &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Only EBFs for the &lt;b&gt;latest fully-supported versions&lt;/b&gt; of SQL Anywhere are shown here: 10.0.1, 11.0.1 and 12.0.1. &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Note:&lt;/b&gt; Some earlier versions are also fully supported but they are not shown here; e.g., the 12.0.0.2758 EBF for Windows was just released. For more information see &lt;a href="http://www.sybase.com/detail?id=1002288" target="_blank"&gt;SQL Anywhere Supported Platforms and Engineering Support Status&lt;/a&gt;.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Also:&lt;/b&gt; Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more &lt;b&gt;new&lt;/b&gt; EBFs released. For a complete list of everything available for download see &lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Downloads - EBFs / Maintenance&lt;/a&gt;.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Current builds for the most active platforms...&lt;/a&gt;&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX Itanium    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3777 EBF       14 Oct 2008

IBM AIX          12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011

Linux x86/x64    12.0.1.3519 EBF       20 Dec 2011
                 11.0.1.2724 EBF       14 Dec 2011
                 10.0.1.4239 EBF       17 Jun 2011

Mac OS           12.0.1.3352 EBF       26 May 2011
                 11.0.1.2449 EBF       29 Jun 2010
                 10.0.1.4042 EBF       01 Oct 2010

Solaris SPARC    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.4239 EBF       17 Jun 2011

Solaris x64      12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3870 EBF       01 Apr 2009

Windows x86/x64  12.0.1.3519 EBF       20 Dec 2011
                 11.0.1.2724 EBF       20 Dec 2011
                 10.0.1.4310 EBF ***   12 Jan 2012 ***
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;] [&lt;a href="http://response.sybase.com/forms/FujiBetaTrial" target="_blank"&gt;Register for the Fuji Beta here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;...and for the other platforms:&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX PA-RISC    10.0.1.3778 EBF                 16 Oct 2008

Linux Itanium    10.0.1.3777 EBF                 14 Oct 2008

NetWare          10.0.1.4036 EBF                 10 Mar 2010

Windows CE       10.0.1.4036 EBF                 09 Mar 2010

Windows Itanium  10.0.1 GA Upgrade from 10.0.0   26 Apr 2007
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-6579723378472834546?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/JUkaND0S1k8" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/JUkaND0S1k8/latest-sql-anywhere-ebf-10014310-for.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/latest-sql-anywhere-ebf-10014310-for.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-8491806679222762845</guid><pubDate>Wed, 11 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-11T08:54:28.418-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><title>Coding Guidelines</title><description>&lt;p&gt;If you can't get past the paywall to this article&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;&lt;a href="http://cacm.acm.org/magazines/2011/12/142527-coding-guidelines-finding-the-art-in-the-science/fulltext" target="_blank"&gt;Coding Guidelines: Finding the Art in the Science&lt;/a&gt;&lt;br /&gt;
&lt;blockquote&gt;Robert Green, Henry Ledgard, Communications of the ACM, December 2011&lt;/blockquote&gt;&lt;hr&gt;&lt;/blockquote&gt;then try this version:&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;&lt;a href="http://www.eng.utoledo.edu/eecs/faculty_web/hledgard/softe/upload/index.php?action=downloadfile&amp;filename=Professional%20Guidelines-2011.doc&amp;directory=&amp;PHPSESSID=a387d53ae4b46dce3c36bea008fad1fa" target="_blank"&gt;Professional coding guidelines&lt;/a&gt;&lt;blockquote&gt;Ledgard, H., 2011 Unpublished report, University of Toledo&lt;/blockquote&gt;&lt;hr&gt;&lt;/blockquote&gt;There's lots of good stuff, most of which I agree with, like making names pronounceable and using vertical alignment.&lt;br /&gt;
&lt;br /&gt;
The comments about comments, however, seem a bit shallow, especially in the CACM article: examples of poorly-written comments in poorly-written code are used to conclude that comments should be avoided. The opposite is true: well-written comments are enormously helpful in explaining the "what" and "why" of a piece of code; no matter how readable the code is it only presents the "how". &lt;br /&gt;
&lt;br /&gt;
Comments have other uses, sometimes historical: documenting as-yet-unsolved bugs, explaining why some unexpected or unusual change was made, even explaining why some change was &lt;b&gt;not&lt;/b&gt; made. &lt;br /&gt;
&lt;br /&gt;
To put this information anywhere other than in the code itself is to guarantee future maintenance programmers will never read it.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-8491806679222762845?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/Wk_T9nXbfwM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/Wk_T9nXbfwM/coding-guidelines.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>2</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/coding-guidelines.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-289009856650763898</guid><pubDate>Mon, 09 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-09T03:01:02.377-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><title>You Can't Do That In SQL!</title><description>&lt;p&gt;Windows 7 Search sucks!&lt;br /&gt;
&lt;h3&gt;No, seriously...&lt;/h3&gt;...the Search feature in Windows 7 really sucks.&lt;br /&gt;
&lt;br /&gt;
At least, it sucks for developers who work with ASCII text files and frequently want to search their code for some fixed string of text, like&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;&lt;i&gt;Show me all the SQL scripts that contain the string literal 'Sampling Stopped'.&lt;/i&gt;&lt;br /&gt;
&lt;hr&gt;&lt;/blockquote&gt;That's how the Windows XP Search used to work: WYTIWILF... what you typed is what it looked for.&lt;br /&gt;
&lt;br /&gt;
So, after several months of increasing frustration, I decided to do something.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;&lt;h3&gt;But what?&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;Drink the Unix Kool-Aid and learn to love &lt;a href="http://gnuwin32.sourceforge.net/packages/grep.htm" target="_blank"&gt;grep for Windows&lt;/a&gt;? &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;switch from Wordpad to something fancy like &lt;a href="http://notepad-plus-plus.org/" target="_blank"&gt;Notepad++&lt;/a&gt;?&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;/li&gt;
&lt;li&gt;go big with something like &lt;a href="http://www.developer.com/db/article.php/3092741/Managing-SQL-in-Visual-Studio-NET.htm" target="_blank"&gt;Visual Studio for SQL&lt;/a&gt;?&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;or . . . build it using SQL Anywhere?&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
If you are familiar with this blog, especially if you've read &lt;a href="http://sqlanywhere.blogspot.com/2009/01/everything-looks-like-database.html" target="_blank"&gt;Everything Looks Like a Database&lt;/a&gt;, the choice shouldn't come as any surprise:&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;&lt;h3&gt;Door Number Four: Build It Using SQL Anywhere&lt;/h3&gt;Here's what the SQL Anywhere solution looks like...&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;A simple browser display that lets you pick a search string, a file type and a folder:  &lt;br /&gt;
&lt;p&gt;&lt;img src="http://2.bp.blogspot.com/-_C4Ld5joSzE/Twmx-P9qw1I/AAAAAAAABhc/CUSvHv8iXSI/s1600/search%2B682_327.jpg" width="682" height="327" title="Search"&gt;&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;and then lets you doubleclick on one of the files to open it in your editor of choice:&lt;br /&gt;
&lt;p&gt;&lt;img src="http://1.bp.blogspot.com/-coz4OwZOXNs/Twmsw8QWhwI/AAAAAAAABhQ/nwsnbLIvyMg/s1600/wordpad_677_330.jpg" width="677" height="330" title="Wordpad"&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Here are the SQL Anywhere features that make it possible:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;A &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbusage/accessrd-s-5562974.html" target="_blank"&gt;Directory Access Server&lt;/a&gt; to implement a SELECT on the file names in the folder, &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;the &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/xp-read-file-sysproc.html*d5e78915" target="_blank"&gt;xp_read_file() system procedure&lt;/a&gt; to read each file,&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;the &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/locate-function.html*d5e19383" target="_blank"&gt;LOCATE() function&lt;/a&gt; to search inside each file,&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;the &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/xp-cmdshell-sysproc.html*d5e78767" target="_blank"&gt;xp_cmdshell() system procedure&lt;/a&gt; to launch the editor on a selected file, and&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;the &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/create-service-statement.html*d5e41355" target="_blank"&gt;CREATE SERVICE statement&lt;/a&gt; to run it all from within a browser using SQL Anywhere's built-in HTTP server.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;Here's the code:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE search_spec (
   one_row_only  INTEGER NOT NULL PRIMARY KEY CHECK ( one_row_only = 1 ),
   folder_spec   VARCHAR ( 200 ) NOT NULL,
   file_ext      VARCHAR ( 10 ) NOT NULL,
   search_for    VARCHAR ( 100 ) NOT NULL );

INSERT search_spec VALUES ( 1, '.\\', '.sql', '' );
COMMIT;

CREATE SERVER local_folder CLASS 'DIRECTORY' USING 'ROOT=C:\\;SUBDIRS=0';

CREATE EXTERNLOGIN DBA TO local_folder;

CREATE EXISTING TABLE local_file AT 'local_folder;;;.';

CREATE SERVICE search TYPE 'RAW' AUTHORIZATION OFF USER DBA 
   AS CALL search();

CREATE PROCEDURE search()
RESULT ( html_string LONG VARCHAR )
BEGIN

DECLARE @folder_spec              VARCHAR ( 200 );
DECLARE @file_ext                 VARCHAR ( 10 );
DECLARE @search_for               VARCHAR ( 100 );
DECLARE @action                   VARCHAR ( 10 );
DECLARE @file_name                VARCHAR ( 200 );
DECLARE @variable_name            LONG VARCHAR;
DECLARE @alter_server_statement   LONG VARCHAR;

DECLARE LOCAL TEMPORARY TABLE local_file_match (
   local_file_name    VARCHAR ( 260 ) NOT NULL PRIMARY KEY )
   NOT TRANSACTIONAL;

SELECT folder_spec,
       file_ext,
       search_for
  INTO @folder_spec,
       @file_ext,
       @search_for
  FROM search_spec;

SET @variable_name = NEXT_HTTP_VARIABLE ( NULL );
WHILE @variable_name IS NOT NULL LOOP
   CASE @variable_name 
      WHEN 'folder_spec' THEN SET @folder_spec = HTTP_VARIABLE ( @variable_name );
      WHEN 'file_ext'    THEN SET @file_ext    = HTTP_VARIABLE ( @variable_name );
      WHEN 'search_for'  THEN SET @search_for  = HTTP_VARIABLE ( @variable_name );
      WHEN 'action'      THEN SET @action      = HTTP_VARIABLE ( @variable_name );
      WHEN 'file_name'   THEN SET @file_name   = HTTP_VARIABLE ( @variable_name );
   END CASE;
   SET @variable_name = NEXT_HTTP_VARIABLE ( @variable_name );
END LOOP;

SET @folder_spec = TRIM ( @folder_spec );
SET @file_ext    = TRIM ( @file_ext );
SET @search_for  = TRIM ( @search_for );

IF RIGHT ( @folder_spec, 1 ) &amp;lt;&gt; '\\' THEN
   SET @folder_spec = STRING ( @folder_spec, '\\' );
END IF;

IF  LENGTH ( @file_ext )  &gt;= 1
AND LEFT ( @file_ext, 1 ) &amp;lt;&gt; '.' THEN
   SET @file_ext = STRING ( '.', @file_ext );
END IF;

SET @action = COALESCE ( @action, 'Search' );

CASE @action

   WHEN 'Search' THEN

      UPDATE search_spec
         SET folder_spec = @folder_spec,
             file_ext    = @file_ext,
             search_for  = @search_for;

      COMMIT;

      SET @alter_server_statement = STRING ( 
         'ALTER SERVER local_folder USING ''ROOT=',
         @folder_spec,
         ';SUBDIRS=0''' );

      EXECUTE IMMEDIATE @alter_server_statement;

   WHEN 'Open' THEN

      CALL xp_cmdshell ( STRING ( 
                            'start "C:\\Program Files (x86)\\Windows NT\\Accessories\\wordpadx.exe" "',
                            @folder_spec, 
                            @file_name,
                            '"' ),
                         'no_output' );

END CASE;

FOR f_fetch AS c_fetch INSENSITIVE CURSOR FOR
SELECT local_file.file_name AS @local_file_name
  FROM local_file 
 WHERE local_file.file_name LIKE STRING ( '%', @file_ext )  
 ORDER BY local_file.file_name 
FOR READ ONLY
DO
   IF TRIM ( @search_for ) = '' THEN
      INSERT local_file_match VALUES ( @local_file_name );
   ELSE
      IF LOCATE ( xp_read_file ( STRING ( @folder_spec, @local_file_name ) ), @search_for ) &gt; 0 THEN
         INSERT local_file_match VALUES ( @local_file_name );
      END IF;
   END IF;
END FOR;

CALL dbo.sa_set_http_header( 'Content-Type', 'text/html' );

SELECT STRING (
   '&amp;lt;HTML&gt;&amp;lt;HEAD&gt;\x0d\x0a',
   '&amp;lt;TITLE&gt;Search&amp;lt;/TITLE&gt;\x0d\x0a',
   '&amp;lt;META HTTP-EQUIV="Pragma" CONTENT="no-cache"&gt;\x0d\x0a',
   '&amp;lt;script Language="JavaScript"&gt;\x0d\x0a',
   'function searchF() {\x0d\x0a',
      'document.f.action = "search?action=Search";\x0d\x0a',
      'document.f.submit();\x0d\x0a',
      'return false; }\x0d\x0a',
   'function openF ( file_name ) {\x0d\x0a',
      'document.f.action = "search?action=Open&amp;file_name=" + file_name;\x0d\x0a',
      'document.f.submit();\x0d\x0a',
      'return false; }\x0d\x0a',
   '&amp;lt;/script&gt;\x0d\x0a',
   '&amp;lt;STYLE&gt; P { font-family: COURIER NEW; font-size: 8pt; margin-bottom: 0; margin-top: 0; } &amp;lt;/STYLE&gt;\x0d\x0a',
   '&amp;lt;/HEAD&gt;\x0d\x0a',
   '&amp;lt;BODY BGCOLOR=#FFFFFF&gt;\x0d\x0a',

   '&amp;lt;FORM NAME="f" METHOD="POST"&gt;&amp;lt;P&gt;\x0d\x0a',

   'Search for this exact string: &amp;lt;INPUT TYPE="TEXT" NAME="search_for" VALUE="',
   @search_for,
   '" SIZE=40 STYLE="font-family: COURIER NEW;"&gt;&amp;lt;P&gt;&amp;nbsp;\x0d\x0a',

   '...in all files with this extension: &amp;lt;INPUT TYPE="TEXT" NAME="file_ext" VALUE="',
   @file_ext,
   '" SIZE=10 STYLE="font-family: COURIER NEW;"&gt;&amp;lt;P&gt;&amp;nbsp;\x0d\x0a',

   '...in this folder: &amp;lt;INPUT TYPE="TEXT" NAME="folder_spec" VALUE="',
   @folder_spec,
   '" SIZE=50 STYLE="font-family: COURIER NEW;"&gt;&amp;lt;P&gt;\x0d\x0a',

   '&amp;lt;INPUT TYPE="SUBMIT" ONCLICK="searchF();" VALUE="Search"&gt;&amp;lt;P&gt;\x0d\x0a',

   LIST ( STRING (
             '&amp;lt;P ONDBLCLICK="openF ( ''',
             local_file_match.local_file_name,
             ''' );"&gt;&amp;nbsp;',
             local_file_match.local_file_name,
             '\x0d\x0a' ),
          ''
          ORDER BY local_file_match.local_file_name ), 

   '&amp;lt;/FORM&gt;&amp;lt;/BODY&gt;&amp;lt;/HTML&gt; ' )
   FROM local_file_match; 

END;
&lt;/pre&gt;&lt;br /&gt;
Here are some notes on the code:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;b&gt;Lines 1 through 8:&lt;/b&gt; The search_spec table is the program's memory, allowing it to pass the most recent search parameters from session to session.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;10:&lt;/b&gt; The &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/create-server-statement.html*d5e41119" target="_blank"&gt;CREATE SERVER statement&lt;/a&gt; creates the local_folder &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbusage/accessrd-s-5562974.html" target="_blank"&gt;Directory Access Server&lt;/a&gt; which initially points at the root folder C:\ (but will be changed later via ALTER SERVER).&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;12:&lt;/b&gt; For security purposes, the Directory Access Server feature requires an EXTERNLOGIN to be created for each SQL user id that will be using it.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;14:&lt;/b&gt; The local_file proxy table allows the search procedure to SELECT file information from the local_folder Directory Access Server.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;16 and 17:&lt;/b&gt; The &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/create-service-statement.html*d5e41355" target="_blank"&gt;CREATE SERVICE statement&lt;/a&gt; names the search procedure as being responsible for responding to browser requests with raw HTTP.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;43 through 53:&lt;/b&gt; This loop analyzes the HTTP parameters passed from the browser and stores the values in the appropriate local variables. You can also pass named parameters via the CREATE SERVICE and CREATE PROCEDURE statments, but in the long run it's easier to maintain a simple NEXT_HTTP_VARIABLE() loop like this one.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;55 through 68:&lt;/b&gt; This code performs some cleanup and editing of the input paramenters.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;70 through 97:&lt;/b&gt; This CASE statement processes the two possible actions: 'Search' when the Search button is pressed, and 'Open' when a file name is double-clicked.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;81 through 86:&lt;/b&gt; The &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/alter-server-statement.html*d5e30453" target="_blank"&gt;ALTER SERVER statement&lt;/a&gt; changes the 'ROOT=' option to point to the input folder.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;90 through 95:&lt;/b&gt; The &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/xp-cmdshell-sysproc.html*d5e78767" target="_blank"&gt;xp_cmdshell system procedure&lt;/a&gt; call opens the selected file using "Wordpad Classic". This exact code probably won't work for you unless you have installed the original Windows XP Wordpad utility as "wordpadx.exe" in this particular location. However, it will work if you change this line&lt;blockquote&gt;&lt;pre&gt;'start "C:\\Program Files (x86)\\Windows NT\\Accessories\\wordpadx.exe" "',&lt;/pre&gt;&lt;/blockquote&gt;to this&lt;blockquote&gt;&lt;pre&gt;'notepad "',&lt;/pre&gt;&lt;/blockquote&gt;&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;99 through 113:&lt;/b&gt; This &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/for-statement.html*d5e49884" target="_blank"&gt;FOR loop&lt;/a&gt; stores the matching file names in the local temporary table local_file_match.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;102:&lt;/b&gt; The &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/colc.html*d5e3471" target="_blank"&gt;LIKE predicate&lt;/a&gt; looks for the @file_ext string in the file names.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;109:&lt;/b&gt; The &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/locate-function.html*d5e19383" target="_blank"&gt;LOCATE() function&lt;/a&gt; and &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/xp-read-file-sysproc.html*d5e78915" target="_blank"&gt;xp_read_file system procedure&lt;/a&gt; look for the @search_for string inside the files themselves.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;115:&lt;/b&gt; The &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/sa-set-http-header-sysproc.html*d5e75802" target="_blank"&gt;sa_set_http_header system procedure&lt;/a&gt; call tells the browser that HTML text is coming next.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;117 through 161:&lt;/b&gt; The SELECT returns a single HTML string to the browser. The \x0d\x0a characters are all unnecessary; they have been included here to make the HTML text more readable.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;122 through 126:&lt;/b&gt; The JavaScript function searchF() and openF() allow the "Search" and "Open" actions to be customized. This may not be required in the case of searchF(), but with openF() the file_name parameter depends on where openF() was called from.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;137 through 147:&lt;/b&gt; These INPUT TEXT tags define the three HTTP input parameters search_for, file_ext and folder_spec.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;149:&lt;/b&gt; This INPUT SUBMIT tag calls searchF() to pass the action HTTP input parameter to the search service.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;151 through 158:&lt;/b&gt; The SQL &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/list-function.html*d5e19227" target="_blank"&gt;LIST()&lt;/a&gt; and &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/string-function.html*d5e25067" target="_blank"&gt;STRING()&lt;/a&gt; functions builds a series of active, double-clickable P paragraph tags to display all the matching file names. Each ONDBLCLICK attribute calls openF() to pass the action and file_name HTTP input parameters to the search service.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;Here are the Windows commands to create the SQL Anywhere database, start it with the HTTP server running on port 12345, compile the code using dbisql and then launch it in your browser:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;"%SQLANY12%\bin32\dbinit.exe" ddd.db

"%SQLANY12%\bin32\dbspawn.exe" -f "%SQLANY12%\bin32\dbeng12.exe" -xs http(port=12345) ddd.db 

"%SQLANY12%\bin32\dbisql.com" -c "ENG=ddd;DBN=ddd;UID=dba;PWD=sql" READ ENCODING Cp1252 search.sql

START "" "http://localhost:12345/search"
&lt;/pre&gt;&lt;br /&gt;
Here's what the HTML looks like:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;&amp;lt;HTML&gt;&amp;lt;HEAD&gt;
&amp;lt;TITLE&gt;Search&amp;lt;/TITLE&gt;
&amp;lt;META HTTP-EQUIV="Pragma" CONTENT="no-cache"&gt;
&amp;lt;script Language="JavaScript"&gt;
function searchF() {
document.f.action = "search?action=Search";
document.f.submit();
return false; }
function openF ( file_name ) {
document.f.action = "search?action=Open&amp;file_name=" + file_name;
document.f.submit();
return false; }
&amp;lt;/script&gt;
&amp;lt;STYLE&gt; P { font-family: COURIER NEW; font-size: 8pt; margin-bottom: 0; margin-top: 0; } &amp;lt;/STYLE&gt;
&amp;lt;/HEAD&gt;
&amp;lt;BODY BGCOLOR=#FFFFFF&gt;
&amp;lt;FORM NAME="f" METHOD="POST"&gt;&amp;lt;P&gt;
Search for this exact string: &amp;lt;INPUT TYPE="TEXT" NAME="search_for" VALUE="'Sampling Stopped'" SIZE=40 STYLE="font-family: COURIER NEW;"&gt;&amp;lt;P&gt;&amp;nbsp;
...in all files with this extension: &amp;lt;INPUT TYPE="TEXT" NAME="file_ext" VALUE=".sql" SIZE=10 STYLE="font-family: COURIER NEW;"&gt;&amp;lt;P&gt;&amp;nbsp;
...in this folder: &amp;lt;INPUT TYPE="TEXT" NAME="folder_spec" VALUE="C:\projects\foxhound\" SIZE=50 STYLE="font-family: COURIER NEW;"&gt;&amp;lt;P&gt;
&amp;lt;INPUT TYPE="SUBMIT" ONCLICK="searchF();" VALUE="Search"&gt;&amp;lt;P&gt;
&amp;lt;P ONDBLCLICK="openF ( '015b_rroad_other_startup.sql' );"&gt;&amp;nbsp;015b_rroad_other_startup.sql
&amp;lt;P ONDBLCLICK="openF ( '019_rroad_setup_help.sql' );"&gt;&amp;nbsp;019_rroad_setup_help.sql
&amp;lt;P ONDBLCLICK="openF ( '023_rroad_start_sampling.sql' );"&gt;&amp;nbsp;023_rroad_start_sampling.sql
&amp;lt;P ONDBLCLICK="openF ( '203_rroad_monitor_sample_loop.sql' );"&gt;&amp;nbsp;203_rroad_monitor_sample_loop.sql
&amp;lt;/FORM&gt;&amp;lt;/BODY&gt;&amp;lt;/HTML&gt; 
&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;&lt;h3&gt;It Could Be Simpler&lt;/h3&gt;Here's what the proxy table local_file looks like:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE EXISTING TABLE DBA.local_file ( 
   permissions                          VARCHAR ( 10 ) NOT NULL,
   size                                 BIGINT NOT NULL,
   access_date_time                     TIMESTAMP NOT NULL,
   modified_date_time                   TIMESTAMP NOT NULL,
   create_date_time                     TIMESTAMP NOT NULL,
   owner                                VARCHAR ( 20 ) NOT NULL,
   file_name                            VARCHAR ( 260 ) NOT NULL,
   contents                             LONG BINARY NOT NULL 
) AT 'local_folder;;;.';
&lt;/pre&gt;&lt;br /&gt;
Not only does local_file contain the file_name, it contains the actual file contents as well!&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;That means instead of calling xp_read_file, the FOR loop on line 99 could simply SELECT the local_file.contents column and use that to LOCATE the search string.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-289009856650763898?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/y5gQrnIMMgo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/y5gQrnIMMgo/you-cant-do-that-in-sql.html</link><author>noreply@blogger.com (Breck Carter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-_C4Ld5joSzE/Twmx-P9qw1I/AAAAAAAABhc/CUSvHv8iXSI/s72-c/search%2B682_327.jpg" height="72" width="72" /><thr:total>5</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/you-cant-do-that-in-sql.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-4389600175301396781</guid><pubDate>Wed, 04 Jan 2012 08:01:00 +0000</pubDate><atom:updated>2012-01-04T03:01:00.798-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">announcement</category><title>Happy New Year!</title><description>&lt;p&gt;Here are my resolutions for 2012:&lt;br /&gt;
&lt;h3&gt;10. Don't Buy A BlackBerry PlayBook&lt;/h3&gt;Every New Year's listicle should include at least one resolution that is possible to keep, this one is mine.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://3.bp.blogspot.com/-3M-rUhWJfb4/TwCOmOUYliI/AAAAAAAABgI/PMzPnKuNwzM/s1600/10%2BBlackBerry%2BPlayBook%2B871_637.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="293" width="400" src="http://3.bp.blogspot.com/-3M-rUhWJfb4/TwCOmOUYliI/AAAAAAAABgI/PMzPnKuNwzM/s400/10%2BBlackBerry%2BPlayBook%2B871_637.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h3&gt;9. Make Time For Tea Time&lt;/h3&gt;...IPA is a tea, right?&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://1.bp.blogspot.com/-ayFTDmqak-A/TwCQvb3xX8I/AAAAAAAABgs/H0Ss9jCd-ys/s1600/9%2BCharles%2BWells%2Bbeer%2B800_600.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="300" width="400" src="http://1.bp.blogspot.com/-ayFTDmqak-A/TwCQvb3xX8I/AAAAAAAABgs/H0Ss9jCd-ys/s400/9%2BCharles%2BWells%2Bbeer%2B800_600.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h3&gt;8. Watch More TV&lt;/h3&gt;David Milch did Deadwood, what could go wrong with "Luck"?&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://2.bp.blogspot.com/-hdAIS7LuEmY/TwCQMDHLlII/AAAAAAAABgU/mYjLMIU4HRM/s1600/8%2BLuck%2B822_418.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="203" width="400" src="http://2.bp.blogspot.com/-hdAIS7LuEmY/TwCQMDHLlII/AAAAAAAABgU/mYjLMIU4HRM/s400/8%2BLuck%2B822_418.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h3&gt;7. Cut Back On The Carbs&lt;/h3&gt;Prime rib, or ribeye? &lt;br /&gt;
Yes, please! &lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://3.bp.blogspot.com/-B4_CPqBA7u8/TwCOGQ7XFoI/AAAAAAAABfk/DM6smNxDsGQ/s1600/7%2BBeef%2B717_364.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="203" width="400" src="http://3.bp.blogspot.com/-B4_CPqBA7u8/TwCOGQ7XFoI/AAAAAAAABfk/DM6smNxDsGQ/s400/7%2BBeef%2B717_364.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h3&gt;6. Explore The World!&lt;/h3&gt;...starting in Scotland.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://3.bp.blogspot.com/-KFMyO5Tr3XQ/TwCN8PTcRXI/AAAAAAAABfY/H81_NSaBDL0/s1600/6%2BSingle%2Bmalts%2B975_466.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="191" width="400" src="http://3.bp.blogspot.com/-KFMyO5Tr3XQ/TwCN8PTcRXI/AAAAAAAABfY/H81_NSaBDL0/s400/6%2BSingle%2Bmalts%2B975_466.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h3&gt;5. Let Sleeping Dogs Lie&lt;/h3&gt;Eat, sleep: what better role model than a dog?&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://4.bp.blogspot.com/-XP_ud8YOa8U/TwCNv2RSmTI/AAAAAAAABfM/yVVUyvJbCGg/s1600/5%2BJason%2Band%2BJulie%2B640_480.JPG" imageanchor="1" style=""&gt;&lt;img border="0" height="300" width="400" src="http://4.bp.blogspot.com/-XP_ud8YOa8U/TwCNv2RSmTI/AAAAAAAABfM/yVVUyvJbCGg/s400/5%2BJason%2Band%2BJulie%2B640_480.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h3&gt;4. Eat More Fruit&lt;/h3&gt;Cabernet Sauvignon, Merlot, and so forth.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://2.bp.blogspot.com/-QUdp7BD2yJc/TwCNdALvB5I/AAAAAAAABfA/eDpQvnX5FlM/s1600/4%2BGrapes%2B800_600.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="300" width="400" src="http://2.bp.blogspot.com/-QUdp7BD2yJc/TwCNdALvB5I/AAAAAAAABfA/eDpQvnX5FlM/s400/4%2BGrapes%2B800_600.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h3&gt;3. Spend More Time Floating&lt;/h3&gt;Some folks jump in, swim around and then get out, thus missing the point of "pool" entirely.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://3.bp.blogspot.com/-yfl7wx_8fr8/TwCNAnfiN9I/AAAAAAAABe0/nWeorRslNts/s1600/3%2BPool%2B660_443.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="268" width="400" src="http://3.bp.blogspot.com/-yfl7wx_8fr8/TwCNAnfiN9I/AAAAAAAABe0/nWeorRslNts/s400/3%2BPool%2B660_443.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h3&gt;2. Follow V8 Supercars&lt;/h3&gt;Like NASCAR but with actual competition.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://4.bp.blogspot.com/-XpR6On_2TSE/TwCMp8JCthI/AAAAAAAABeo/VLJ8Dooglzc/s1600/2%2BV8%2BSupercars%2B640_425%2B.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="266" width="400" src="http://4.bp.blogspot.com/-XpR6On_2TSE/TwCMp8JCthI/AAAAAAAABeo/VLJ8Dooglzc/s400/2%2BV8%2BSupercars%2B640_425%2B.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
...and last but not least:&lt;br /&gt;
&lt;h3&gt;1. Post More Examples Here&lt;/h3&gt;&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-4389600175301396781?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/qu-f8Us94q0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/qu-f8Us94q0/happy-new-year.html</link><author>noreply@blogger.com (Breck Carter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-3M-rUhWJfb4/TwCOmOUYliI/AAAAAAAABgI/PMzPnKuNwzM/s72-c/10%2BBlackBerry%2BPlayBook%2B871_637.jpg" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2012/01/happy-new-year.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-8206551568178786382</guid><pubDate>Mon, 26 Dec 2011 14:16:00 +0000</pubDate><atom:updated>2011-12-26T09:16:34.888-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">latest EBFs</category><category domain="http://www.blogger.com/atom/ns#">announcement</category><title>Latest SQL Anywhere EBFs - Santa Claus Edition</title><description>&lt;p&gt;The three asterisks "***" show what's new since the &lt;a href="http://sqlanywhere.blogspot.com/2011/12/latest-sql-anywhere-ebfs-11012724-linux.html" target="_blank"&gt;previous list&lt;/a&gt;. &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Only EBFs for the &lt;b&gt;latest fully-supported versions&lt;/b&gt; of SQL Anywhere are shown here: 10.0.1, 11.0.1 and 12.0.1. &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Note:&lt;/b&gt; Some earlier versions are also fully supported but they are not shown here; e.g., the 12.0.0.2758 EBF for Windows was just released. For more information see &lt;a href="http://www.sybase.com/detail?id=1002288" target="_blank"&gt;SQL Anywhere Supported Platforms and Engineering Support Status&lt;/a&gt;.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Also:&lt;/b&gt; Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more &lt;b&gt;new&lt;/b&gt; EBFs released. For a complete list of everything available for download see &lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Downloads - EBFs / Maintenance&lt;/a&gt;.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Current builds for the most active platforms...&lt;/a&gt;&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX Itanium    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3777 EBF       14 Oct 2008

IBM AIX          12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011

Linux x86/x64    12.0.1.3519 EBF ***   20 Dec 2011 ***
                 11.0.1.2724 EBF       14 Dec 2011
                 10.0.1.4239 EBF       17 Jun 2011

Mac OS           12.0.1.3352 EBF       26 May 2011
                 11.0.1.2449 EBF       29 Jun 2010
                 10.0.1.4042 EBF       01 Oct 2010

Solaris SPARC    12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.4239 EBF       17 Jun 2011

Solaris x64      12.0.1.3523 EBF       15 Dec 2011
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3870 EBF       01 Apr 2009

Windows x86/x64  12.0.1.3519 EBF       20 Dec 2011 ***
                 11.0.1.2724 EBF       20 Dec 2011 ***
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;] [&lt;a href="http://response.sybase.com/forms/FujiBetaTrial" target="_blank"&gt;Register for the Fuji Beta here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;...and for the other platforms:&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX PA-RISC    10.0.1.3778 EBF                 16 Oct 2008

Linux Itanium    10.0.1.3777 EBF                 14 Oct 2008

NetWare          10.0.1.4036 EBF                 10 Mar 2010

Windows CE       10.0.1.4036 EBF                 09 Mar 2010

Windows Itanium  10.0.1 GA Upgrade from 10.0.0   26 Apr 2007
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-8206551568178786382?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/cgMzuBs9nsY" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/cgMzuBs9nsY/latest-sql-anywhere-ebfs-santa-claus.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/latest-sql-anywhere-ebfs-santa-claus.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-8088585123630066401</guid><pubDate>Fri, 23 Dec 2011 08:01:00 +0000</pubDate><atom:updated>2011-12-23T03:01:00.867-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><title>Product Suggestion: User-Defined ERRORMSG()</title><description>&lt;p&gt;Exception handling in SQL Anywhere procedures, triggers and events is quite powerful, of that there is no question.&lt;br /&gt;
&lt;br /&gt;
When it comes to &lt;b&gt;user-defined&lt;/b&gt; exceptions, however...&lt;br /&gt;
&lt;h3&gt;...not so much&lt;/h3&gt;&lt;br /&gt;
Here's a demonstration:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Procedure p1 calls procedure p2.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;Procedure p1 is called by an application (in this case ISQL).&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;When p2 executes it uses the &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/signal-statement.html*d5e61658" target="_blank"&gt;SIGNAL statement&lt;/a&gt; to raise a user-defined exception.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;That exception is captured by the &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/begin-statement.html*d5e33770" target="_blank"&gt;EXCEPTION block&lt;/a&gt; in p2.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;The exception block in p2 then &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;displays the &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/sqlcode-wsqlref.html*d5e5524" target="_blank"&gt;SQLCODE&lt;/a&gt;, &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/sqlstate-wsqlref.html" target="_blank"&gt;SQLSTATE&lt;/a&gt; and &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/errormsg-function.html*d5e15798" target="_blank"&gt;ERRORMSG()&lt;/a&gt; on the database console log, and then &lt;br /&gt;
&lt;/li&gt;

&lt;li&gt;uses the &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/resignal-statement.html*d5e58055" target="_blank"&gt;RESIGNAL statement&lt;/a&gt; to pass the exception up to procedure p1.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;When the exception reaches p1 it is captured by the EXCEPTION block in p1.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;The exception block in p1 then &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;displays another message on the database console log and &lt;br /&gt;
&lt;/li&gt;

&lt;li&gt;once again uses the RESIGNAL statement to pass the exception up to the application.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;When the exception reaches the application (ISQL) a dialog box is displayed.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Here's the code:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE PROCEDURE p1()
BEGIN
   DECLARE @sqlcode   INTEGER;
   DECLARE @sqlstate  VARCHAR ( 5 );
   DECLARE @errormsg  VARCHAR ( 32767 );

   CALL p2();

EXCEPTION

   WHEN OTHERS THEN

      SELECT SQLCODE, SQLSTATE, ERRORMSG() 
        INTO @sqlcode, @sqlstate, @errormsg;

      MESSAGE STRING ( 
         'EXCEPTION detected in p1() at ', 
         CURRENT TIMESTAMP, 
         ': SQLCODE = ', @sqlcode,  
         ', SQLSTATE = ', @sqlstate,  
         ', ERRORMSG() = ', @errormsg ) 
         TO CONSOLE;

      RESIGNAL;

END;

CREATE PROCEDURE p2()
BEGIN
   DECLARE @sqlcode   INTEGER;
   DECLARE @sqlstate  VARCHAR ( 5 );
   DECLARE @errormsg  VARCHAR ( 32767 );

   BEGIN
      DECLARE @exception EXCEPTION FOR SQLSTATE '99001';
      SIGNAL @exception;
   END;

EXCEPTION
   WHEN OTHERS THEN

      SELECT SQLCODE, SQLSTATE, ERRORMSG() 
        INTO @sqlcode, @sqlstate, @errormsg;

      MESSAGE STRING ( 
         'EXCEPTION detected in p2() at ', 
         CURRENT TIMESTAMP, 
         ': SQLCODE = ', @sqlcode,  
         ', SQLSTATE = ', @sqlstate,  
         ', ERRORMSG() = ', @errormsg ) 
         TO CONSOLE;

      RESIGNAL;

END;

CALL p1(); -- call from ISQL
&lt;/pre&gt;&lt;br /&gt;
Here's what appears in the database console log, followed by the dialog box:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;EXCEPTION detected in p2() at 2011-12-22 15:16:54.381: SQLCODE = -297, SQLSTATE = 99001, ERRORMSG() = User-defined exception signaled
EXCEPTION detected in p1() at 2011-12-22 15:16:54.382: SQLCODE = -297, SQLSTATE = 99001, ERRORMSG() = User-defined exception signaled
&lt;/pre&gt;&lt;img src="http://4.bp.blogspot.com/-5woFEejQ83M/TvOceShNvII/AAAAAAAABeQ/9Juhn62fspE/s1600/ISQL%2BError%2B678_363.jpg" width="678" height="363" title="ISQL Error"&gt;&lt;br /&gt;
&lt;br /&gt;
That's all well and good, but the problem is this: only the SQLSTATE is user-defined. The SQLCODE and ERRORMSG() values are both fixed and pre-defined, and they mean exactly the same thing: &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://dcx.sybase.com/index.html#1201/en/saerrors/errm297.html" target="_blank"&gt;SQLCODE = -297 User-defined exception signaled&lt;/a&gt; &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;ERRORMSG() = User-defined exception signaled&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;h3&gt;Hey! We know that already!&lt;/h3&gt;&lt;br /&gt;
In order to be truly useful, user-defined exceptions need some mechanism for providing a user-defined error message as well as a SQLSTATE code.&lt;br /&gt;
&lt;br /&gt;
Even that &lt;strike&gt;pathetic&lt;/strike&gt; limited Transact SQL &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/raiserror-statement.html*d5e57045" target="_blank"&gt;RAISERROR statement&lt;/a&gt; has a mechanism for providing a user-defined message as well as an error code.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-8088585123630066401?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/Xmk8Jab0OTI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/Xmk8Jab0OTI/product-suggestion-user-defined.html</link><author>noreply@blogger.com (Breck Carter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-5woFEejQ83M/TvOceShNvII/AAAAAAAABeQ/9Juhn62fspE/s72-c/ISQL%2BError%2B678_363.jpg" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/product-suggestion-user-defined.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-8024156893878469287</guid><pubDate>Wed, 21 Dec 2011 08:01:00 +0000</pubDate><atom:updated>2011-12-21T03:01:00.356-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><title>Let's play "Gotcha!" - the ACID Edition</title><description>&lt;p&gt;Welcome to the ACID Edition of "Gotcha!", the self-testing quiz game for SQL Anywhere developers.&lt;br /&gt;
&lt;br /&gt;
Let's get started right away...&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Are your transactions atomic?&lt;/h3&gt;&lt;br /&gt;
Are they consistent? isolated? durable?&lt;br /&gt;
&lt;br /&gt;
Are they &lt;b&gt;reentrant&lt;/b&gt;?&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Are you &lt;b&gt;sure&lt;/b&gt;?&lt;/h3&gt;&lt;br /&gt;
Let's say you have created a SQL Anywhere web service to update your database, and this web service is launched when the user clicks on an INPUT TYPE="BUTTON" on a web page.&lt;br /&gt;
&lt;br /&gt;
Let's also say you've designed the code carefully so all the database updates are performed within a single database transaction, with a single COMMIT after the updates are complete.&lt;br /&gt;
&lt;br /&gt;
Furthermore, let's say you have carefully inspected the code, and you are sure of the following facts:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Each time your web service is executed, it either runs on a brand-new database connection or a pool connection that has been freshly sanitized (because &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbprogramming/pg-httpserver-s-521208111.html" target="_blank"&gt;that's how SQL Anywhere web services work&lt;/a&gt;), &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;if the web service finishes normally, by definition the COMMIT will have been executed, which means all the updates performed during the database transaction will have been successfully written to the database, and&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;if the web service terminates abnormally before reaching the COMMIT, SQL Anywhere will roll back all the updates performed during the database transaction (because &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbusage/using-introduction-transact.html" target="_blank"&gt;that's what SQL Anywhere does when a connection terminates without doing a COMMIT&lt;/a&gt;).&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;Here's a simple demonstration that's just one step up from "Hello World":&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://1.bp.blogspot.com/-RWa2grXVrQY/Tu9e46zoMbI/AAAAAAAABdw/HeDbOJ1bFsE/s1600/Service%2Bs%2B800_428.jpg" width="800" height="428" title="Service s"&gt;&lt;br /&gt;
&lt;br /&gt;
Here's what the service does when it is invoked from the browser:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;It deletes all 100 rows from a table called t, then &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;it re-inserts all 100 rows and finally&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;it displays them as ordered pairs inside parentheses.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;Here's the code: One table, two procedures and one service; remember, this is a demonstration, so don't expect anything useful:&lt;br /&gt;
&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t (
   pkey   INTEGER NOT NULL PRIMARY KEY,
   data   INTEGER NOT NULL );

INSERT t SELECT row_num, row_num FROM sa_rowgenerator ( 1, 100 );
COMMIT;

CREATE PROCEDURE p()
BEGIN

DECLARE @sqlcode   INTEGER;
DECLARE @sqlstate  VARCHAR ( 5 );
DECLARE @errormsg  VARCHAR ( 32767 );

DELETE t;

WAITFOR DELAY '00:00:00.001';

INSERT t SELECT row_num, row_num FROM sa_rowgenerator ( 1, 100 );

WAITFOR DELAY '00:00:00.001';

COMMIT;

EXCEPTION
   WHEN OTHERS THEN
      SELECT SQLCODE, SQLSTATE, ERRORMSG() 
        INTO @sqlcode, @sqlstate, @errormsg;
      MESSAGE STRING ( 
         'DIAG ', @@SPID, ' ', CURRENT TIMESTAMP, ' EXCEPTION in p: SQLCODE = ', @sqlcode,  
         ', SQLSTATE = ', @sqlstate,  
         ', ERRORMSG() = ', @errormsg ) 
         TO CONSOLE;
      RESIGNAL;

END;

CREATE SERVICE s 
   TYPE 'RAW' AUTHORIZATION OFF USER DBA
   AS CALL s();

CREATE PROCEDURE s()
RESULT ( html_string LONG VARCHAR )
BEGIN
DECLARE @sqlcode   INTEGER;
DECLARE @sqlstate  VARCHAR ( 5 );
DECLARE @errormsg  VARCHAR ( 32767 );

MESSAGE STRING ( 'DIAG ', @@SPID, ' ', CURRENT TIMESTAMP, ' s started...' ) TO CONSOLE;

CALL p();

CALL dbo.sa_set_http_header( 'Content-Type', 'text/html' );

WAITFOR DELAY '00:00:00.001';

SELECT STRING (
   '&lt;HTML&gt; ',
   '&lt;HEAD&gt; ',
   '&lt;TITLE&gt;Service s&lt;/TITLE&gt; ',
   '&lt;/HEAD&gt; ',
   '&lt;BODY BGCOLOR=#FFFFFF&gt; ',
   '&lt;FORM ACTION="s" NAME="f" METHOD="POST"&gt;',
   LIST ( STRING ( '( ', t.pkey, ', ', t.data, ' )' ), ', ' ORDER BY t.pkey ),
   '&lt;P&gt;',
   '&lt;INPUT ONCLICK="return submitF ( ''Save'' );" TYPE="SUBMIT" VALUE="Save"&gt; ',
   '&lt;/FORM&gt;',
   '&lt;/BODY&gt; ',
   '&lt;/HTML&gt; ' )
FROM t;

EXCEPTION
   WHEN OTHERS THEN
      SELECT SQLCODE, SQLSTATE, ERRORMSG() 
        INTO @sqlcode, @sqlstate, @errormsg;
      MESSAGE STRING ( 
         'DIAG ', @@SPID, ' ', CURRENT TIMESTAMP, ' EXCEPTION in s: SQLCODE = ', @sqlcode,  
         ', SQLSTATE = ', @sqlstate,  
         ', ERRORMSG() = ', @errormsg ) 
         TO CONSOLE;

      ROLLBACK;

      RESIGNAL;

END;
&lt;/pre&gt;&lt;br /&gt;
As you can see, the code also explicitly captures any EXCEPTION raised in the code:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;The outer EXCEPTION handler in procedure s displays the connection number for the service  &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;together with the current connection number (@@SPID) and the SQLCODE, and then&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;it does a ROLLBACK and RESIGNAL before the service terminates.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;The inner EXCEPTION handler in procedure p does the same thing except no ROLLBACK.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;So far, so good&lt;/h3&gt;&lt;br /&gt;
Every time you click on the "Save" button, it does the same thing and displays the same output:&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://1.bp.blogspot.com/-RWa2grXVrQY/Tu9e46zoMbI/AAAAAAAABdw/HeDbOJ1bFsE/s1600/Service%2Bs%2B800_428.jpg" width="800" height="428" title="Service s"&gt;&lt;br /&gt;
&lt;br /&gt;
What happens if you start pounding on the Save button, and the service gets cancelled and restarted before it can finish?&lt;br /&gt;
&lt;br /&gt;
That's not a problem, right? The first execution of the service gets rolled back if it hasn't done the COMMIT yet, and the second service runs... unless it gets cancelled too.&lt;br /&gt;
&lt;br /&gt;
But that's not a problem either, right?&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
Here's the skill-testing question: &lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;What do you see in the browser window if your &lt;b&gt;really&lt;/b&gt; start pounding on the Save button as fast as you can?&lt;hr&gt;&lt;/blockquote&gt;&lt;br /&gt;
Don't rush, take your time...&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/vXGhvoekY44" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
Your code's been reviewed, it's ACID, reentrant, all those good things? &lt;br /&gt;
&lt;br /&gt;
Right?&lt;br /&gt;
&lt;br /&gt;
Shouldn't you see this no matter what? &lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://1.bp.blogspot.com/-RWa2grXVrQY/Tu9e46zoMbI/AAAAAAAABdw/HeDbOJ1bFsE/s1600/Service%2Bs%2B800_428.jpg" width="800" height="428" title="Service s"&gt;&lt;br /&gt;
&lt;br /&gt;
Are you sure? Take your time...&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/vXGhvoekY44" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Gotcha!&lt;/h3&gt;&lt;br /&gt;
If you keep pounding on the Save button, eventually you will see this in the browser:&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://4.bp.blogspot.com/-wBJBM4tUjZc/Tu9fJ2ZXSzI/AAAAAAAABd8/ZqXxiOoyeH4/s1600/SQL%2Berror%2B800_428.jpg" width="800" height="428" title="SQL error"&gt;&lt;br /&gt;
&lt;br /&gt;
Here are all the diagnostic messages from the SQL Anywhere database server console:&lt;br /&gt;
&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;DIAG 365 2011-12-19 10:05:00.924 s started...
DIAG 365 2011-12-19 10:05:01.093 EXCEPTION in p: SQLCODE = -299, SQLSTATE = 57014, ERRORMSG() = Statement interrupted by user
DIAG 366 2011-12-19 10:05:01.102 s started...
DIAG 365 2011-12-19 10:05:01.102 EXCEPTION in s: SQLCODE = -299, SQLSTATE = 57014, ERRORMSG() = Statement interrupted by user
DIAG 366 2011-12-19 10:05:01.208 EXCEPTION in p: SQLCODE = -193, SQLSTATE = 23W01, ERRORMSG() = Primary key for table 't' is not unique : Primary key value ('1')
DIAG 367 2011-12-19 10:05:01.253 s started...
DIAG 366 2011-12-19 10:05:01.256 EXCEPTION in s: SQLCODE = -193, SQLSTATE = 23W01, ERRORMSG() = Primary key for table 't' is not unique : Primary key value ('1')
DIAG 367 2011-12-19 10:05:01.419 EXCEPTION in p: SQLCODE = -299, SQLSTATE = 57014, ERRORMSG() = Statement interrupted by user
DIAG 368 2011-12-19 10:05:01.425 s started...
DIAG 367 2011-12-19 10:05:01.429 EXCEPTION in s: SQLCODE = -299, SQLSTATE = 57014, ERRORMSG() = Statement interrupted by user
DIAG 368 2011-12-19 10:05:01.539 EXCEPTION in p: SQLCODE = -193, SQLSTATE = 23W01, ERRORMSG() = Primary key for table 't' is not unique : Primary key value ('1')
DIAG 368 2011-12-19 10:05:01.547 EXCEPTION in s: SQLCODE = -193, SQLSTATE = 23W01, ERRORMSG() = Primary key for table 't' is not unique : Primary key value ('1')
DIAG 369 2011-12-19 10:05:01.589 s started...
DIAG 369 2011-12-19 10:05:01.740 EXCEPTION in p: SQLCODE = -299, SQLSTATE = 57014, ERRORMSG() = Statement interrupted by user
DIAG 370 2011-12-19 10:05:01.746 s started...
DIAG 369 2011-12-19 10:05:01.750 EXCEPTION in s: SQLCODE = -299, SQLSTATE = 57014, ERRORMSG() = Statement interrupted by user
DIAG 370 2011-12-19 10:05:01.864 EXCEPTION in p: SQLCODE = -193, SQLSTATE = 23W01, ERRORMSG() = Primary key for table 't' is not unique : Primary key value ('1')
DIAG 370 2011-12-19 10:05:01.872 EXCEPTION in s: SQLCODE = -193, SQLSTATE = 23W01, ERRORMSG() = Primary key for table 't' is not unique : Primary key value ('1')
&lt;/pre&gt;&lt;br /&gt;
Some of the exceptions are expected: SQLCODE = -299 happens when the service is cancelled by the browser. The second column (365, 366, etc) shows the connection number so you can see how the executions overlap.&lt;br /&gt;
&lt;br /&gt;
The other exceptions are not expected: the SQLCODE = -193 primary key violations mean the DELETE / INSERT logic doesn't work properly when the Save button is given a vigorous workout.&lt;br /&gt;
&lt;br /&gt;
Here's another interesting observation: the "SQL error" doesn't reach the browser until after the primary key exception has been raised several times. How do we know that? Because once the browser displays "SQL error" there's no Save button any more, and that clearly wasn't the case: the repeated "s started..." messages prove there was a Save button on screen long after the first primary key exception.&lt;br /&gt;
&lt;br /&gt;
Locking, blocking, the atomic nature of transactions and the reentrant nature of SQL Anywhere executable code... none of that helps &lt;b&gt;this&lt;/b&gt; application avoid a showstopper exception.&lt;br /&gt;
&lt;br /&gt;
For extra points, explain &lt;b&gt;why&lt;/b&gt; it happens and &lt;b&gt;how&lt;/b&gt; to fix it.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-8024156893878469287?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/BNC9iMDnTFU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/BNC9iMDnTFU/lets-play-gotcha-acid-edition.html</link><author>noreply@blogger.com (Breck Carter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-RWa2grXVrQY/Tu9e46zoMbI/AAAAAAAABdw/HeDbOJ1bFsE/s72-c/Service%2Bs%2B800_428.jpg" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/lets-play-gotcha-acid-edition.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-4499390689410992072</guid><pubDate>Mon, 19 Dec 2011 08:01:00 +0000</pubDate><atom:updated>2011-12-19T15:27:58.084-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">latest EBFs</category><category domain="http://www.blogger.com/atom/ns#">announcement</category><title>Latest SQL Anywhere EBFs: 11.0.1.2724 and 12.0.1.3523 (revised)</title><description>&lt;p&gt;The three asterisks "***" show what's new since the &lt;a href="http://sqlanywhere.blogspot.com/2011/12/latest-sql-anywhere-ebfs-11012724.html" target="_blank"&gt;previous list&lt;/a&gt;. &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Only EBFs for the &lt;b&gt;latest fully-supported versions&lt;/b&gt; of SQL Anywhere are shown here: 10.0.1, 11.0.1 and 12.0.1. &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Note:&lt;/b&gt; Some earlier versions are also fully supported but they are not shown here; e.g., the 12.0.0.2758 EBF for Windows was just released. For more information see &lt;a href="http://www.sybase.com/detail?id=1002288" target="_blank"&gt;SQL Anywhere Supported Platforms and Engineering Support Status&lt;/a&gt;.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Also:&lt;/b&gt; Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more &lt;b&gt;new&lt;/b&gt; EBFs released. For a complete list of everything available for download see &lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Downloads - EBFs / Maintenance&lt;/a&gt;.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Current builds for the most active platforms...&lt;/a&gt;&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX Itanium    12.0.1.3523 EBF ***   15 Dec 2011 ***
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3777 EBF       14 Oct 2008

IBM AIX          12.0.1.3523 EBF ***   15 Dec 2011 ***
                 11.0.1.2724 EBF       12 Dec 2011

Linux x86/x64    12.0.1.3457 EBF       12 Oct 2011
                 11.0.1.2724 EBF ***   14 Dec 2011 ***
                 10.0.1.4239 EBF       17 Jun 2011

Mac OS           12.0.1.3352 EBF       26 May 2011
                 11.0.1.2449 EBF       29 Jun 2010
                 10.0.1.4042 EBF       01 Oct 2010

Solaris SPARC    12.0.1.3523 EBF ***   15 Dec 2011 ***
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.4239 EBF       17 Jun 2011

Solaris x64      12.0.1.3523 EBF ***   15 Dec 2011 ***
                 11.0.1.2724 EBF       12 Dec 2011
                 10.0.1.3870 EBF       01 Apr 2009

Windows x86/x64  12.0.1.3505 EBF       24 Nov 2011
                 11.0.1.2713 EBF       24 Nov 2011
                 10.0.1.4295 EBF       18 Nov 2011
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;] [&lt;a href="http://response.sybase.com/forms/FujiBetaTrial" target="_blank"&gt;Register for the Fuji Beta here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;...and for the other platforms:&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX PA-RISC    10.0.1.3778 EBF                 16 Oct 2008

Linux Itanium    10.0.1.3777 EBF                 14 Oct 2008

NetWare          10.0.1.4036 EBF                 10 Mar 2010

Windows CE       10.0.1.4036 EBF                 09 Mar 2010

Windows Itanium  10.0.1 GA Upgrade from 10.0.0   26 Apr 2007
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-4499390689410992072?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/yUHq66fvyZg" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/yUHq66fvyZg/latest-sql-anywhere-ebfs-11012724-linux.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/latest-sql-anywhere-ebfs-11012724-linux.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-6673949772951887419</guid><pubDate>Fri, 16 Dec 2011 08:01:00 +0000</pubDate><atom:updated>2011-12-16T03:01:06.505-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><title>Another tool for the box</title><description>&lt;p&gt;&lt;img src="http://2.bp.blogspot.com/-pKGNaoiYu00/TujzjXd66nI/AAAAAAAABdU/ucliE2lNbu0/s1600/apple-peeler-corer1_400_254.jpg" width="400" height="254" title="What is this thing?"&gt;&lt;br /&gt;
&lt;br /&gt;
Here's another simple question: &lt;br /&gt;
&lt;blockquote&gt;&lt;b&gt;Question:&lt;/b&gt; How do I select the top 10 rows in each partition of a table?"&lt;/blockquote&gt;Will &lt;a href="http://sqlanywhere.blogspot.com/2011/12/if-you-cant-join-em.html" target="_blank"&gt;CROSS APPLY work as well as it did before&lt;/a&gt;?&lt;br /&gt;
&lt;br /&gt;
Here's the data. The table looks pretty much the same, except that the entry_id is autoincrementing instead of repeating and there's a lot more data; three partitions with 100 rows in each partition:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t ( 
   partition_id     INTEGER NOT NULL,
   entry_id         INTEGER NOT NULL DEFAULT AUTOINCREMENT,
   data             VARCHAR ( 10 ) NOT NULL,
   PRIMARY KEY ( entry_id ) );

BEGIN
   DECLARE @loop_counter INTEGER;
   SET @loop_counter = 1;
   WHILE @loop_counter &lt;= 100 LOOP
      INSERT t ( partition_id, data ) VALUES ( 10, 'aaa' );
      INSERT t ( partition_id, data ) VALUES ( 20, 'bbb' );
      INSERT t ( partition_id, data ) VALUES ( 30, 'ccc' );
      SET @loop_counter = @loop_counter + 1;
   END LOOP;
   COMMIT;
END;

SELECT * FROM t ORDER BY entry_id;

partition_id,entry_id,data
10,1,'aaa'
20,2,'bbb'
30,3,'ccc'
10,4,'aaa'
20,5,'bbb'
30,6,'ccc'
10,7,'aaa'
20,8,'bbb'
30,9,'ccc'
...
partition_id,entry_id,data
10,298,'aaa'
20,299,'bbb'
30,300,'ccc'
&lt;/pre&gt;
Before tackling the "top 10" question, let's confirm that the CROSS APPLY answer to &lt;a href="http://sqlanywhere.blogspot.com/2011/12/if-you-cant-join-em.html" target="_blank"&gt;the previous question&lt;/a&gt; "How do I select the first row in each partition of a table?" works just as well on this table:
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT first_t.*
  FROM ( SELECT DISTINCT
                t.partition_id 
           FROM t
          ORDER BY t.partition_id ) AS unique_partition
  CROSS APPLY
       ( SELECT FIRST * 
           FROM t
          WHERE t.partition_id = unique_partition.partition_id
          ORDER BY t.entry_id ) AS first_t 
 ORDER BY first_t.partition_id;
&lt;/pre&gt;
Yes, it works:

&lt;pre&gt;&lt;blockquote&gt;partition_id,entry_id,data
10,1,'aaa'
20,2,'bbb'
30,3,'ccc'
&lt;/blockquote&gt;&lt;/pre&gt;
Plus, it is clear from the SQL what its purpose is:
&lt;ul&gt;&lt;li&gt;When the "( SELECT DISTINCT ... )" query is translated into English you get "select each distinct partition_id," &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;when the "( SELECT FIRST ... )" query is translated into English you get "select the first row by entry_id for each row in the other result set," and then&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;when the combined CROSS APPLY query is translated by combining the other two translations, you get "select the first row by entry_id for each distinct partition_id"&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;which is pretty much a restatement of the original question: "How do I select the first row in each partition?"

&lt;p&gt;It's not perfect; in a perfect world, every single query problem would be solved by a single SQL SELECT statement, not a combination of two queries. But, this is real life... and the original question did refer to two queries: "first row" and "each partition", and the CROSS APPLY contains exactly those two queries...

&lt;p&gt;Not like the &lt;a href="http://sqlanywhere.blogspot.com/2011/09/unrequited-olap.html" target="_blank"&gt;Old School solution&lt;/a&gt; which does funky things with MIN and GROUP BY, neither of which are even indirectly mentioned in the original question,
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT t.* 
  FROM t INNER JOIN ( SELECT partition_id     AS partition_id,
                             MIN ( entry_id ) AS min_entry_id
                        FROM t
                       GROUP BY partition_id
                    ) AS first_row
          ON t.partition_id = first_row.partition_id
         AND t.entry_id     = first_row.min_entry_id
 ORDER BY t.partition_id;
&lt;/pre&gt;or the &lt;a href="http://sqlanywhere.blogspot.com/2011/09/olap-counseling.html" target="_blank"&gt;OLAP solution&lt;/a&gt; which introduced the OVER, WINDOW, PARTITION BY and nested ORDER BY clauses plus the RANK function and a WHERE clause to deal with the RANK result.
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT partition_id,
       entry_id,
       data 
  FROM ( select partition_id, 
                entry_id, 
                data,
                rank ( ) OVER partition_id_window AS entry_rank
           FROM t
         WINDOW partition_id_window AS (
                   PARTITION BY partition_id
                   ORDER BY entry_id
                ) ) DT
 WHERE entry_rank = 1
 ORDER BY partition_id;
&lt;/pre&gt;Go ahead, translate either of those queries into English, see what you get!

&lt;p&gt;&lt;h3&gt;Back on topic...&lt;/h3&gt;
&lt;blockquote&gt;&lt;b&gt;Question:&lt;/b&gt; How do I select the top 10 rows in each partition of a table?"&lt;/blockquote&gt;
It turns out the CROSS APPLY solution scales just as well as the OLAP solution. With the OLAP solution, the "WHERE entry_rank = 1" predicate was simply changed to "entry_rank &lt;= 10", and with the CROSS APPLY the "FIRST" simply needs to be changed to "TOP 10".

&lt;p&gt;Once again, it works, and once again, the SQL matches the question: 

&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT first_t.*
  FROM ( SELECT DISTINCT
                t.partition_id 
           FROM t
          ORDER BY t.partition_id ) AS unique_partition
  CROSS APPLY
       ( SELECT TOP 10 * 
           FROM t
          WHERE t.partition_id = unique_partition.partition_id
          ORDER BY t.entry_id ) AS first_t 
 ORDER BY first_t.partition_id;

partition_id,entry_id,data
10,1,'aaa'
10,4,'aaa'
10,7,'aaa'
10,10,'aaa'
10,13,'aaa'
10,16,'aaa'
10,19,'aaa'
10,22,'aaa'
10,25,'aaa'
10,28,'aaa'
20,2,'bbb'
20,5,'bbb'
20,8,'bbb'
20,11,'bbb'
20,14,'bbb'
20,17,'bbb'
20,20,'bbb'
20,23,'bbb'
20,26,'bbb'
20,29,'bbb'
30,3,'ccc'
30,6,'ccc'
30,9,'ccc'
30,12,'ccc'
30,15,'ccc'
30,18,'ccc'
30,21,'ccc'
30,24,'ccc'
30,27,'ccc'
30,30,'ccc'
&lt;/pre&gt;
Sometimes "scalability" means "Will it handle millions of rows?", other times the definition is different: "Will it handle more complex problems as well as it handled simple ones?"

&lt;p&gt;For the second definition, at least so far, the answer for CROSS APPLY is "yes".

&lt;p&gt;&lt;img src="http://4.bp.blogspot.com/-6NZvoEu1RYc/Tujzsi9faGI/AAAAAAAABdg/LFQmfyMk56Y/s1600/apple-peeler-corer2_400_300.jpg" width="400" height="300" title="It's an apple peeler-corer!"&gt;

&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-6673949772951887419?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/Ct_EpXz1i1I" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/Ct_EpXz1i1I/another-tool-for-box.html</link><author>noreply@blogger.com (Breck Carter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-pKGNaoiYu00/TujzjXd66nI/AAAAAAAABdU/ucliE2lNbu0/s72-c/apple-peeler-corer1_400_254.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/another-tool-for-box.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-4911763678759815807</guid><pubDate>Wed, 14 Dec 2011 08:01:00 +0000</pubDate><atom:updated>2011-12-14T03:01:00.373-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL code</category><title>If you can't JOIN 'em, ...</title><description>&lt;p&gt;The question's simple, why should the answer be complicated?&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;&lt;b&gt;Question:&lt;/b&gt; How do I select the first row in each partition of a table?&lt;hr&gt;&lt;/blockquote&gt;Here's the data:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t ( 
   partition_id     INTEGER NOT NULL,
   entry_id         INTEGER NOT NULL,
   data             VARCHAR ( 10 ) NOT NULL,
   PRIMARY KEY ( partition_id, entry_id ) );

INSERT t VALUES ( 10, 1, 'aaa' );
INSERT t VALUES ( 10, 2, 'bbb' );
INSERT t VALUES ( 10, 3, 'ccc' );

INSERT t VALUES ( 20, 1, 'ppp' );
INSERT t VALUES ( 20, 2, 'qqq' );
INSERT t VALUES ( 20, 3, 'rrr' );

INSERT t VALUES ( 30, 1, 'xxx' );
INSERT t VALUES ( 30, 2, 'yyy' );
INSERT t VALUES ( 30, 3, 'zzz' );
&lt;/pre&gt;Here's what the result should look like:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT [the first row in each partition of t];

partition_id,entry_id,data
10,1,'aaa'
20,1,'ppp'
30,1,'xxx'
&lt;/pre&gt;If you didn't already know, could you tell what the &lt;b&gt;question&lt;/b&gt; was by reading the answers in these earlier articles?&lt;br /&gt;
&lt;br /&gt;
First, there was this &lt;a href="http://sqlanywhere.blogspot.com/2011/09/unrequited-olap.html" target="_blank"&gt;Old School solution&lt;/a&gt; using MIN and GROUP BY:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT t.* 
  FROM t INNER JOIN ( SELECT partition_id     AS partition_id,
                             MIN ( entry_id ) AS min_entry_id
                        FROM t
                       GROUP BY partition_id
                    ) AS first_row
          ON t.partition_id = first_row.partition_id
         AND t.entry_id     = first_row.min_entry_id
 ORDER BY t.partition_id;
&lt;/pre&gt;Second, there was an &lt;a href="http://sqlanywhere.blogspot.com/2011/09/olap-counseling.html" target="_blank"&gt;OLAP solution&lt;/a&gt; using RANK and PARTITION BY:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT partition_id,
       entry_id,
       data 
  FROM ( select partition_id, 
                entry_id, 
                data,
                rank ( ) OVER partition_id_window AS entry_rank
           FROM t
         WINDOW partition_id_window AS (
                   PARTITION BY partition_id
                   ORDER BY entry_id
                ) ) DT
 WHERE entry_rank = 1
 ORDER BY partition_id;
&lt;/pre&gt;Both get points for being obscure, neither look much like an answer to the question.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Let's Try Again&lt;/h3&gt;&lt;blockquote&gt;&lt;hr&gt;&lt;b&gt;Question:&lt;/b&gt; How do I select the first row in each partition of a table?&lt;hr&gt;&lt;/blockquote&gt;Let's start by breaking the question in two parts, and solving each part separately:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Question 1:&lt;/b&gt; How do I select all the unique values for partition_id?&lt;br /&gt;
&lt;br /&gt;
Answer: Use DISTINCT when selecting all the values of partition_id. &lt;br /&gt;
&lt;br /&gt;
There's no question what this code is doing, it's selecting all the unique values of partition_id just like the question asked:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT DISTINCT
       t.partition_id 
  FROM t
 ORDER BY t.partition_id;

partition_id
10
20
30
&lt;/pre&gt;&lt;b&gt;Question 2:&lt;/b&gt; How do I select the first row in a single partition?&lt;br /&gt;
&lt;br /&gt;
Answer: Use a WHERE clause to pick the partition, then FIRST and ORDER BY to select the first row. &lt;br /&gt;
&lt;br /&gt;
Once again, it's clear from reading the code what the question was:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT FIRST * 
  FROM t
 WHERE t.partition_id = 10
 ORDER BY t.entry_id;

partition_id,entry_id,data
10,1,'aaa'
&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;What now?&lt;/h3&gt;&lt;br /&gt;
Now comes the hard part: Combining those two result sets to get the answer to the original question.&lt;br /&gt;
&lt;br /&gt;
Let's try this:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT first_t.*
  FROM ( SELECT DISTINCT
                t.partition_id 
           FROM t
          ORDER BY t.partition_id ) AS unique_partition
  INNER JOIN
       ( SELECT FIRST * 
           FROM t
          WHERE t.partition_id = unique_partition.partition_id
          ORDER BY t.entry_id ) AS first_t 
 ORDER BY first_t.partition_id;
&lt;/pre&gt;You knew that wouldn't work, didn't you? INNER JOIN needs an ON clause:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;Could not execute statement.
There is no way to join 'unique_partition' to 'first_t'
SQLCODE=-146, ODBC 3 State="42000"
Line 1, column 1
&lt;/pre&gt;&lt;/blockquote&gt;Let's try the old-fashioned "comma join operator", it doesn't need an ON clause:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT first_t.*
  FROM ( SELECT DISTINCT
                t.partition_id 
           FROM t
          ORDER BY t.partition_id ) AS unique_partition,
       ( SELECT FIRST * 
           FROM t
          WHERE t.partition_id = unique_partition.partition_id
          ORDER BY t.entry_id ) AS first_t 
 ORDER BY first_t.partition_id;
&lt;/pre&gt;Different problem this time; you can't refer to a column in the left-hand side of a join in the right-hand side:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;Could not execute statement.
Illegal reference to correlation name 'unique_partition'
SQLCODE=-824, ODBC 3 State="42S02"
Line 1, column 1
&lt;/pre&gt;&lt;/blockquote&gt;One last try; let's move the WHERE clause to an ON clause so INNER JOIN will work:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT first_t.*
  FROM ( SELECT DISTINCT
                t.partition_id 
           FROM t
          ORDER BY t.partition_id ) AS unique_partition
  INNER JOIN
       ( SELECT FIRST * 
           FROM t
          ORDER BY t.entry_id ) AS first_t 
  ON first_t.partition_id = unique_partition.partition_id 
 ORDER BY first_t.partition_id;
&lt;/pre&gt;Yeah, it "works" all right, but it only returns one row instead of three; the predicate really needs to be inside the inner SELECT, not the outer one:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;partition_id,entry_id,data
10,1,'aaa'
&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;h3&gt;You've seen that before, haven't you?&lt;/h3&gt;&lt;br /&gt;
Let's be honest, you've seen those error messages and wrong results before, haven't you? Like when you were trying to solve a problem that &lt;b&gt;should&lt;/b&gt; have been easy (like this one) but you had to fiddle around with complex subqueries or temporary tables or even (gasp!) cursor fetch loops, and you ended up with a solution that looked like it was designed for a whole different problem.&lt;br /&gt;
&lt;br /&gt;
It turns out there &lt;b&gt;is&lt;/b&gt; a join operator that makes both of those error messages go away:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;it &lt;b&gt;doesn't&lt;/b&gt; need an ON clause, and &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;it &lt;b&gt;does&lt;/b&gt; allow references from the right-hand side back to the left-hand side.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;h3&gt;It's called CROSS APPLY...&lt;/h3&gt;&lt;br /&gt;
...and it looks like this:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;SELECT first_t.*
  FROM ( SELECT DISTINCT
                t.partition_id 
           FROM t
          ORDER BY t.partition_id ) AS unique_partition
  CROSS APPLY
       ( SELECT FIRST * 
           FROM t
          WHERE t.partition_id = unique_partition.partition_id
          ORDER BY t.entry_id ) AS first_t 
 ORDER BY first_t.partition_id;

partition_id,entry_id,data
10,1,'aaa'
20,2,'bbb'
30,3,'ccc'
&lt;/pre&gt;CROSS APPLY is like the comma-join and CROSS JOIN operators in that it doesn't need (and cannot have) an ON clause. It is different from other join operators in that predicates on the right-hand side can refer to columns on the left-hand side.&lt;br /&gt;
&lt;br /&gt;
So, like comma-join and CROSS JOIN, the CROSS APPLY joins every single row on the left-hand side with every single row on the right-hand side...&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;but...&lt;/h3&gt;&lt;br /&gt;
...exactly what constitutes "every single row on the right-hand side" depends on each row on the left-hand side.&lt;br /&gt;
&lt;br /&gt;
In this case, for each row on the left-hand side, the set consisting of "every single row on the right-hand side" is boiled down to one single row, the first row with matching partition_id.&lt;br /&gt;
&lt;br /&gt;
So, CROSS APPLY is like INNER JOIN with special powers.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
References:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://iablog.sybase.com/paulley/2008/07/cross-and-outer-apply/" target="_blank"&gt;Glenn Paulley's article on cross and outer apply and how they differ from LATERAL&lt;/a&gt; &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;a href="http://toonbarn.com/other-cartoons/disney-and-pixars-the-incredibles-review/" target="_blank"&gt;&lt;img src="http://toonbarn.com/wordpress/wp-content/uploads/2011/04/Mr-Incredible.jpg" width="575" height="431" title="Mr. Incredible"&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/li&gt;

&lt;li&gt;&lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/from-statement.html" target="_blank"&gt;The FROM clause topic in the Help&lt;/a&gt;&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="http://dcx.sybase.com/index.html#1201/en/dbusage/apply-joins-joinsasp.html" target="_blank"&gt;CROSS APPLY examples in the Help&lt;/a&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-4911763678759815807?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/-ZJQ4Piqgl4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/-ZJQ4Piqgl4/if-you-cant-join-em.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>3</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/if-you-cant-join-em.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-6905682458494917120</guid><pubDate>Tue, 13 Dec 2011 15:16:00 +0000</pubDate><atom:updated>2011-12-13T10:16:57.797-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">latest EBFs</category><category domain="http://www.blogger.com/atom/ns#">announcement</category><title>Latest SQL Anywhere EBFs: 11.0.1.2724</title><description>&lt;p&gt;The three asterisks "***" show what's new since the &lt;a href="http://sqlanywhere.blogspot.com/2011/11/latest-sql-anywhere-ebfs-12013505-and.html" target="_blank"&gt;previous list&lt;/a&gt;. Only EBFs for the three fully-supported versions of SQL Anywhere are shown here: 10.0.1, 11.0.1 and 12.0.1.&lt;br /&gt;
&lt;h3&gt;&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Current builds for the most active platforms...&lt;/a&gt;&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX Itanium    12.0.1.3469 EBF       14 Oct 2011
                 11.0.1.2724 EBF ***   12 Dec 2011 ***
                 10.0.1.3777 EBF       14 Oct 2008

IBM AIX          12.0.1.3469 EBF       20 Oct 2011
                 11.0.1.2724 EBF ***   12 Dec 2011 ***

Linux x86/x64    12.0.1.3457 EBF       12 Oct 2011
                 11.0.1.2700 EBF       27 Oct 2011
                 10.0.1.4239 EBF       17 Jun 2011

Mac OS           12.0.1.3352 EBF       26 May 2011
                 11.0.1.2449 EBF       29 Jun 2010
                 10.0.1.4042 EBF       01 Oct 2010

Solaris SPARC    12.0.1.3469 EBF       20 Oct 2011
                 11.0.1.2724 EBF ***   12 Dec 2011 ***
                 10.0.1.4239 EBF       17 Jun 2011

Solaris x64      11.0.1.2724 EBF ***   12 Dec 2011 ***
                 10.0.1.3870 EBF       01 Apr 2009

Windows x86/x64  12.0.1.3505 EBF       24 Nov 2011
                 11.0.1.2713 EBF       24 Nov 2011
                 10.0.1.4295 EBF       18 Nov 2011
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;] [&lt;a href="http://response.sybase.com/forms/FujiBetaTrial" target="_blank"&gt;Register for the Fuji Beta here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;...and for the other platforms:&lt;/h3&gt;&lt;pre style="line-height:1.2em"&gt;HP-UX PA-RISC    10.0.1.3778 EBF                 16 Oct 2008

Linux Itanium    10.0.1.3777 EBF                 14 Oct 2008

NetWare          10.0.1.4036 EBF                 10 Mar 2010

Windows CE       10.0.1.4036 EBF                 09 Mar 2010

Windows Itanium  10.0.1 GA Upgrade from 10.0.0   26 Apr 2007
&lt;/pre&gt;[&lt;a href="http://downloads.sybase.com/swd/base.do?client=support" target="_blank"&gt;Download EBFs here&lt;/a&gt;]&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-6905682458494917120?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/9aCtb5dUjAI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/9aCtb5dUjAI/latest-sql-anywhere-ebfs-11012724.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/latest-sql-anywhere-ebfs-11012724.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-8517402705670260126</guid><pubDate>Fri, 09 Dec 2011 17:04:00 +0000</pubDate><atom:updated>2011-12-09T15:40:32.635-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">performance and tuning</category><title>Product Suggestion: Minimum Row Size</title><description>&lt;p&gt;&lt;b&gt;Question:&lt;/b&gt; Have you ever set PCTFREE for a table?&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Likely Answer Number 1:&lt;/b&gt; &lt;i&gt;"Never."&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Likely Answer Number 2:&lt;/b&gt; &lt;i&gt;"Don't think so, can't remember."&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Likely Answer Number 3:&lt;/b&gt; &lt;i&gt;"I set it to 100 once, can't remember why."&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://1.bp.blogspot.com/-NKyLLPWex4I/TuI7c4BF59I/AAAAAAAABdI/eRbwF2sQ0hg/s1600/knob.jpg" width="400" height="301" title="knob"&gt;&lt;br /&gt;
&lt;br /&gt;
As SQL Anywhere performance tuning knobs go, the PCTFREE setting is one of most obscure and least used. It's been around forever, but nobody ever uses it, probably because nobody can figure out how to use it properly.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;What the %#!@ is PCTFREE?&lt;/h3&gt;&lt;br /&gt;
Here's a code example that sets PCTFREE to 50% for table t:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t
   pkey    INTEGER         NOT NULL DEFAULT AUTOINCREMENT PRIMARY KEY,
   data    VARCHAR ( 200 ) NOT NULL DEFAULT '',
   PCTFREE 50 );
&lt;/pre&gt;Here's &lt;a href="http://dcx.sybase.com/index.html#1201/en/dbreference/create-table-statement.html" target="_blank"&gt;what the Help says&lt;/a&gt;:&lt;br /&gt;
&lt;blockquote&gt;&lt;hr&gt;&lt;b&gt;PCTFREE clause&lt;/b&gt; - Specifies the percentage of free space you want to reserve for each table page. The free space is used if rows increase in size when the data is updated. If there is no free space in a table page, every increase in the size of a row on that page requires the row to be split across multiple table pages, causing row fragmentation and possible performance degradation. &lt;br /&gt;
&lt;br /&gt;
The value percent-free-space is an integer between 0 and 100. The former value specifies that no free space is to be left on each page—each page is to be fully packed. A high value causes each row to be inserted into a page by itself. If PCTFREE is not set, or is later dropped, the default PCTFREE value is applied according to the database page size (200 bytes for a 4 KB (and up) page size). The value for PCTFREE is stored in the ISYSTAB system table.&lt;hr&gt;&lt;/blockquote&gt;&lt;br /&gt;
Just what should PCTFREE be set to? Is 50% good? Is 40% better, or is 60%?&lt;br /&gt;
&lt;br /&gt;
It doesn't help that PCTFREE is a percentage but the default is expressed in bytes... I think you can convert, but who cares? Is 200 bytes good? Or 100? Or 400? ...the answer is the same, "I don't know."&lt;br /&gt;
&lt;br /&gt;
The problem is this: When designing a new cargo ship / warehouse / database, folks generally know how many tonnes / cubic feet / bytes it should store; i.e., the capacity, not the free space. &lt;br /&gt;
&lt;br /&gt;
Taken down to the database page level, folks know (or can figure out) how big the average row is when it's inserted, and how big it will be after it's been updated. What they don't know is how to convert that to an efficient amount of free space to leave after inserting.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Why should we care?&lt;/h3&gt;&lt;br /&gt;
It is a fairly common practice to insert empty rows and then update them later when actual data become available... easy is good.&lt;br /&gt;
&lt;br /&gt;
Once inserted, a row can never be moved to a different page, so when a page becomes full of empty rows the first UPDATE that expands a column will split that row... splits are bad, hence PCTFREE.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Here's a thought...&lt;/h3&gt;&lt;br /&gt;
...instead of (or in addition to) PCTFREE why not give the users a knob called MINROWSIZE which they can calculate as expected average row size after the row has been updated, and is to be used by SQL Anywhere as the minimum amount of table data space to be reserved for each row when it is inserted?&lt;br /&gt;
&lt;br /&gt;
In this case, MINROWSIZE 25 means reserve 4 bytes for the integer and about 20 bytes for the string even though the INSERT is providing an empty row:&lt;br /&gt;
&lt;!-- ++++++++++++++++++++++++++++++ --&gt;&lt;b&gt;&lt;/b&gt;&lt;pre class="brush: text"&gt;CREATE TABLE t
   pkey    INTEGER         NOT NULL DEFAULT AUTOINCREMENT PRIMARY KEY,
   data    VARCHAR ( 200 ) NOT NULL DEFAULT '',
   MINROWSIZE 25 );

INSERT t VALUES ( DEFAULT, DEFAULT );
&lt;/pre&gt;Currently, when a new row is inserted the PCTFREE amount is compared to the current free space in the page when deciding if a new row will fit. The size of each existing row and whether or not it is expected to grow in size is not taken into account, just the total space used and the PCTFREE amount, so depending of the current state of affairs inside the page the ideal PCTFREE could be larger or smaller than the actual value.&lt;br /&gt;
&lt;br /&gt;
With MINROWSIZE, SQL Anywhere would take into account existing rows that have still not used up their MINROWSIZE allocation, as well as the MINROWSIZE allocation for the new row, when making the decision.&lt;br /&gt;
&lt;br /&gt;
With some effort, the user could provide a MINROWSIZE value that is meaningful and accurate... as opposed to picking the number for Lotto PCTFREE.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-8517402705670260126?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/dqO5IITNIzw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/dqO5IITNIzw/product-suggestion-minimum-row-size.html</link><author>noreply@blogger.com (Breck Carter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-NKyLLPWex4I/TuI7c4BF59I/AAAAAAAABdI/eRbwF2sQ0hg/s72-c/knob.jpg" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/product-suggestion-minimum-row-size.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-1745212852580452468</guid><pubDate>Wed, 07 Dec 2011 08:01:00 +0000</pubDate><atom:updated>2011-12-07T03:01:02.329-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">announcement</category><category domain="http://www.blogger.com/atom/ns#">Foxhound</category><title>A Walking Tour of the Foxhound FAQ</title><description>&lt;p&gt;Is there anything duller than documentation?&lt;br /&gt;
&lt;br /&gt;
Let's see... how about reading Frequently Asked Questions?&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Even worse?&lt;/h3&gt;&lt;br /&gt;
How about a walking tour through some new FAQs? Not the big shiny &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-Whats-new-in-Foxhound-1-2.html" target="_blank"&gt;What's New in Foxhound 1.2&lt;/a&gt; entries, but the more obscure ones, like this one:&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-What-environment-variables-are-used-by-Foxhound.html" target="_blank"&gt;What environment variables are used by Foxhound?&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
FOXHOUND1 is the all-important "Foxhound drive-and-path" used by all the Foxhound shortcuts... it's Foxhound's version of %SQLANY12%, if you lose it you'll probably want to set it up again.&lt;br /&gt;
&lt;br /&gt;
FOXHOUND1BIN is the esoteric variable you can set to force Foxhound to start up using the 32-bit version of SQL Anywhere if you have both versions installed... "esoteric" because you almost never need it, Foxhound runs just fine using the 64-bit version. But, if you need it, &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-start-Foxhound-with-the-32-bit-version-of-SQL-Anywhere-when-both-the-32-bit-and-64-bit-versions-of-SQL-Anywhere-are-installed.html" target="_blank"&gt;here's how to use FOXHOUND1BIN&lt;/a&gt; (no, you don't need it if only the 32-bit version of SQL Anywhere is installed, Foxhound will use that one automatically).&lt;br /&gt;
&lt;br /&gt;
FOXHOUND1UPGRADE refers to the variable that controls how much data the "post-setup" process will copy from your old Foxhound database when you are upgrading to a new version. The environment variable version of FOXHOUND1UPGRADE sets the initial value, but it defaults to "ALL" and is easy to change during the installation process, so it is rarely used.... the environment variable is rarely used, that is, but the underlying value is very important.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Ooops...&lt;/h3&gt;&lt;br /&gt;
...wait a minute, &lt;b&gt;that&lt;/b&gt; wasn't a FAQ, that was three paragraphs &lt;b&gt;about&lt;/b&gt; a FAQ. To experience the full measure of dullness you have to follow the links, this article is just a travelogue.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
Speaking of FOXHOUND1UPGRADE, this FAQ tells the full story: &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-the-different-FOXHOUND1UPGRADE-values-work.html" target="_blank"&gt;How do the different FOXHOUND1UPGRADE values work?&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
There's a lot of logic behind upgrading... it's a big deal if you have a big database, especially if you don't need all the data any more. You can let &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_options.html#purge_process" target="_blank"&gt;the purge process&lt;/a&gt; slowly delete it over time, or you can let the upgrade process quickly delete-it-by-not-copying it, so "upgrading" to the same version of Foxhound suddenly makes sense: &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-shrink-the-size-of-the-Foxhound-database.html" target="_blank"&gt;How do I shrink the size of the Foxhound database?&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
An "alias" feature has been added to process that compiles the FAQ so different questions can point to the same answer. For example, this somewhat obscure question "&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-measure-latency-and-throughput.html" target="_blank"&gt;How can the Heartbeat time be larger than the Sample time?&lt;/a&gt;" is answered as part of this longer explanation "&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-measure-latency-and-throughput.html" target="_blank"&gt;How do I measure latency and throughput?&lt;/a&gt;"&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
The FAQ alias feature is most useful in the &lt;a href="http://www.risingroad.com/foxhound/faq/index.html#Messages" target="_blank"&gt;alphanumeric Messages section&lt;/a&gt; where messages containing multiple phrases and numeric codes appear repeatedly in the list. Quite a few messages have two or three entries pointing to the same explanation; this one's the winner with 4 appearances in the sorted list:&lt;br /&gt;
&lt;blockquote&gt;&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-I-O-error-Fatal-error-A-disk-read-from-file-foxhound1-db-failed-with-error-code-23-transaction-rolled-back.html" target="_blank"&gt;23: I/O error Fatal error: A disk read from file "...\foxhound1.db" failed with error code: (23) -- transaction rolled back --&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-I-O-error-Fatal-error-A-disk-read-from-file-foxhound1-db-failed-with-error-code-23-transaction-rolled-back.html" target="_blank"&gt;A disk read from file "...\foxhound1.db" failed with error code: (23) -- transaction rolled back --&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-I-O-error-Fatal-error-A-disk-read-from-file-foxhound1-db-failed-with-error-code-23-transaction-rolled-back.html" target="_blank"&gt;I/O error Fatal error: A disk read from file "...\foxhound1.db" failed with error code: (23) -- transaction rolled back --&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-I-O-error-Fatal-error-A-disk-read-from-file-foxhound1-db-failed-with-error-code-23-transaction-rolled-back.html" target="_blank"&gt;transaction rolled back: I/O error Fatal error: A disk read from file "...\foxhound1.db" failed with error code: (23)&lt;/a&gt;&lt;br /&gt;
&lt;/blockquote&gt;&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
Even more useful than the alias feature is the new Google "Custom Search Engine" link that appears on every single FAQ page:&lt;br /&gt;
&lt;br /&gt;
&lt;div id="csex" style="width: 50%;"&gt;Loading&lt;/div&gt;&lt;script src="http://www.google.com/jsapi" type="text/javascript"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;script type="text/javascript"&gt; 
  google.load('search', '1', {language : 'en'});
  google.setOnLoadCallback(function() {
    var customSearchControl = new google.search.CustomSearchControl('016640858164961096467:khteiivkume');
    customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);
    var options = new google.search.DrawOptions();
    options.enableSearchboxOnly("http://google.com/cse?cx=016640858164961096467:khteiivkume", null, true);    
    customSearchControl.draw('csex', options);  }, true);&lt;/script&gt;Go ahead, give it a whirl, type in "adhoc" and do a search.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
Speaking of adhoc reporting, it's not new with Foxhound 1.2 but it might as well be; it wasn't &lt;b&gt;documented&lt;/b&gt; until now:&lt;br /&gt;
&lt;blockquote&gt;&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-run-adhoc-queries-on-the-Foxhound-database.html" target="_blank"&gt;How do I run adhoc queries on the Foxhound database?&lt;/A&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-see-the-schema-for-adhoc-reporting.html" target="_blank"&gt;How do I see the schema for adhoc reporting?&lt;/A&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-change-the-ADHOC-password.html" target="_blank"&gt;How do I change the ADHOC password?&lt;/A&gt;&lt;br /&gt;
&lt;/blockquote&gt;Plus, a bunch of new columns were added, and new views, to make queries easier; after all, it's all &lt;b&gt;your&lt;/b&gt; data!&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
Here's one of the shorter FAQs:&lt;br /&gt;
&lt;blockquote&gt;&lt;b&gt;Question:&lt;/b&gt; &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-backup-my-Foxhound-database.html" target="_blank"&gt;How do I backup my Foxhound database?&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Answer:&lt;/b&gt; Use one or both of the Foxhound shortcuts:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre&gt;All Programs 
   - Foxhound1 
      - Tools 
         - Backup Foxhound Database 
         - Backup Foxhound Transaction Log
&lt;/pre&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;The second shortcut, Backup Foxhound Transaction Log, is new to Foxhound 1.2.&lt;br /&gt;
&lt;br /&gt;
So is this explanation&lt;br /&gt;
&lt;blockquote&gt;&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-the-Foxhound-backup-shortcuts-work.html" target="_blank"&gt;How do the Foxhound backup shortcuts work?&lt;/a&gt;&lt;br /&gt;
&lt;/blockquote&gt;plus, at long last, an explanation of what to do with all those backups:&lt;br /&gt;
&lt;blockquote&gt;&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-restore-the-Foxhound-database-from-a-backup.html" target="_blank"&gt;How do I restore the Foxhound database from a backup?&lt;/a&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
Some folks like the context-sensitive Help frame on the right side of the Foxhound display, some folks hate it, but one thing &lt;b&gt;everyone&lt;/b&gt; hated was the "Hide Help / Show Help" buttons when they didn't work. &lt;br /&gt;
&lt;br /&gt;
Now those buttons are gone, and if you want to &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-hide-and-show-the-Help-for-Foxhound.html" target="_blank"&gt;turn the Help frame on and off&lt;/a&gt; you have to check the "Show Help" box on the Foxhound Options page.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
If there was a contest for "Shortest FAQ" this would be a another contestant:&lt;br /&gt;
&lt;blockquote&gt;&lt;b&gt;Question:&lt;/b&gt; &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-set-up-Foxhound-to-monitor-a-large-number-of-target-databases.html" target="_blank"&gt;How do I set up Foxhound to monitor a large number of target databases?&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Answer:&lt;/b&gt; See &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_monitor_options.html#options7" target="_blank"&gt;Section 7. Manage Multiple Monitor Sessions&lt;/a&gt; on the Monitor Options page.&lt;br /&gt;
&lt;/blockquote&gt;It used to be an endless affair starting and stopping 100 sampling sessions: click, click, click, &lt;i&gt;[curse]&lt;/i&gt;, click, click, click, &lt;i&gt;[curse]&lt;/i&gt;, ...&lt;br /&gt;
&lt;br /&gt;
Now it's a matter of setting up a text file with all the connection strings, then clicking on one button to start them all, another to stop them all, with nothing stopping you from starting and stopping them individually like before.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
And finally, we have the answer to this important question: &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-much-overhead-does-the-Foxhound-connection-add-to-the-target-database.html" target="_blank"&gt;How much overhead does the Foxhound connection add to the target database?&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Here's an excerpt, possibly a candidate for Quote Of The Day:&lt;br /&gt;
&lt;blockquote&gt;"Generally speaking, if Foxhound's overhead causes any problems, the target database is already in serious difficulty for other reasons."&lt;br /&gt;
&lt;/blockquote&gt;That's a true statement, not just for Foxhound but for any performance monitor product: If monitoring performance hurts performance, there is already a problem that needs to be dealt with.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-1745212852580452468?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/vd0t0Apo0v4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/vd0t0Apo0v4/walking-tour-of-foxhound-faq.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/walking-tour-of-foxhound-faq.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-8243336716677178570</guid><pubDate>Mon, 05 Dec 2011 08:01:00 +0000</pubDate><atom:updated>2011-12-05T03:01:00.661-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">announcement</category><category domain="http://www.blogger.com/atom/ns#">Foxhound</category><title>Foxhound 1.2 Is Now Available</title><description>&lt;p&gt;Foxhound is a browser-based health, availability and performance monitor for SQL Anywhere that displays everything you need to know at a glance.&lt;br /&gt;
&lt;h3&gt;... and it's FREE! (to upgrade, that is)&lt;/h3&gt;&lt;br /&gt;
If you already have version 1.0 or 1.1, Foxhound 1.2 is &lt;A HREF="http://www.risingroad.com/foxhound/index.html" TITLE="Foxhound 1.2" target="_blank"&gt;now available as a free upgrade&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
If not, you can &lt;A HREF="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-get-a-copy-of-Foxhound.html" target="_blank"&gt;get an evaluation copy or buy a copy here&lt;/a&gt;.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Here's &lt;A HREF="http://www.risingroad.com/foxhound/index.html" TITLE="Foxhound 1.2" target="_blank"&gt;the download&lt;/a&gt;,&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;&lt;li&gt;here's &lt;A HREF="http://www.risingroad.com/foxhound/faq/index.html" TITLE="the FAQ" target="_blank"&gt;the FAQ&lt;/a&gt;, &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;&lt;li&gt;here are &lt;A HREF="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_contents.html" TITLE="the Docs" target="_blank"&gt;the Docs&lt;/a&gt;, &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;&lt;li&gt;here are the basic &lt;A HREF="http://www.risingroad.com/foxhound/index.html#features" TITLE="the Features" target="_blank"&gt;Features&lt;/a&gt;, and&lt;br /&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Here's the What's New . . .&lt;/h3&gt;&lt;br /&gt;
Now it's easier to manage up to 100 Monitor sessions:&lt;ul&gt;&lt;li&gt;The "Alerts Criteria" page has been redesigned as the "Monitor Options" page.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_monitor_options.html" target="_blank"&gt;The Monitor Options Page&lt;/a&gt;.&lt;/blockquote&gt;&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;The Default Settings can now be edited apart from the options for any particular target database.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_monitor_options.html#switch" target="_blank"&gt;Monitor Options - Switch Display&lt;/a&gt;.&lt;/blockquote&gt;&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;The new Force Default Settings On All Targets button on the Monitor Options page lets you "push" the default settings to all the targets, all at once. Previously, you could only "pull" the settings to each target one at a time with the Restore Default Settings button.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_monitor_options.html#defaults" target="_blank"&gt;Save And Restore Alerts Criteria Defaults&lt;/a&gt;.&lt;/blockquote&gt;&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;The Manage Multiple Monitor Sessions feature lets you specify batches of target databases, and the Start and Stop All Sessions buttons let you turn sampling on and off for each batch.&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_monitor_options.html#options7" target="_blank"&gt;Manage Multiple Monitor Sessions&lt;/a&gt;.&lt;/blockquote&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;hr&gt;The Monitor tab on the Foxhound Menu page is now easier to use, and more informative:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;The lists of targets and outstanding Alerts have been vertically squished so you don't have to scroll down so far when you've got lot of targets.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;The Disable Refresh and Enable Refresh buttons make it easier to scroll down to look at something.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;More "at a glance" columns have been added:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Active Alerts count, with a link down into the Alerts list&lt;br /&gt;
&lt;/li&gt;

&lt;li&gt;Conns&lt;br /&gt;
&lt;/li&gt;

&lt;li&gt;Waiting Req&lt;br /&gt;
&lt;/li&gt;

&lt;li&gt;Locks Held, Blocked Conns&lt;br /&gt;
&lt;/li&gt;

&lt;li&gt;CPU Time&lt;br /&gt;
&lt;/li&gt;

&lt;li&gt;Temp Space&lt;br /&gt;
&lt;/li&gt;

&lt;li&gt;Disk Reads, Writes&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;The Start All Sampling and Stop All Sampling buttons let you turn sampling on and off for all the targets.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;See: &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_menu.html#monitor" target="_blank"&gt;The Monitor Tab&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;Scrolling through gigabytes of data on the History page used to be slowwwww, like continental drift, now it's faster.&lt;br /&gt;
&lt;br /&gt;
Plus, new scroll amounts have been added (500 samples and 3 hours):&lt;pre style="line-height:1.2em"&gt;« Newest « 500 « 100 « 20 « 1 sample         1 sample » 20 » 100 » 500 » Oldest » 
« Month « Week « Day « 3 Hours « 1 Hour   1 Hour » 3 Hours » Day » Week » Month »&lt;/pre&gt;&lt;br /&gt;
...and, the "Freeze Frame Heights" button lets you resize and freeze the History page frames so they don't reset (&lt;i&gt;"Grrr!"&lt;/i&gt;) every time you scroll:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;The database-level samples frame, &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;the blocked connections frame and &lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;the current connections frame.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;See: &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_history.html" target="_blank"&gt;The History Page&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;It's &lt;b&gt;your&lt;/b&gt; data; now it's easier to run adhoc queries:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;New views have been added for adhoc reporting.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;A separate read-only "Adhoc Schema" database lets you see what the views and underlying tables look like.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;New connection-level columns have been added to make queries easier to write:&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;LockRowID                         BIGINT NULL
blocked_connection_count          BIGINT NOT NULL DEFAULT 0
current_req_status                VARCHAR ( 100 ) NOT NULL DEFAULT ''             
cache_satisfaction                DECIMAL ( 30, 0 ) NOT NULL DEFAULT 0.0 
time_connected                    BIGINT NOT NULL DEFAULT 0
total_waits                       BIGINT NOT NULL DEFAULT 0 
waiting_time                      DECIMAL ( 30, 6 ) NOT NULL DEFAULT 0.0
transaction_running_time          BIGINT NOT NULL DEFAULT 0
time_since_last_request           BIGINT NOT NULL DEFAULT 0
index_satisfaction                DECIMAL ( 30, 0 ) NOT NULL DEFAULT 0.0&lt;/pre&gt;&lt;/blockquote&gt;Except for LockRowID, all the new columns contain values that Foxhound used to calculate only when the data was displayed on the Monitor and History pages; now these values are calculated once and stored permanently.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;See: &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-run-adhoc-queries-on-the-Foxhound-database.html" target="_blank"&gt;How do I run adhoc queries on the Foxhound database?&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;Now you can use Gmail to send Alert emails as well as receive them.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-tell-Foxhound-to-send-Alert-emails-via-the-Gmail-SMTP-server-smtp-gmail-com.html" target="_blank"&gt;How do I tell Foxhound to send Alert emails via the Gmail SMTP server smtp.gmail.com?&lt;/a&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;hr&gt;The data upgrade process now runs faster...&lt;br /&gt;
&lt;br /&gt;
...as well as giving you control over how much data to upgrade:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;The default is FOXHOUND1UPGRADE = ALL to copy all the data from your old Foxhound database to the new one.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;If you choose FOXHOUND1UPGRADE = OPTIONS during the installation process, Foxhound will copy everything &lt;b&gt;except&lt;/b&gt; the Monitor samples. When you start Foxhound again, all the old sample data will be gone &lt;b&gt;but&lt;/b&gt; sampling will start again right away (assuming it was running before you installed the new version).&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;If you want to save the Monitor samples recorded since a particular date, specify FOXHOUND1UPGRADE = yyyymmmdd.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;To save the samples recorded during the past n days, use FOXHOUND1UPGRADE = n.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;
&lt;/ul&gt;What's &lt;b&gt;really&lt;/b&gt; cool about the new upgrade process is you can use it to purge, shrink and reorganize the Foxhound database: just reinstall the same version of Foxhound with FOXHOUND1UPGRADE = yyyymmmdd or FOXHOUND1UPGRADE = n.&lt;br /&gt;
&lt;p&gt;See:&lt;br /&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-the-different-FOXHOUND1UPGRADE-values-work.html" target="_blank"&gt;How do the different FOXHOUND1UPGRADE values work?&lt;/a&gt;&lt;br /&gt;
&lt;p&gt;&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-shrink-the-size-of-the-Foxhound-database.html" target="_blank"&gt;How do I shrink the size of the Foxhound database?&lt;/a&gt;&lt;br /&gt;
&lt;/blockquote&gt;&lt;br /&gt;
&lt;hr&gt;Good news, bad news:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Foxhound still supports all versions of SQL Anywhere from 5.5 through 12.0.1.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;Foxhound itself now runs on SQL Anywhere Version 12.0.1.&lt;br /&gt;
&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;Foxhound will use the 64-bit version of SQL Anywhere 12.0.1 if it is installed. Previously, Foxhound ran only on the 32-bit version of SQL Anywhere 11.0.1.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-tell-if-Foxhound-is-running-on-the-32-bit-or-64-bit-version-of-SQL-Anywhere.html" target="_blank"&gt;How do I tell if Foxhound is running on the 32-bit or 64-bit version of SQL Anywhere?&lt;/a&gt;&lt;/blockquote&gt;&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;You can force Foxhound to use the 32-bit version of SQL Anywhere by setting the FOXHOUND1BIN environment variable to Bin32... sometimes, it's just easier to do that than to try to figure out what's going wrong with ODBC and a legacy target database.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-start-Foxhound-with-the-32-bit-version-of-SQL-Anywhere-when-both-the-32-bit-and-64-bit-versions-of-SQL-Anywhere-are-installed.html" target="_blank"&gt;How do I start Foxhound with the 32-bit version of SQL Anywhere when both the 32-bit and 64-bit versions of SQL Anywhere are installed?&lt;/a&gt;&lt;/blockquote&gt;&lt;p style="clear: both;"&gt;&lt;/li&gt;

&lt;li&gt;Bad news: Foxhound &lt;b&gt;requires&lt;/b&gt; SQL Anywhere 12.0.1.3298 or later to run. And it still only runs on Windows. But none of that affects your target database: as long as Foxhound can establish an ODBC connection to your database, it can monitor the performance and display the schema.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_introduction.html#requirements" target="_blank"&gt;Requirements&lt;/a&gt;.&lt;/blockquote&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;hr&gt;The look-and-feel of the Foxhound Options page has changed to match the new improved Monitor Options window.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_options.html" target="_blank"&gt;The Foxhound Options Page&lt;/a&gt;.&lt;/blockquote&gt;&lt;br /&gt;
&lt;hr&gt;A single global "Enable Emails" setting has been added to the Foxhound Options page in case you want to turn off an accidental "alert storm".&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/foxhound1_help/foxhound_options.html#global_overrides" target="_blank"&gt;Foxhound Options - Global Overrides&lt;/a&gt;.&lt;/blockquote&gt;&lt;br /&gt;
&lt;hr&gt;New shortcuts have been added:&lt;blockquote&gt;&lt;pre style="line-height:1.2em"&gt;start - All Programs 
   Foxhound1
      Start Foxhound via Chrome
      Tools 
         Backup Foxhound Transaction Log
         Start Foxhound via Chrome - debug
&lt;/pre&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;blockquote&gt;See:&lt;br /&gt;
&lt;blockquote&gt;&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-start-Foxhound.html" target="_blank"&gt;How do I start Foxhound?&lt;/a&gt;&lt;br /&gt;
&lt;p&gt;&lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-backup-my-Foxhound-database.html" target="_blank"&gt;How do I backup my Foxhound database?&lt;/a&gt;&lt;br /&gt;
&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;hr&gt;Exception handling has been improved to allow Foxhound to continue operating after receiving bad data from a target database.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-report-unexpected-errors-and-exceptions.html" target="_blank"&gt;How do I report unexpected errors and exceptions?&lt;/a&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;hr&gt;The thirty-day Evaluation period can now be extended: just get a Evaluation Renewal key from breck.carter@gmail.com.&lt;br /&gt;
&lt;blockquote&gt;See: &lt;a href="http://www.risingroad.com/foxhound/faq/FAQ-How-do-I-get-a-copy-of-Foxhound.html" target="_blank"&gt;How do I get a copy of Foxhound?&lt;/a&gt;&lt;/blockquote&gt;&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-8243336716677178570?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/7uM6_aMjen0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/7uM6_aMjen0/foxhound-12-is-now-available.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/foxhound-12-is-now-available.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-3270946349029231008</guid><pubDate>Fri, 02 Dec 2011 20:01:00 +0000</pubDate><atom:updated>2011-12-02T15:01:00.341-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">The fRiDaY File</category><title>The fRiDaY File - Mobility Mayhem</title><description>&lt;p&gt;&lt;h3&gt;Texting!&lt;/h3&gt;&lt;br /&gt;
&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/5-Sjld5yy3Q" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Recalculating!&lt;/h3&gt;&lt;br /&gt;
&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/tZXM_g3mqew" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Texting!&lt;/h3&gt;&lt;br /&gt;
&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/hObgDUfPyqo" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
&lt;h3&gt;Meet Mayhem, prerecorded...&lt;/h3&gt;&lt;br /&gt;
&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/Zs7DJD2SfOA" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;&lt;a href="http://www.facebook.com/mayhemhere?sk=app_4949752878" target="_blank"&gt;...and in person:&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;
&lt;a href="http://www.facebook.com/mayhemhere?sk=app_4949752878" target="_blank"&gt;&lt;img src="http://platform.ak.fbcdn.net/www/app_full_proxy.php?app=4949752878&amp;v=1&amp;size=o&amp;cksum=11fa2efe27058f825c8c898184f816c5&amp;src=http%3A%2F%2Flbservice.allstate.com%2Ffb-mayhem-bunch%2Fmayhem_facebook_placeholder.jpg" title="Meet Mayhem"&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-3270946349029231008?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/SEcpAdAbGm8" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/SEcpAdAbGm8/friday-file-mobility-mayhem.html</link><author>noreply@blogger.com (Breck Carter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://img.youtube.com/vi/5-Sjld5yy3Q/default.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/friday-file-mobility-mayhem.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-497787815177352569.post-8833275689719867394</guid><pubDate>Fri, 02 Dec 2011 08:01:00 +0000</pubDate><atom:updated>2011-12-02T03:01:00.717-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">server setup and admin</category><title>How do you know when it's time to ship?</title><description>&lt;p&gt;At some point, every piece of software has to "go out the door", into the hands of &lt;strike&gt;unsuspecting&lt;/strike&gt; eager customers.&lt;br /&gt;
&lt;br /&gt;
The question is, how do you know when that time has come? &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;When all the bugs are gone!&lt;/h3&gt;&lt;br /&gt;
Yeah, sure, and if that was the case there wouldn't be &lt;b&gt;any&lt;/b&gt; new software, ever, because the bugs are never all gone.&lt;br /&gt;
&lt;br /&gt;
Fortunately, there are three simple criteria to be met before a software product is ready to ship:&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;1. All the new bugs are going into the "Future Fix" category.&lt;/h3&gt;&lt;br /&gt;
Once upon a time, you were determined to fix each and every flaw that turned up, no matter how minor. &lt;br /&gt;
&lt;br /&gt;
Now, it's not just a matter of "&lt;b&gt;When&lt;/b&gt; will this bug get fixed?" (Not Now) but "&lt;b&gt;Will&lt;/b&gt; this bug get fixed?"... that's when a Future Fix bug gets flagged with "Wait For Reproducible" which is the same as "No, Never Mind".&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;2. You're writing more FAQs than fixes.&lt;/h3&gt;&lt;br /&gt;
Sometimes it's easier to cover an electrical outlet than to document why it's not a good idea to stick a fork into it... until you consider the fact that docs don't need to be tested but if you change the code you have to go back and run all . . . those . . . endless . . . regression tests again.&lt;br /&gt;
&lt;br /&gt;
Plus, it's really easy to say "It's not a bug, it's a FAQ!" with a straight face, a lot easier than "It's not a bug, it's a feature!" even though the two statements are not that different.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;br /&gt;
Here's the third and final "Ready To Ship" criteria:&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;3. You just wrote the same FAQ a second time.&lt;/h3&gt;&lt;br /&gt;
...and it's not as well-written as the first one!&lt;br /&gt;
&lt;br /&gt;
That's when you know, it's time to ship.&lt;br /&gt;
&lt;br /&gt;
&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/497787815177352569-8833275689719867394?l=sqlanywhere.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SqlAnywhere/~4/F1Rbok9WYds" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/SqlAnywhere/~3/F1Rbok9WYds/how-do-you-know-when-its-time-to-ship.html</link><author>noreply@blogger.com (Breck Carter)</author><thr:total>0</thr:total><feedburner:origLink>http://sqlanywhere.blogspot.com/2011/12/how-do-you-know-when-its-time-to-ship.html</feedburner:origLink></item></channel></rss>

