<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>SQL</title>
        <link>http://www.jasonfollas.com/blog/category/6.aspx</link>
        <description>SQL</description>
        <language>en-US</language>
        <copyright>Jason Follas</copyright>
        <managingEditor>jason@jasonfollas.com</managingEditor>
        <generator>Subtext Version 2.0.0.43</generator>
        <item>
            <title>SQL Server 2008 RTM Available for Download</title>
            <link>http://jasonfollas.com/blog/archive/2008/08/06/sql-server-2008-rtm-available-for-download.aspx</link>
            <description>&lt;p&gt;I found out about 15 minutes ago that SQL Server 2008 RTM version is available for download from MSDN!  Congratulations to the SQL Server team!&lt;/p&gt;
&lt;p&gt;As I watch the 3 GB download trickle slowly onto my hard drive, I'm left with a few questions at this point, like what version of NETFX will be installed when I run setup?  RC0 so nicely installed the .NET 3.5 SP1 Beta Framework onto my machine, and since .NET 3.5 SP1 itself has not RTM'd, what are they going to do?  Distribute the beta with the SQL Server RTM bits?  Release .NET 3.5 SP1 to manufacturing?  Remove the install dependency on .NET 3.5 SP1?  &lt;/p&gt;
&lt;p&gt;I'll know in [checking the download rate] about 3 days from now.  ;-)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Well, come to find out, a few days later, Microsoft released the RTM version of NETFX 3.5 SP1.  You need to have Visual Studio 2008 fully upgraded to SP1 (which will include the NETFX 3.5 SP1) before installing SQL 2008.&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/48.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/08/06/sql-server-2008-rtm-available-for-download.aspx</guid>
            <pubDate>Wed, 06 Aug 2008 19:46:30 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/48.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/08/06/sql-server-2008-rtm-available-for-download.aspx#feedback</comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/48.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/48.aspx</trackback:ping>
        </item>
        <item>
            <title>SQL Server 2008: Spatial Data, Part 8</title>
            <link>http://jasonfollas.com/blog/archive/2008/06/23/sql-server-2008-spatial-data-part-8.aspx</link>
            <description>&lt;p&gt; &lt;/p&gt;
&lt;p&gt;In this, the eighth part in a series on the new Spatial Data types in SQL Server 2008, I'll step away from the database and do a little spatial coding using .NET.&lt;/p&gt;
&lt;h2&gt;Redistributable .NET Library&lt;/h2&gt;
&lt;p&gt;Up to this point in &lt;a target="_blank" href="http://www.jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;the series&lt;/a&gt;, I have demonstrated a lot of interesting (?) things that you can do with the new Spatial data types (&lt;strong&gt;Geometry&lt;/strong&gt; and &lt;strong&gt;Geography&lt;/strong&gt;) in SQL Server 2008.  You might be thinking, "That's swell, and all, but I wish I could do some of that stuff without needing to be tethered to a database."  Well, you know what?  You can!&lt;/p&gt;
&lt;p&gt;I mentioned in a previous post that the Spatial data types were implemented as SQLCLR User-Defined Types.  I've since been corrected by &lt;a target="_blank" href="http://blogs.msdn.com/isaac"&gt;Isaac Kunen&lt;/a&gt;, who stated that they are more accurately described as System-Defined Types, with the difference being that these are automatically installed and available for use as part of SQL Server 2008, regardless of whether the ENABLE CLR bit has been activated.  Semantics aside, these types are merely classes within a .NET assembly, and Microsoft is making this freely available as part of a Feature Pack for SQL Server (which will be redistributable as part of your stand-alone application, according to Isaac):&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="http://www.microsoft.com/downloads/details.aspx?FamilyId=089A9DAD-E2DF-43E9-9CD8-C06320520B40&amp;amp;displaylang=en"&gt;SQL Server 2008 RC0 Feature Pack Download Page&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(Look for "Microsoft SQL Server System CLR Types," which includes the two Spatial types plus the HierarchyID type.  This link is for RC0, and may not be applicable to future versions as the product is finalized and released.)&lt;/p&gt;
&lt;h2&gt;Builder API&lt;/h2&gt;
&lt;p&gt;A new feature that was included with the first Release Candidate (RC0) is the &lt;a target="_blank" href="http://blogs.msdn.com/isaac/archive/2008/05/30/our-upcoming-builder-api.aspx"&gt;Builder API&lt;/a&gt;.  This is a collection of interfaces and classes that helps you to construct spatial types by specifying one point at a time until all points have been added.&lt;/p&gt;
&lt;p&gt;The Builder API is not only useful for creating new instances of spatial data, but also for consuming existing instances one point at a time (maybe to convert an instance into another format).  Documentation is light at the moment, so I'm still trying to grok exactly how to best utilize it.&lt;/p&gt;
&lt;p&gt;For my first experiment with the API, I obtained some Zip Code Boundary data in ASCII format from the U.S. Census Bureau:&lt;/p&gt;
&lt;p&gt;&lt;a title="http://www.census.gov/geo/www/cob/z52000.html#ascii" href="http://www.census.gov/geo/www/cob/z52000.html#ascii"&gt;http://www.census.gov/geo/www/cob/z52000.html#ascii&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My goal was to parse the data, and then create a new &lt;strong&gt;SqlGeography&lt;/strong&gt; instance for each zip code.  (Note: &lt;strong&gt;SqlGeography&lt;/strong&gt; is the .NET class name that T-SQL refers to simply as &lt;strong&gt;Geography&lt;/strong&gt;).  The &lt;strong&gt;SqlGeographyBuilder&lt;/strong&gt; class proved to be perfect for accomplishing this task.&lt;/p&gt;
&lt;p&gt;At its core, the &lt;strong&gt;SqlGeographyBuilder&lt;/strong&gt; implements the &lt;strong&gt;IGeographySink&lt;/strong&gt; interface.  If you wanted to consume an existing &lt;strong&gt;SqlGeography&lt;/strong&gt; instance, you could implement &lt;strong&gt;IGeographySink&lt;/strong&gt; in your own class, and then invoke the &lt;strong&gt;SqlGeography&lt;/strong&gt;'s Populate() instance method, passing in your object as the parameter.  The Populate() method takes care of calling the appropriate &lt;strong&gt;IGeographySink&lt;/strong&gt; methods within your class.&lt;/p&gt;
&lt;p&gt;In this case, I'm not starting with an existing &lt;strong&gt;SqlGeography&lt;/strong&gt; instance, so my code will need to call the methods of the &lt;strong&gt;SqlGeographyBuilder&lt;/strong&gt; in the correct order:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart8_139B8/IGeographySink_2.png"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="386" alt="IGeographySink" width="416" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart8_139B8/IGeographySink_thumb.png" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;After EndGeography() has been invoked, the new instance is available via the ConstructedGeography property of the &lt;strong&gt;SqlGeographyBuilder&lt;/strong&gt; class.  &lt;/p&gt;
&lt;p&gt;Simple enough, right?  Yeah, I'm still a little lost myself...  But, here's some code to help demonstrate what's going on!&lt;/p&gt;
&lt;p&gt;First, let's look at the ASCII data.  A single zip code's boundary might be defined as:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;      1469      -0.824662148292608E+02       0.413848583827499E+02
      -0.824602851767940E+02       0.413864290595145E+02
      -0.824610630000000E+02       0.413860590000000E+02
      -0.824685900000000E+02       0.413841470000000E+02
      -0.824686034536111E+02       0.413843846804627E+02
      -0.824605990000000E+02       0.413863160000000E+02
      -0.824602851767940E+02       0.413864290595145E+02
END&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;The very first line happens to contain an identifier (maps to a second file that lists the actual USPS zip code).  The coordinate listed in the first line is not actually part of the boundary, but rather appears to be the population center of that area.  The actual boundary begins with the second line, and continues until you encounter the "END".  Also, in case you couldn't tell, coordinates in this data are in Longitude-Latitude order.&lt;/p&gt;
&lt;p&gt;Since a Zip Code is a polygon, and since we are working with &lt;strong&gt;SqlGeography&lt;/strong&gt;, we must be aware of ring ordering.  That is, the exterior ring of a polygon must be defined in a counter-clockwise order so that as you "walk the ring", the interior is always to your left.  If you reverse the order, then &lt;strong&gt;SqlGeography&lt;/strong&gt; assumes that you're trying to define a polygon containing the entire world &lt;em&gt;except&lt;/em&gt; for the small area inside of the polygon.&lt;/p&gt;
&lt;p&gt;Well, in this case, the order of the points of the Zip Code boundary is defined in clockwise order... so, we must be aware of this and call into the &lt;strong&gt;SqlGeographyBuilder&lt;/strong&gt; in the opposite order (so the last point defined in the ASCII data is the first point used while building our new instance).  &lt;/p&gt;
&lt;p&gt;To accomplish this, I simply parse the Lat/Long coordinates as "double" types, and then push them onto a stack.  Then, I pop the stack and call into the Builder API with each point.  At the end, I obtain the new &lt;strong&gt;SqlGeography&lt;/strong&gt; instance from the ConstructedGeography property.  &lt;/p&gt;
&lt;p&gt;(Note: This is demonstrative code - some things should probably be cleaned up/refactored/error handled... You have been warned)&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; SqlGeography ParseAsGeography(&lt;span class="kwrd"&gt;string&lt;/span&gt; zipcode_points)
{
    StringReader sr = &lt;span class="kwrd"&gt;new&lt;/span&gt; StringReader(zipcode_points);
    &lt;span class="kwrd"&gt;string&lt;/span&gt; line = sr.ReadLine();

    Stack&amp;lt;&lt;span class="kwrd"&gt;double&lt;/span&gt;[]&amp;gt; Points = &lt;span class="kwrd"&gt;new&lt;/span&gt; Stack&amp;lt;&lt;span class="kwrd"&gt;double&lt;/span&gt;[]&amp;gt;();

    &lt;span class="kwrd"&gt;while&lt;/span&gt; (line != &lt;span class="kwrd"&gt;null&lt;/span&gt;  &amp;amp;&amp;amp; line != &lt;span class="str"&gt;"END"&lt;/span&gt;)
    {
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (line != String.Empty)
        {
            Points.Push(ParseLatLngValues(line));
        }

        line = sr.ReadLine();
    }

    &lt;span class="kwrd"&gt;return&lt;/span&gt; CreateGeography(Points);
}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;double&lt;/span&gt;[] ParseLatLngValues(&lt;span class="kwrd"&gt;string&lt;/span&gt; line)
{
    &lt;span class="rem"&gt;//      -0.838170700000000E+02       0.409367390000000E+02&lt;/span&gt;

    &lt;span class="kwrd"&gt;double&lt;/span&gt;[] ret = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="kwrd"&gt;double&lt;/span&gt;[2];

    &lt;span class="kwrd"&gt;string&lt;/span&gt; lng = System.Text.RegularExpressions.Regex&lt;br /&gt;                            .Matches(line, &lt;span class="str"&gt;"\\S+"&lt;/span&gt;)[0].Value;
    &lt;span class="kwrd"&gt;string&lt;/span&gt; lat = System.Text.RegularExpressions.Regex&lt;br /&gt;                            .Matches(line, &lt;span class="str"&gt;"\\S+"&lt;/span&gt;)[1].Value;

    &lt;span class="kwrd"&gt;double&lt;/span&gt;.TryParse(lat, &lt;span class="kwrd"&gt;out&lt;/span&gt; ret[0]);
    &lt;span class="kwrd"&gt;double&lt;/span&gt;.TryParse(lng, &lt;span class="kwrd"&gt;out&lt;/span&gt; ret[1]);

    &lt;span class="kwrd"&gt;return&lt;/span&gt; ret;
}&lt;/pre&gt;
&lt;p&gt;&lt;a title="http://www.microsoft.com/downloads/details.aspx?FamilyId=089A9DAD-E2DF-43E9-9CD8-C06320520B40&amp;amp;displaylang=en" href="http://www.microsoft.com/downloads/details.aspx?FamilyId=089A9DAD-E2DF-43E9-9CD8-C06320520B40&amp;amp;displaylang=en"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; SqlGeography CreateGeography(Stack&amp;lt;&lt;span class="kwrd"&gt;double&lt;/span&gt;[]&amp;gt; points)
{
    SqlGeographyBuilder builder = &lt;span class="kwrd"&gt;new&lt;/span&gt; SqlGeographyBuilder();
    builder.SetSrid(4326);
    builder.BeginGeography(OpenGisGeographyType.Polygon);

    &lt;span class="kwrd"&gt;double&lt;/span&gt;[] point = points.Pop();

    builder.BeginFigure(point[0], point[1]);

    &lt;span class="kwrd"&gt;while&lt;/span&gt; (points.Count &amp;gt; 0)
    {
        point = points.Pop();
        builder.AddLine(point[0], point[1]);
    }

    builder.EndFigure();
    builder.EndGeography();

    &lt;span class="kwrd"&gt;return&lt;/span&gt; builder.ConstructedGeography;
}&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://www.jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;SQL Server 2008: Spatial Data, Part 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/27/sql-server-2008-spatial-data-part-2.aspx"&gt;SQL Server 2008: Spatial Data, Part 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/28/sql-server-2008-spatial-data-part-3.aspx"&gt;SQL Server 2008: Spatial Data, Part 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx"&gt;SQL Server 2008: Spatial Data, Part 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/07/sql-server-2008-spatial-data-part-5.aspx"&gt;SQL Server 2008: Spatial Data, Part 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/11/sql-server-2008-spatial-data-part-6.aspx"&gt;SQL Server 2008: Spatial Data, Part 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/05/16/sql-server-2008-spatial-data-part-7.aspx"&gt;SQL Server 2008: Spatial Data, Part 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f06%2f23%2fsql-server-2008-spatial-data-part-8.aspx"&gt;&lt;img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f06%2f23%2fsql-server-2008-spatial-data-part-8.aspx" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/46.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/06/23/sql-server-2008-spatial-data-part-8.aspx</guid>
            <pubDate>Tue, 24 Jun 2008 02:18:47 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/46.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/06/23/sql-server-2008-spatial-data-part-8.aspx#feedback</comments>
            <slash:comments>9</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/46.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/46.aspx</trackback:ping>
        </item>
        <item>
            <title>Coding in SQL Server: An Evolution</title>
            <link>http://jasonfollas.com/blog/archive/2008/06/19/coding-in-sql-server-an-evolution.aspx</link>
            <description>&lt;p&gt;Tuesday at the &lt;a target="_blank" href="http://www.nwnug.com/PermaLink,guid,95b33c73-aca9-4f3f-842e-3eec9158795b.aspx"&gt;NWNUG&lt;/a&gt; meeting, &lt;a target="_blank" href="http://aspadvice.com/blogs/ssmith/"&gt;Steven Smith&lt;/a&gt; spoke on various ways to squeeze performance out of your ASP.NET applications.  This was a fantastic talk, and gave me plenty to think about (since ASP.NET is not my forte, I only consider myself to have an intermediate skillset on this topic).&lt;/p&gt;
&lt;p&gt;One suggestion that he made involved caching database writes.  That is, instead of immediately writing logging-type information to the database for every request, which is a relatively expensive operation considering the small payload size, that you could accumulate them in a short-term cache, and then perform the write operation periodically.  Fewer database calls = faster performance.&lt;/p&gt;
&lt;p&gt;In his example, he spoke of his advertisement server that might serve many impressions per second, but he doesn't want each impression to incur an expensive database write.  So, he keeps track of the activity locally, and then persists to the database every 5 seconds using a single database call containing multiple data points.&lt;/p&gt;
&lt;p&gt;The code that Steve demonstrated utilized XML to contain the data within a single block of text (read: can be passed in as a single parameter to a stored procedure):&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;ROOT&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Activity&lt;/span&gt; &lt;span class="attr"&gt;customerId&lt;/span&gt;&lt;span class="kwrd"&gt;="ALFKI"&lt;/span&gt; &lt;span class="attr"&gt;viewCount&lt;/span&gt;&lt;span class="kwrd"&gt;="5"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Activity&lt;/span&gt; &lt;span class="attr"&gt;customerId&lt;/span&gt;&lt;span class="kwrd"&gt;="ANATR"&lt;/span&gt; &lt;span class="attr"&gt;viewCount&lt;/span&gt;&lt;span class="kwrd"&gt;="7"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;ROOT&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Now, consuming XML from T-SQL is an area that I know very well, so I cringed a little bit when Steve showed the actual stored procedure code itself:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;CREATE&lt;/span&gt; &lt;span class="kwrd"&gt;PROCEDURE&lt;/span&gt; dbo.BulkLogCustomerViews
  @@doc text &lt;span class="rem"&gt;-- XML Doc...&lt;/span&gt;
&lt;span class="kwrd"&gt;AS&lt;/span&gt;

&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @idoc   &lt;span class="kwrd"&gt;int&lt;/span&gt;

&lt;span class="rem"&gt;-- Create an internal representation (virtual table) of &lt;br /&gt;   the XML document...&lt;/span&gt;
&lt;span class="kwrd"&gt;EXEC&lt;/span&gt; sp_xml_preparedocument @idoc &lt;span class="kwrd"&gt;OUTPUT&lt;/span&gt;, @@doc

&lt;span class="rem"&gt;-- Perform UPDATES&lt;/span&gt;
&lt;span class="kwrd"&gt;UPDATE&lt;/span&gt; TopCustomerLog
&lt;span class="kwrd"&gt;SET&lt;/span&gt; TopCustomerLog.ViewCount = TopCustomerLog.ViewCount &lt;br /&gt;    + ox2.viewCount
&lt;span class="kwrd"&gt;FROM&lt;/span&gt; &lt;span class="kwrd"&gt;OPENXML&lt;/span&gt; (@idoc, &lt;span class="str"&gt;'/ROOT/Activity'&lt;/span&gt;,1)
          &lt;span class="kwrd"&gt;WITH&lt;/span&gt; ( [customerId]  &lt;span class="kwrd"&gt;NCHAR&lt;/span&gt;(5)
        , viewCount &lt;span class="kwrd"&gt;int&lt;/span&gt;
        ) ox2
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt; TopCustomerLog.[customerId] = ox2.[customerId]
 
&lt;span class="rem"&gt;-- Perform INSERTS&lt;/span&gt;
INSERT &lt;span class="kwrd"&gt;INTO&lt;/span&gt; TopCustomerLog
     ( CustomerID
     , ViewCount
     )
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; [customerId]
     , viewCount
  &lt;span class="kwrd"&gt;FROM&lt;/span&gt; &lt;span class="kwrd"&gt;OPENXML&lt;/span&gt; (@idoc, &lt;span class="str"&gt;'/ROOT/Activity'&lt;/span&gt;,1)
          &lt;span class="kwrd"&gt;WITH&lt;/span&gt; ( customerId  &lt;span class="kwrd"&gt;NCHAR&lt;/span&gt;(5)
        , viewCount  &lt;span class="kwrd"&gt;int&lt;/span&gt;
        ) ox
  &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; &lt;span class="kwrd"&gt;NOT&lt;/span&gt; &lt;span class="kwrd"&gt;EXISTS&lt;/span&gt; 
(&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; customerId &lt;span class="kwrd"&gt;FROM&lt;/span&gt; TopCustomerLog 
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt; TopCustomerLog.customerId = ox.customerId)

&lt;span class="rem"&gt;-- Remove the 'virtual table' now...&lt;/span&gt;
&lt;span class="kwrd"&gt;EXEC&lt;/span&gt; sp_xml_removedocument @idoc&lt;/pre&gt;
&lt;p&gt;&lt;span class="kwrd"&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="kwrd"&gt;Now, to Steve's credit, this code works just fine, and can probably be used as-is on all versions of SQL Server from 7.0 through 2008.  But, since we really don't write ASP applications consisting entirely of Response.Write any longer, I'd like to see Steve update his demo to use more modern techniques on the database as well.  ;-)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="kwrd"&gt;The first thing that he could do is update the procedure to utilize the XML data type that was first introduced in SQL Server 2005.  This would simplify the code a little bit, and would get rid of the dependency on the COM-based MSXML.dll, which the sp_xml_preparedocument and OPENXML() uses.&lt;/span&gt;&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;CREATE&lt;/span&gt; &lt;span class="kwrd"&gt;PROCEDURE&lt;/span&gt; dbo.BulkLogCustomerViews
  @doc xml
&lt;span class="kwrd"&gt;AS&lt;/span&gt;

&lt;span class="rem"&gt;-- Perform UPDATES&lt;/span&gt; &lt;br /&gt;&lt;span class="kwrd"&gt;UPDATE&lt;/span&gt; TopCustomerLog
&lt;span class="kwrd"&gt;SET&lt;/span&gt;    TopCustomerLog.ViewCount = TopCustomerLog.ViewCount &lt;br /&gt;       + ox2.viewCount
&lt;span class="kwrd"&gt;FROM&lt;/span&gt;   (
       &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; T.activity.&lt;span class="kwrd"&gt;value&lt;/span&gt;(&lt;span class="str"&gt;'@customerId'&lt;/span&gt;, &lt;span class="str"&gt;'nchar(5)'&lt;/span&gt;) &lt;br /&gt;              as CustomerID, 
              T.activity.&lt;span class="kwrd"&gt;value&lt;/span&gt;(&lt;span class="str"&gt;'@viewCount'&lt;/span&gt;, &lt;span class="str"&gt;'int'&lt;/span&gt;) viewCount
       &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   @doc.nodes(&lt;span class="str"&gt;'/ROOT/Activity'&lt;/span&gt;) &lt;span class="kwrd"&gt;as&lt;/span&gt; T(activity)
       ) ox2
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  TopCustomerLog.[customerId] = ox2.[customerId]
 
&lt;span class="rem"&gt;-- Perform INSERTS&lt;/span&gt;
INSERT &lt;span class="kwrd"&gt;INTO&lt;/span&gt; TopCustomerLog
     ( CustomerID
     , ViewCount
     )
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; [customerId]
     , viewCount
&lt;span class="kwrd"&gt;FROM&lt;/span&gt; (
       &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; T.activity.&lt;span class="kwrd"&gt;value&lt;/span&gt;(&lt;span class="str"&gt;'@customerId'&lt;/span&gt;, &lt;span class="str"&gt;'nchar(5)'&lt;/span&gt;) &lt;br /&gt;              as CustomerID, 
              T.activity.&lt;span class="kwrd"&gt;value&lt;/span&gt;(&lt;span class="str"&gt;'@viewCount'&lt;/span&gt;, &lt;span class="str"&gt;'int'&lt;/span&gt;) viewCount
       &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   @doc.nodes(&lt;span class="str"&gt;'/ROOT/Activity'&lt;/span&gt;) &lt;span class="kwrd"&gt;as&lt;/span&gt; T(activity)
       ) ox
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt; &lt;span class="kwrd"&gt;NOT&lt;/span&gt; &lt;span class="kwrd"&gt;EXISTS&lt;/span&gt; (
    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; customerId &lt;span class="kwrd"&gt;FROM&lt;/span&gt; TopCustomerLog 
    &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; TopCustomerLog.customerId = ox.customerId )&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;br /&gt;
Note that the XML data type in SQL Server doesn't need to be a well-formed document.  In this case, Steve could just pass in series of "Activity" elements (no "ROOT" element would be required by SQL Server, so he would also be able to simplify the .NET code that actually creates the XML string):&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Activity&lt;/span&gt; &lt;span class="attr"&gt;customerId&lt;/span&gt;&lt;span class="kwrd"&gt;="ALFKI"&lt;/span&gt; &lt;span class="attr"&gt;viewCount&lt;/span&gt;&lt;span class="kwrd"&gt;="5"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Activity&lt;/span&gt; &lt;span class="attr"&gt;customerId&lt;/span&gt;&lt;span class="kwrd"&gt;="ANATR"&lt;/span&gt; &lt;span class="attr"&gt;viewCount&lt;/span&gt;&lt;span class="kwrd"&gt;="7"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Consequently, the XPath (XQuery, actually) within the nodes() method of the stored procedure code would need to change as well: &lt;/p&gt;
&lt;pre class="csharpcode"&gt;@doc.nodes(&lt;span class="str"&gt;'Activity'&lt;/span&gt;) &lt;span class="kwrd"&gt;as&lt;/span&gt; T(activity)&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;
But, we can kick this up a notch and use some SQL Server 2008 features as well. First, there's new "Upsert" capabilities (MERGE statement) that tries to simplify what Steve does with the UPDATE followed by INSERT:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;CREATE&lt;/span&gt; &lt;span class="kwrd"&gt;PROCEDURE&lt;/span&gt; dbo.BulkLogCustomerViews
  @doc xml
&lt;span class="kwrd"&gt;AS&lt;/span&gt;

MERGE TopCustomerLog &lt;span class="kwrd"&gt;AS&lt;/span&gt; target
&lt;span class="kwrd"&gt;USING&lt;/span&gt; (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; T.activity.&lt;span class="kwrd"&gt;value&lt;/span&gt;(&lt;span class="str"&gt;'@customerId'&lt;/span&gt;, &lt;span class="str"&gt;'nchar(5)'&lt;/span&gt;) 
              &lt;span class="kwrd"&gt;as&lt;/span&gt; CustomerID, 
              T.activity.&lt;span class="kwrd"&gt;value&lt;/span&gt;(&lt;span class="str"&gt;'@viewCount'&lt;/span&gt;, &lt;span class="str"&gt;'int'&lt;/span&gt;) &lt;span class="kwrd"&gt;as&lt;/span&gt; viewCount
       &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   @doc.nodes(&lt;span class="str"&gt;'Activity'&lt;/span&gt;) &lt;span class="kwrd"&gt;as&lt;/span&gt; T(activity)) &lt;span class="kwrd"&gt;AS&lt;/span&gt; source 
&lt;span class="kwrd"&gt;ON&lt;/span&gt;    (target.CustomerID = source.CustomerID)
&lt;span class="kwrd"&gt;WHEN&lt;/span&gt;  MATCHED 
      &lt;span class="kwrd"&gt;THEN&lt;/span&gt; &lt;span class="kwrd"&gt;UPDATE&lt;/span&gt; &lt;span class="kwrd"&gt;SET&lt;/span&gt; target.ViewCount = target.ViewCount + source.viewCount
&lt;span class="kwrd"&gt;WHEN&lt;/span&gt;  &lt;span class="kwrd"&gt;NOT&lt;/span&gt; MATCHED
      &lt;span class="kwrd"&gt;THEN&lt;/span&gt; INSERT (CustomerID, ViewCount)
      &lt;span class="kwrd"&gt;VALUES&lt;/span&gt; (source.CustomerID, source.viewCount);&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;One more thing that could be done to further simplify this T-SQL is to use a Table-valued Parameter instead of the XML.  This would allow Steve to pass a fully populated table of data into the stored procedure and consume it directly by the MERGE statement.&lt;/p&gt;
&lt;p&gt;The first step is to create a T-SQL type that defines the table structure of the parameter (this is a one-time operation, unless the table structure changes):&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;CREATE&lt;/span&gt; TYPE CustomerViewType &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;TABLE&lt;/span&gt; 
( 
    CustomerID &lt;span class="kwrd"&gt;nchar&lt;/span&gt;(5) &lt;span class="kwrd"&gt;NOT&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;,
    ViewCount &lt;span class="kwrd"&gt;int&lt;/span&gt; &lt;span class="kwrd"&gt;NOT&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;
);&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Now, a parameter can be defined of this type, and used just like any other table-value variable:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;ALTER&lt;/span&gt; &lt;span class="kwrd"&gt;PROCEDURE&lt;/span&gt; dbo.BulkLogCustomerViews
  @views CustomerViewType READONLY
&lt;span class="kwrd"&gt;AS&lt;/span&gt;

MERGE TopCustomerLog &lt;span class="kwrd"&gt;AS&lt;/span&gt; target
&lt;span class="kwrd"&gt;USING&lt;/span&gt; @views &lt;span class="kwrd"&gt;AS&lt;/span&gt; source 
&lt;span class="kwrd"&gt;ON&lt;/span&gt;    (target.CustomerID = source.CustomerID)
&lt;span class="kwrd"&gt;WHEN&lt;/span&gt;  MATCHED 
      &lt;span class="kwrd"&gt;THEN&lt;/span&gt; &lt;span class="kwrd"&gt;UPDATE&lt;/span&gt; &lt;span class="kwrd"&gt;SET&lt;/span&gt; target.ViewCount = target.ViewCount &lt;br /&gt;                      + source.viewCount
&lt;span class="kwrd"&gt;WHEN&lt;/span&gt;  &lt;span class="kwrd"&gt;NOT&lt;/span&gt; MATCHED
      &lt;span class="kwrd"&gt;THEN&lt;/span&gt; INSERT (CustomerID, ViewCount)
      &lt;span class="kwrd"&gt;VALUES&lt;/span&gt; (source.CustomerID, source.viewCount);&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;On the ADO.NET side, the table-valued parameter could be represented as a DataTable object (other options also exist), and can be assigned directly as the value of the stored procedure's parameter object:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;// Create a data table, and provide its structure&lt;/span&gt;
DataTable customerViews = &lt;span class="kwrd"&gt;new&lt;/span&gt; DataTable();
customerViews.Columns.Add(&lt;span class="str"&gt;"CustomerID"&lt;/span&gt;, &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(&lt;span class="kwrd"&gt;string&lt;/span&gt;));
customerViews.Columns.Add(&lt;span class="str"&gt;"ViewCount"&lt;/span&gt;, &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(&lt;span class="kwrd"&gt;int&lt;/span&gt;));

&lt;span class="rem"&gt;// Fill with rows&lt;/span&gt;
customerViews.Rows.Add(&lt;span class="str"&gt;"ALFKI"&lt;/span&gt;, 5);
customerViews.Rows.Add(&lt;span class="str"&gt;"ANATR"&lt;/span&gt;, 7);

&lt;span class="kwrd"&gt;using&lt;/span&gt; (SqlConnection conn = &lt;span class="kwrd"&gt;new&lt;/span&gt; SqlConnection(&lt;span class="str"&gt;"..."&lt;/span&gt;))
{
  SqlCommand cmd = conn.CreateCommand();
  cmd.CommandType = System.Data.CommandType.StoredProcedure;
  cmd.CommandText = &lt;span class="str"&gt;"dbo.BulkLogCustomerViews"&lt;/span&gt;;

  SqlParameter param &lt;br /&gt;        = cmd.Parameters.AddWithValue(&lt;span class="str"&gt;"@views"&lt;/span&gt;, customerViews);

  conn.Open();

  cmd.ExecuteNonQuery();
}&lt;/pre&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/45.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/06/19/coding-in-sql-server-an-evolution.aspx</guid>
            <pubDate>Thu, 19 Jun 2008 20:50:36 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/45.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/06/19/coding-in-sql-server-an-evolution.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/45.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/45.aspx</trackback:ping>
        </item>
        <item>
            <title>SQL Server 2008 RC0 Install: Sql2005SsmsExpressFacet</title>
            <link>http://jasonfollas.com/blog/archive/2008/06/19/sql-server-2008-rc0-install-sql2005ssmsexpressfacet.aspx</link>
            <description>&lt;p&gt;This morning's goal was to quickly install SQL Server 2008 RC0, and then move on with some project work.  Let's just say that my project work &lt;em&gt;should&lt;/em&gt; resume by this afternoon...&lt;/p&gt;
&lt;p&gt;In the interest of disk space, I removed an existing installation of SQL Server 2005 Developer Edition.  And then the installation of 2008 RC0 began by installing the "Microsoft.NET Framework 3.5 SP1 (Beta)"...  which is probably "install-smell" for me needing to pave my machine when the product finally RTM's.  But, I digress...&lt;/p&gt;
&lt;p&gt;The installation went pretty smoothly until it came time for the "System Configuration Check" that takes place after you select everything that you would like to install, but before the files actually get installed.  In my case, this check failed because "The SQL Server 2005 Express Tools are installed.  To continue, remove the SQL Server 2005 Express Tools."  (This is the "Sql2005SsmsExpressFacet" rule of the installation)&lt;/p&gt;
&lt;p&gt;Thank you, Microsoft, for that succinct failure message that includes instructions for resolution...  Except, I didn't have the SQL Server 2005 Express Tools installed.  They didn't show up in my Programs list, in the Start menu, or on my C: drive at all.  How am I to uninstall something that isn't installed?  Hrmmm....&lt;/p&gt;
&lt;p&gt;After about an hour's search around my hard drive, I finally went into the registry, and discovered the following key:&lt;/p&gt;
&lt;p&gt;HKLM\Software\Microsoft\Microsoft SQL Server\90\Tools\ShellSEM&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: &lt;/em&gt;&lt;a target="_blank" href="http://weblog.rebex.cz/blogs/honzas"&gt;&lt;em&gt;Jan Sotola&lt;/em&gt;&lt;/a&gt;&lt;em&gt; reports that the affected 64-bit version key is:&lt;br /&gt;
&lt;/em&gt;&lt;font face="Arial"&gt;&lt;em&gt;HKLM\Software\&lt;strong&gt;Wow6432Node&lt;/strong&gt;\Microsoft\...&lt;br /&gt;
   ...\Microsoft SQL Server\90\Tools\ShellSEM&lt;/em&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;Contained within was some registry information belonging to Red Gate SQL Prompt.  Apparently, despite my removing of the SQL 2005 Express Tools some time ago, this registry key was not removed because the Red Gate information was still there.&lt;/p&gt;
&lt;p&gt;On a hunch, I renamed the key to "ShellSEM.old", and the SQL Server 2008 installation carried on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Shortly after posting this, Theo Spears from Red Gate sent the following email:&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;blockquote&gt;&lt;font face="Arial"&gt;&lt;em&gt;"I apologise for this issue; the SQL Prompt team here has been working to address it. You and your readers may be interested to hear that we now have a version which works with SQL Server 2008 RC0, and no longer blocks the installation. To get a copy send us an email at support@red-gate.com"&lt;/em&gt;&lt;/font&gt;&lt;/blockquote&gt;
&lt;p&gt;I should clarify that my little rant above was not targeted at Red Gate, but I'm so happy to hear that they are proactively working to resolve this little issue.  I would have just liked for Microsoft to use more than a single registry key as evidence of a conflicting product installation, that's all.&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/44.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/06/19/sql-server-2008-rc0-install-sql2005ssmsexpressfacet.aspx</guid>
            <pubDate>Thu, 19 Jun 2008 15:02:43 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/44.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/06/19/sql-server-2008-rc0-install-sql2005ssmsexpressfacet.aspx#feedback</comments>
            <slash:comments>21</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/44.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/44.aspx</trackback:ping>
        </item>
        <item>
            <title>SQL Server 2008: Spatial Data, Part 7</title>
            <link>http://jasonfollas.com/blog/archive/2008/05/16/sql-server-2008-spatial-data-part-7.aspx</link>
            <description>&lt;p&gt;The &lt;a target="_blank" href="http://www.opengeospatial.org"&gt;Open Geospatial Consortium&lt;/a&gt;'s &lt;a target="_blank" href="http://www.opengeospatial.org/standards/sfa"&gt;Simple Features specification&lt;/a&gt;, which SQL Server 2008's &lt;strong&gt;Geometry&lt;/strong&gt; data type is based upon, defines standards for working with spatial data using a flat-earth (projected planar) model.  Ironically, these standards don't exactly cover the intricacies of using an ellipsoidal model, which is needed to "accurately" represent the world that we live in.  In other words, the OGC standards define how to work with paper maps of the world, but not globes.&lt;/p&gt;
&lt;p&gt;Fortunately, the SQL Server team recognized that that the &lt;strong&gt;Geometry&lt;/strong&gt; type is inadequate for a lot of scenarios, and implemented a second data type just for representing geospatial data using a true ellipsoidal model: &lt;strong&gt;Geography&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this, the seventh part of a series about the SQL Server Spatial Data Type, I'll examine some of the key differences between the &lt;strong&gt;Geometry&lt;/strong&gt; and the &lt;strong&gt;Geography&lt;/strong&gt; type that developers should be aware of.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;Latitude and Longitude&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
Locations on a flat model are defined in terms of X and Y.  There exists some point known as the Origin where X and Y are both zero.  From there, it is defined that values of X will increase (or decrease, in the case of negatve numbers) if you move horizontally away from the Origin.  Likewise, the values of Y will increase if you move vertically away from the Origin.  &lt;/p&gt;
&lt;p&gt;By convention, both X and Y will grow to infinity, so flat models do not "wrap around" and start approaching the Origin again if you go too far in one direction.  Usually, a coordinate system will be based on some underlying representation of the real-world, so coordinates that are beyond the defined boundaries of that map are logically undefined.&lt;/p&gt;
&lt;p&gt;By contrast, though, an ellipsoidal model &lt;em&gt;does&lt;/em&gt; wrap around.  If you started at a point in the middle and kept traveling in a straight line to the right, you will eventually return to that starting point.  &lt;/p&gt;
&lt;p&gt;So, it turns out not to be very practical to define points on a ball using X and Y.  Instead, points are defined using angles.  Longitude is the horizontal angle (how far East or West from a &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Prime_meridian"&gt;Prime Meridian&lt;/a&gt;) and ranges from -180 degrees to 180 degrees (with -180 and 180 being the same).  Latitude is the vertical angle (how far North or South from the Equator) and ranges from -90 degrees to 90 degrees (with -90 representing the South Pole and 90 representing the North Pole).&lt;/p&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="240" alt="latlon" width="229" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/latlon_3.jpg" /&gt; &lt;/p&gt;
&lt;p&gt;In terms of the &lt;strong&gt;Geography&lt;/strong&gt; data type, just be aware that there is no X and Y.  Instead, you work with Long and Lat.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: All of the SQL Server 2008 CTPs to date that include Spatial support, including the most recent February 2008 version, use Lat-Long ordering within WKT.  This was a design decision based on the fact that the OGC standard did not already define parameter ordering for angular coordinates.  Starting with the first Release Candidate, however, these parameters will be swapped to use Long-Lat ordering.  Doing so will align SQL Server's spatial support with other platforms that have already implemented Long-Lat ordering.  Note also that that this is aligned with the concepts of X and Y, which by convention lists the X value first.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;Straight Lines&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
The shortest distance between two points is a straight line.  But, a straight line on a flat-earth model is far different than a straight line on an ellipsoidal model.  To demonstrate, consider the shortest path from Redmond, WA, USA to Cambridge, England, UK: &lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/s7_1_2.png"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="290" alt="s7_1" width="380" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/s7_1_thumb.png" /&gt;&lt;/a&gt;  &lt;br /&gt;
On this planar projection, it certainly looks like the shortest path.  Even when examined on a 3D model, it looks correct: &lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/s7_2_2.png"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="274" alt="s7_2" width="364" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/s7_2_thumb.png" /&gt;&lt;/a&gt; &lt;br /&gt;
But, if the camera is moved towards the North Pole, then the error becomes apparent:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/s7_3_2.png"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="274" alt="s7_3" width="364" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/s7_3_thumb.png" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;In the ellipsoidal model, the shortest path between the points is not the red line, which roughly parallels the lines of Latitude, but rather the black arrow!  Converted back to a planar projection, this actual shortest path appears curved:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/s7_4_2.png"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="290" alt="s7_4" width="380" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/s7_4_thumb.png" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;em&gt;(in this view, the black curve was [hastily] plotted by hand)&lt;/em&gt; &lt;br /&gt;
&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Instance Methods&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
The following slide shows in all of the instance methods that have been implemented for the &lt;strong&gt;Geography&lt;/strong&gt; type as of the February 2008 CTP.  For comparison, instance methods from the &lt;strong&gt;Geometry&lt;/strong&gt; type that do not exist in the &lt;strong&gt;Geography&lt;/strong&gt; type are shown in gray.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/GeographyMethods_2.png"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="260" alt="GeographyMethods" width="340" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart7_128A3/GeographyMethods_thumb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It may be impossible to define some of these equivalent methods for &lt;strong&gt;Geography&lt;/strong&gt;, simply because the rules are different.  For instance, if you define a set of points that make up a Polygon, what is considered to be the interior and exterior of that shape?  Since the world coordinates wrap around in an ellipsoidal model, you might be intending to represent a shape whose interior is the entire world except for the small portion.  There is simply no way to convey your intent using the methods as described by the OGC standard.&lt;/p&gt;
&lt;p&gt;In an attempt to prevent this particular scenario, the SQL Server team has imposed a limit on the size of a &lt;strong&gt;Geography&lt;/strong&gt; in the February 2008 CTP: you cannot define a &lt;strong&gt;Geography&lt;/strong&gt; that is larger than a hemisphere.   &lt;/p&gt;
&lt;p&gt;There may very well be logical solutions for working around some of the issues that prevented the SQL team from implementing all of &lt;strong&gt;Geometry's&lt;/strong&gt; methods in the &lt;strong&gt;Geography&lt;/strong&gt; type.  However, in this case, Microsoft appears to be waiting for the OGC to define certain rules as part of a standard rather than coming up with their own assumptions, which could be invalidated later by the standards group going in a different direction.&lt;/p&gt;
&lt;p&gt;More on the &lt;strong&gt;Geography&lt;/strong&gt; type later!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;SQL Server 2008: Spatial Data, Part 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/27/sql-server-2008-spatial-data-part-2.aspx"&gt;SQL Server 2008: Spatial Data, Part 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/28/sql-server-2008-spatial-data-part-3.aspx"&gt;SQL Server 2008: Spatial Data, Part 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx"&gt;SQL Server 2008: Spatial Data, Part 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/07/sql-server-2008-spatial-data-part-5.aspx"&gt;SQL Server 2008: Spatial Data, Part 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/11/sql-server-2008-spatial-data-part-6.aspx"&gt;SQL Server 2008: Spatial Data, Part 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/06/23/sql-server-2008-spatial-data-part-8.aspx"&gt;(next part) SQL Server 2008: Spatial Data, Part 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f05%2f16%2fsql-server-2008-spatial-data-part-7.aspx"&gt;&lt;img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f05%2f16%2fsql-server-2008-spatial-data-part-7.aspx" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/42.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/05/16/sql-server-2008-spatial-data-part-7.aspx</guid>
            <pubDate>Sat, 17 May 2008 02:53:41 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/42.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/05/16/sql-server-2008-spatial-data-part-7.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/42.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/42.aspx</trackback:ping>
        </item>
        <item>
            <title>SQL Server 2008: Spatial Data, Part 6</title>
            <link>http://jasonfollas.com/blog/archive/2008/04/11/sql-server-2008-spatial-data-part-6.aspx</link>
            <description>&lt;p&gt; &lt;/p&gt;
&lt;p&gt;In the &lt;a target="_blank" href="http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx"&gt;Part 4&lt;/a&gt; and &lt;a target="_blank" href="http://jasonfollas.com/blog/archive/2008/04/07/sql-server-2008-spatial-data-part-5.aspx"&gt;Part 5&lt;/a&gt; of the series, I demonstrated some instance methods of the &lt;strong&gt;Geometry&lt;/strong&gt; type that returned a new &lt;strong&gt;Geometry&lt;/strong&gt; based on existing instances.  In this part, I will concentrate on instance methods and properties of the &lt;strong&gt;Geometry&lt;/strong&gt; type that return scalar values and Points. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;STArea, STLength&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
Typically, your spatial data will represent something from the real world.  A LineString may be the collection of points gathered from a GPS device, and together they may represent the path that you took from your home to the office.  A Polygon may be the collection of points around the boundary of governmental territory, like a county or a parish within your state.&lt;/p&gt;
&lt;p&gt;In both of these cases, the time will come when you will want to know the length of the LineString (or length of the perimeter of the Polygon) and the area within the Polygon.  OGC standard method &lt;font face="monospace"&gt;STArea()&lt;/font&gt; returns a float indicating the area of the instance in square units (or 0, if the instance is not a Polygon and does not have area).  &lt;font face="monospace"&gt;STLength()&lt;/font&gt; returns a float indicating the length of the instance in units (or 0, if the instance is a Point and does not have length).  &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g GEOMETRY = &lt;span class="str"&gt;'POLYGON((10 10, 10 40, 40 40, 10 10))'&lt;/span&gt;
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STArea(), @g.STLength()

Results:

Area       Length&lt;br /&gt;450        102.426406871193&lt;/pre&gt;
&lt;p&gt; &lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="52" alt="Spatial_6_1" width="57" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A265/Spatial_6_1_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STCentroid&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
Thinking back to Mr. Bollenbacher's 10th Grade Geometry class, we had to use a compass and straight edge to construct lines bisecting the angles of polygons (primarily triangles).  The point where the angle bisectors met was the exact center, or &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Centroid"&gt;centroid&lt;/a&gt;, of the shape.  Centroids are important because any line that passes through a centroid will divide the Polygon into two parts of equal area.  It should be noted that a Centroid may not actually be on the surface of a Polygon.&lt;/p&gt;
&lt;p&gt;The OGC standard method &lt;font face="monospace"&gt;STCentroid()&lt;/font&gt; returns a Point indicating the centroid of the shape.  If the instance is not a Polygon (or MultiPolygon), then NULL will be returned.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g GEOMETRY = &lt;span class="str"&gt;'POLYGON((10 10, 10 40, 40 40, 10 10))'&lt;/span&gt;
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STCentroid().ToString()

Results:

POINT (20 30)&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="52" alt="Spatial_6_2" width="57" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A265/Spatial_6_2_3.png" /&gt;  &lt;br /&gt;
&lt;em&gt;Note: SpatialViewer displays an individual point as an X. &lt;br /&gt;
&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;STWithin, STContains&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
Two OGC standard methods returns a 1 or 0 indicating whether all of the points of one instance exist entirely inside of another instance.  &lt;font face="monospace"&gt;STWithin()&lt;/font&gt; tests whether the base instance is inside of the parameter instance, while &lt;font face="monospace"&gt;STContains()&lt;/font&gt; tests whether the parameter instance is inside of the base instance.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g geometry = &lt;span class="str"&gt;'POLYGON ((10 10, 13 30, 30 30, 30 15, &lt;br /&gt;                                 10 10))'&lt;/span&gt;
&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @h geometry = &lt;span class="str"&gt;'LINESTRING (16 16, 16 24, 25 18)'&lt;br /&gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STContains(@h), @g.STWithin(@h)&lt;br /&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @h.STContains(@g), @h.STWithin(@g) &lt;br /&gt;
Results:

1     0&lt;br /&gt;&lt;br /&gt;0     1&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="41" alt="Spatial_6_3" width="51" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A265/Spatial_6_3_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;ST is {something}?&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
There are a number of OGC standard methods to check whether a given instance meets certain specifications: STIsClosed, STIsEmpty, STIsRing, STIsSimple, STIsValid&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CLOSED:&lt;/strong&gt; An instance is considered to be closed if the start point is the same as the end point.  By definition, a Polygon has to be closed, and a Point is not closed.  That really only leaves LineString.  For a collection of objects to be considered closed, all of its members must be closed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EMPTY:&lt;/strong&gt; A &lt;strong&gt;Geometry&lt;/strong&gt; can be initialized in a special way as to not contain any points.  In SQL terms, this is sort of like having a NULL value, except it really is an instantiated object.  For example, LINESTRING EMPTY is a valid LineString, but it has no points.  Another humorous example is POINT EMPTY, which initializes to a Point without a Point....  so it's kind of Pointless, right?  (thank you, I'm here all week, tip your waitress).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RING:&lt;/strong&gt; An instance is considered to be a ring if it is both Closed and Simple.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SIMPLE:&lt;/strong&gt; An instance is considered to be simple if it does not cross over itself or otherwise touch itself.  For example, a LineString forming the letter 'S' is simple because it never comes in contact with itself.  But, a LineString that forms a Figure-Eight (8) is &lt;u&gt;not&lt;/u&gt; simple because it would have to cross over itself.  Likewise, two circles (MultiPolygon) stacked on top of each other to form a Figure-Eight would &lt;u&gt;not&lt;/u&gt; be simple because they touch each other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VALID:&lt;/strong&gt; A Geometry can cross over itself, but it cannot legally trace over itself.  That is, picture a LineString that backtracks over itself at some point, kind of like how I write my letter "P".  This is not considered to be Valid.&lt;/p&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="41" alt="Spatial_6_4" width="51" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A265/Spatial_6_4_3.png" /&gt; &lt;/p&gt;
&lt;p&gt;Tip: SQL Server will allow an invalid &lt;strong&gt;Geometry&lt;/strong&gt; to be instantiated, and Microsoft has provided an extension method called &lt;font face="monospace"&gt;MakeValid()&lt;/font&gt; that will convert the invalid instance into a valid instance.  In the letter "P" example, instead of the vertical line going down and then back up (as I draw it by hand), the valid form will eliminate the duplication of points simply by start at the bottom and going up (so that the LineString never traces over itself).  If it's not possible to simplify a shape in this way so that there is only one continuous path, then it will be broken up into multiple valid shapes (i.e., a MultiLineString, etc).&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STX, STY, Z, M&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
Individual coordinates of a Point can be accessed via the OGC Standard properties STX and STY.  Three-dimensional Points also have a Z coordinate, which can be accessed via Microsoft's extended Z property.  Likewise, four-dimensional Points have a M (for Measure) coordinate, which can be accessed via Microsoft's extended M property.  If Z or M is not defined for a given point, then NULL will be returned.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g geometry = &lt;span class="str"&gt;'POINT(1 2)'&lt;/span&gt;
&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @h geometry = &lt;span class="str"&gt;'POINT(1 2 3 4)'&lt;/span&gt;
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STX, @g.STY, @g.Z, @g.M
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @h.STX, @h.STY, @h.Z, @h.M

Results:

1    2    &lt;span class="kwrd"&gt;NULL&lt;/span&gt;    &lt;span class="kwrd"&gt;NULL&lt;/span&gt;
1    2    3       4&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STPointOnSurface&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
When working with spatial data, especially without using a viewer, it can be kind of difficult to pick an arbitrary point that is inside of a Polygon (or on a LineString).  Thankfully, the OGC standard method &lt;font face="monospace"&gt;STPointOnSurface()&lt;/font&gt; does just that.  Given a &lt;strong&gt;Geometry&lt;/strong&gt; instance, it will return a somewhat random point that is guaranteed to be located within the interior of that instance.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g geometry = &lt;span class="str"&gt;'POLYGON((10 10, 14 15, 50 12, 45 30, &lt;br /&gt;                                10 30, 10 10))'&lt;/span&gt;
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STPointOnSurface().ToString()

Results:

POINT (23 25)&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="141" alt="Spatial_6_5" width="208" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A265/Spatial_6_5_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STSrid&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
All of my examples to this point have used the default Spatial Reference ID of 0 (for the &lt;strong&gt;Geometry&lt;/strong&gt; type) simply because I have not been specifying one.  The SRID is the mechanism that defines one geometry as being based on a different set of parameters than a geometry with a different SRID.  &lt;/p&gt;
&lt;p&gt;For example, you may have a set of shapes defined where each unit represents one meter, while another set of shapes is based on a reference system where each unit represents 1.5 inches.  It's totally legal to mix these shapes together the same column of a table in your database, provided that you assign a different SRID to each.  SQL Server does not need to know what units represent, because it will never permit the interaction of a shape from one SRID with a shape from another SRID.  &lt;/p&gt;
&lt;p&gt;The OGC standard property STSrid will get (or set) the SRID of the Geometry instance.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;-- @g will have the default SRID = 0&lt;br /&gt;&lt;/span&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g GEOMETRY = &lt;span class="str"&gt;'POLYGON((10 10, 10 40, 40 40, 10 10))'&lt;/span&gt;

&lt;span class="rem"&gt;-- @h is defined with SRID = 123&lt;br /&gt;&lt;/span&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @h GEOMETRY
    = GEOMETRY::STGeomFromText(&lt;span class="str"&gt;'POLYGON((10 10, 40 10, 
                                         40 40, 10 10))'&lt;/span&gt;, 
                               123)

&lt;span class="kwrd"&gt;select&lt;/span&gt; @g.STUnion(@h).ToString()

&lt;span class="rem"&gt;-- Returns NULL because of different SRIDs.  But, let's change&lt;br /&gt;&lt;br /&gt;-- @g to use SRID = 123&lt;/span&gt;

&lt;span class="kwrd"&gt;SET&lt;/span&gt; @g.STSrid = 123

&lt;span class="kwrd"&gt;select&lt;/span&gt; @g.STUnion(@h).ToString()

&lt;span class="rem"&gt;-- Returns POLYGON ((10 10, 40 10, 40 40, 10 40, 10 10))&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;Jason, What's Next?&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
Enough of this flat Earth stuff!  In the next part, I'll explore the Geography data type.  This is where things really start to get interesting.  Stay tuned!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;SQL Server 2008: Spatial Data, Part 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/27/sql-server-2008-spatial-data-part-2.aspx"&gt;SQL Server 2008: Spatial Data, Part 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/28/sql-server-2008-spatial-data-part-3.aspx"&gt;SQL Server 2008: Spatial Data, Part 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx"&gt;SQL Server 2008: Spatial Data, Part 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/07/sql-server-2008-spatial-data-part-5.aspx"&gt;SQL Server 2008: Spatial Data, Part 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/05/16/sql-server-2008-spatial-data-part-7.aspx"&gt;(next part) SQL Server 2008: Spatial Data, Part 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/06/23/sql-server-2008-spatial-data-part-8.aspx"&gt;SQL Server 2008: Spatial Data, Part 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f04%2f11%2fsql-server-2008-spatial-data-part-6.aspx"&gt;&lt;img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f04%2f11%2fsql-server-2008-spatial-data-part-6.aspx" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/39.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/04/11/sql-server-2008-spatial-data-part-6.aspx</guid>
            <pubDate>Sat, 12 Apr 2008 00:44:19 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/39.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/04/11/sql-server-2008-spatial-data-part-6.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/39.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/39.aspx</trackback:ping>
        </item>
        <item>
            <title>Using PIVOT and RANK Together</title>
            <link>http://jasonfollas.com/blog/archive/2008/04/10/using-pivot-and-rank-together.aspx</link>
            <description>&lt;p&gt;&lt;a target="_blank" href="http://www.michaeleatonconsulting.com/blog/"&gt;A friend of mine&lt;/a&gt; (name withheld, I didn't actually ask if I could blog this... ;-) asked for advice to what appears to be a simple problem until you try to implement it.  Consider the following somewhat normalized table:&lt;/p&gt;
&lt;table cellspacing="0" cellpadding="2" width="400" border="1"&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;th valign="top" width="133"&gt;
            &lt;p&gt;AccountNum&lt;/p&gt;
            &lt;/th&gt;
            &lt;th valign="top" width="133"&gt;
            &lt;p&gt;Name&lt;/p&gt;
            &lt;/th&gt;
            &lt;th valign="top" width="133"&gt;
            &lt;p&gt;Email&lt;/p&gt;
            &lt;/th&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;0851774002  &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;John Doe   &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;jd@foo.com&lt;/p&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;0851774003    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;John Doe    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;jd@foo.com&lt;/p&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;0851774001    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;John Doe    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;jd@foo.com&lt;/p&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;0851774100    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;John Doe    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;jd@foo.com&lt;/p&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;0851693000    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;Bob Public    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;bob@bar.com&lt;/p&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;1138299000    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;Jane Doe    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;JaneD@baz.com&lt;/p&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;1353452000    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;Jane Doe    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;JaneD@baz.com&lt;/p&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;1028030000    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;Jane Doe  &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;JaneD@baz.com&lt;/p&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;0851636000    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;Jane Doe    &lt;/p&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="133"&gt;
            &lt;p&gt;JaneD@baz.com&lt;/p&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;
What he wanted was to collapse the data to one row per person, with a column for each Account Number.  That is, he needed to pivot the table.&lt;/p&gt;
&lt;p&gt;When you pivot a table, unique values in the source column that you pivot on become new columns in the resulting table.  So, in this case, it would not make sense to pivot on the AccountNum column, because the result would be a new column named [0851774002], another one named [0851774003], etc.&lt;/p&gt;
&lt;p&gt;Instead, an intermediate step needed to be performed that introduced a value that &lt;em&gt;could&lt;/em&gt; be pivoted on.  This value needed to be consistent across the individual people (so that the first record for everybody contained the same value in this new column, the second record for everybody contained the same value, etc).&lt;/p&gt;
&lt;p&gt;SQL Server 2005 introduced Ranking functions that provide the ability to rank a record within a partition.  In this case, we can use RANK() to assign a unique number for each record, and partition by the person's name (so that the RANK will reset for each person).  By prefixing some text to the rank number, we end up with something like:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt;    Name,
          Email,
          AccountNum,
          &lt;span class="str"&gt;'AccountNum'&lt;/span&gt; +
          &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;br /&gt;
               &lt;font size="3"&gt;&lt;strong&gt;RANK() &lt;span class="kwrd"&gt;OVER&lt;/span&gt; 
               ( PARTITION &lt;span class="kwrd"&gt;BY&lt;/span&gt; Name, Email 
                 &lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; AccountNum )&lt;/strong&gt;&lt;/font&gt; &lt;br /&gt;
          &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;VARCHAR&lt;/span&gt;(10)) R
          &lt;span class="kwrd"&gt;FROM&lt;/span&gt;     myTable
Results:

Name        Email          AccountNum  R
=========== ============== ==========  ============
Bob &lt;span class="kwrd"&gt;&lt;font color="#000000"&gt;Public&lt;/font&gt;&lt;/span&gt;  bob@bar.com    0851693000  AccountNum1
Jane Doe    JaneD@baz.com  0851636000  AccountNum1
Jane Doe    JaneD@baz.com  1028030000  AccountNum2
Jane Doe    JaneD@baz.com  1138299000  AccountNum3
Jane Doe    JaneD@baz.com  1353452000  AccountNum4
John Doe    jd@foo.com     0851774001  AccountNum1
John Doe    jd@foo.com     0851774002  AccountNum2
John Doe    jd@foo.com     0851774003  AccountNum3
John Doe    jd@foo.com     0851774100  AccountNum4&lt;/pre&gt;
&lt;pre class="csharpcode"&gt; &lt;/pre&gt;
&lt;p&gt;The new column (R) is the concatenation of the literal string "AccountNum" and the string representation of the number that the RANK function returned.  But the bigger point is that now this column can be used for pivoting, and result in a series of new columns called [AccountNum1], [AccountNum2], [AccountNum3], etc.&lt;/p&gt;
&lt;p&gt;Pivoting in SQL Server 2005 requires explicit declaration of values as a column list.  In this case, we can't just say "Pivot on the R column", but rather must say "Pivot on the R column, and make new columns only for these specific values".  This restriction is a little bit of a downside because we need knowledge of the values in the column.  Or, in this case, we need to know how many possible Account Numbers a person could possibly have so that we create enough columns in the result.&lt;/p&gt;
&lt;p&gt;The entire solution is as follows:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt;  *
&lt;span class="kwrd"&gt;FROM&lt;/span&gt;    ( &lt;span class="kwrd"&gt;SELECT&lt;/span&gt;    Name,
                    Email,
                    AccountNum,
                    &lt;span class="str"&gt;'AccountNum'&lt;/span&gt;
                    + &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(RANK() 
          &lt;span class="kwrd"&gt;OVER&lt;/span&gt; ( PARTITION &lt;span class="kwrd"&gt;BY&lt;/span&gt; Name, Email 
                 &lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; AccountNum ) &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;VARCHAR&lt;/span&gt;(10)) R
          &lt;span class="kwrd"&gt;FROM&lt;/span&gt;      myTable
        ) &lt;span class="kwrd"&gt;AS&lt;/span&gt; rankedSource 
PIVOT 
( 
    &lt;span class="kwrd"&gt;MAX&lt;/span&gt;(AccountNum) 
    &lt;span class="kwrd"&gt;FOR&lt;/span&gt; R &lt;span class="kwrd"&gt;IN&lt;/span&gt; 
    ( [AccountNum1], [AccountNum2], [AccountNum3],
      [AccountNum4], [AccountNum5], [AccountNum6],
      [AccountNum7], [AccountNum8], [AccountNum9],
      [AccountNum10] ) 
) &lt;span class="kwrd"&gt;AS&lt;/span&gt; pivottable&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;And the results (showing only two of the AccountNum columns, even though there are actually 10)&lt;/p&gt;
&lt;pre class="csharpcode"&gt;Name       Email         AccountNum1  AccountNum2&lt;br /&gt;========== ============= ===========  ===========
Bob &lt;span class="kwrd"&gt;&lt;font color="#000000"&gt;Public&lt;/font&gt;&lt;/span&gt; bob@bar.com   0851693000   &lt;span class="kwrd"&gt;NULL&lt;/span&gt;
Jane Doe   JaneD@baz.com 0851636000   1028030000
John Doe   jd@foo.com    0851774001   0851774002&lt;/pre&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/38.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/04/10/using-pivot-and-rank-together.aspx</guid>
            <pubDate>Thu, 10 Apr 2008 15:03:03 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/38.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/04/10/using-pivot-and-rank-together.aspx#feedback</comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/38.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/38.aspx</trackback:ping>
        </item>
        <item>
            <title>SQL Server 2008: Spatial Data, Part 5</title>
            <link>http://jasonfollas.com/blog/archive/2008/04/07/sql-server-2008-spatial-data-part-5.aspx</link>
            <description>&lt;p&gt;In &lt;a target="_blank" href="http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx"&gt;the previous part&lt;/a&gt; of this series, I demonstrated instance methods that transformed a single &lt;strong&gt;Geometry&lt;/strong&gt; type into another useful &lt;strong&gt;Geometry&lt;/strong&gt;.  In this post, we'll go a step further and show methods that allow two or more instances to interact with one another in order to produce a new &lt;strong&gt;Geometry&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For my baseline, I'll use two Polygons that overlap each other:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g geometry 
        = &lt;span class="str"&gt;'POLYGON((10 10, 40 10, 40 40, 10 40, 10 10))'&lt;/span&gt;
&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @h geometry 
        = &lt;span class="str"&gt;'POLYGON((30 30, 50 30, 50 50, 30 50, 30 30))'&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="63" alt="Spatial_5_1" width="68" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A67F/Spatial_5_1_3.png" /&gt; &lt;/p&gt;
&lt;h2&gt;STDifference&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
&lt;font face="monospace"&gt;STDifference()&lt;/font&gt; returns a new instance consisting of all points from the base instance that do not contain points from the parameter instance.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;span class="kwrd"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STDifference(@h).ToString();

&lt;span class="kwrd"&gt;&lt;font color="#000000"&gt;Result&lt;/font&gt;&lt;/span&gt;:

POLYGON ((10 10, 40 10, 40 30, 30 30, 30 40, 10 40, 10 10))&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="63" alt="Spatial_5_2" width="68" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A67F/Spatial_5_2_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STIntersection&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
&lt;font face="monospace"&gt;STIntersection()&lt;/font&gt; returns a new instance containing only the points that are in common between the base instance and the parameter instance.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STIntersection(@h).ToString();

&lt;span class="kwrd"&gt;Result&lt;/span&gt;:

POLYGON ((30 30, 40 30, 40 40, 30 40, 30 30))&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="63" alt="Spatial_5_3" width="68" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A67F/Spatial_5_3_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STSymDifference&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
&lt;font face="monospace"&gt;STSymDifference()&lt;/font&gt; returns a new instance containing only the points that are unique to both the base instance and the parameter instance (i.e., it excludes the points that &lt;font face="monospace"&gt;STIntersection()&lt;/font&gt; would return).&lt;/p&gt;
&lt;p&gt;In this case, the set of points is actually two different Polygons.  Because &lt;font face="monospace"&gt;STSymDifference()&lt;/font&gt; needs to return a single instance of &lt;em&gt;something&lt;/em&gt;, it will wrap those two Polygons into a collection (MultiPolygon).&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STSymDifference(@h).ToString();

&lt;span class="kwrd"&gt;Result&lt;/span&gt;:

MULTIPOLYGON (((40 30, 50 30, 50 50, 30 50, 30 40, 40 40, 40 30)), 
              ((10 10, 40 10, 40 30, 30 30, 30 40, 10 40, 10 10)))&lt;/pre&gt;
&lt;p&gt; &lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="63" alt="Spatial_5_4" width="68" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A67F/Spatial_5_4_6.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STUnion&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
&lt;font face="monospace"&gt;STUnion()&lt;/font&gt; returns a new instance containing all of the points of the base instance and the parameter instance merged together.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STUnion(@h).ToString();

Results:

POLYGON ((10 10, 40 10, 40 30, 50 30, 50 50, 30 50, &lt;br /&gt;          30 40, 10 40, 10 10))&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="63" alt="Spatial_5_5" width="68" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A67F/Spatial_5_5_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;Blended Types&lt;/h2&gt;
&lt;p&gt;&lt;br /&gt;
The instance methods described above do not work just for Polygons.  You can actually use them on different types, or even collections of different types.  &lt;/p&gt;
&lt;p&gt;For instance, if we look at the results of using a LineString as the base instance and a Polygon as the parameter instance, &lt;font face="monospace"&gt;STDifference()&lt;/font&gt; will return a MultiLineString constisting of the points from the original LineString that do not lie within the Polygon: &lt;br /&gt;
&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g geometry = &lt;span class="str"&gt;'LINESTRING(9 9, 40 40)'&lt;/span&gt;
&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @h geometry = &lt;span class="str"&gt;'POLYGON((15 15, 15 30, 30 30, 30 15, 15 15))'&lt;/span&gt;
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STDifference(@h).ToString();

Results:

MULTILINESTRING ((40 40, 30 30), (15 15, 9 9))&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="51" alt="Spatial_5_6" width="60" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A67F/Spatial_5_6_6.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;font face="monospace"&gt;STIntersection()&lt;/font&gt; will return the points from the original LineString that do lie within the Polygon: &lt;br /&gt;
&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STIntersection(@h).ToString();

Results:

LINESTRING (30 30, 15 15)&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="51" alt="Spatial_5_7" width="60" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A67F/Spatial_5_7_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;font face="monospace"&gt;STUnion()&lt;/font&gt; cannot determine a single common Geometry type, so it will return a mixed collection of types:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STUnion(@h).ToString();

Results:

GEOMETRYCOLLECTION 
(
     LINESTRING (40 40, 30 30), 
     POLYGON ((15 15, 30 15, 30 30, 15 30, 15 15)), 
     LINESTRING (15 15, 9 9)
)&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="51" alt="Spatial_5_8" width="60" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart5_A67F/Spatial_5_8_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;SQL Server 2008: Spatial Data, Part 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/27/sql-server-2008-spatial-data-part-2.aspx"&gt;SQL Server 2008: Spatial Data, Part 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/28/sql-server-2008-spatial-data-part-3.aspx"&gt;SQL Server 2008: Spatial Data, Part 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx"&gt;SQL Server 2008: Spatial Data, Part 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/11/sql-server-2008-spatial-data-part-6.aspx"&gt;(next part) SQL Server 2008: Spatial Data, Part 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/05/16/sql-server-2008-spatial-data-part-7.aspx"&gt;SQL Server 2008: Spatial Data, Part 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/06/23/sql-server-2008-spatial-data-part-8.aspx"&gt;SQL Server 2008: Spatial Data, Part 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f04%2f07%2fsql-server-2008-spatial-data-part-5.aspx"&gt;&lt;img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f04%2f07%2fsql-server-2008-spatial-data-part-5.aspx" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/37.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/04/07/sql-server-2008-spatial-data-part-5.aspx</guid>
            <pubDate>Mon, 07 Apr 2008 15:51:00 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/37.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/04/07/sql-server-2008-spatial-data-part-5.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/37.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/37.aspx</trackback:ping>
        </item>
        <item>
            <title>SQL Server 2008: Spatial Data, Part 4</title>
            <link>http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx</link>
            <description>&lt;p&gt;In this, the 4th post in a series (&lt;a target="_blank" href="http://jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;Part 1&lt;/a&gt;, &lt;a target="_blank" href="http://jasonfollas.com/blog/archive/2008/03/27/sql-server-2008-spatial-data-part-2.aspx"&gt;Part 2&lt;/a&gt;, &lt;a target="_blank" href="http://jasonfollas.com/blog/archive/2008/03/28/sql-server-2008-spatial-data-part-3.aspx"&gt;Part 3&lt;/a&gt;) on the new spatial data types in SQL Server 2008, I'll explain some of the methods that are used to transform a single &lt;strong&gt;Geometry&lt;/strong&gt; instance into another useful &lt;strong&gt;Geometry&lt;/strong&gt; instance.  Note that I'm using &lt;strong&gt;Geometry&lt;/strong&gt; for simplicity, &lt;strike&gt;but these techniques also work with &lt;strong&gt;Geography&lt;/strong&gt;&lt;/strike&gt;.  &lt;em&gt;&lt;strong&gt;Edit&lt;/strong&gt;: Ok, after starting to take a hard look at Geography, I realized that A LOT of the methods that Geometry offers are not implemented in Geography.  :-/  Sorry to mislead you.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;fieldset style="PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; PADDING-TOP: 5px"&gt;&lt;legend&gt;Useful Tip&lt;/legend&gt;To help me to visualize geometries as I explore the capabilities of the spatial data type, I've been using &lt;a target="_blank" href="http://www.codeplex.com/SpatialViewer"&gt;SpatialViewer&lt;/a&gt;, a tool written by fellow SQL Server MVP &lt;a target="_blank" href="http://sqlblogcasts.com/blogs/simons/"&gt;Simon Sabin&lt;/a&gt;. &lt;/fieldset&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;As a baseline, I'll be using my highest quality, hand-drawn letter "S".  This LineString is a simple geometry (it does not cross over itself), but is not closed (the starting and ending points are not the same).&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g GEOMETRY
&lt;span class="kwrd"&gt;SET&lt;/span&gt; @g = &lt;span class="str"&gt;'LINESTRING (  69 26, 69 23, 69 21, 67 20, 65 20, 
          63 18, 58 17, 52 17, 51 17, 49 17, 45 18, 44 20, 
          44 21, 42 26, 42 29, 42 32, 42 35, 43 35, 47 38, 
          50 41, 55 42, 58 42, 65 44, 66 44, 67 44, 68 45, 
          69 47, 70 48, 70 50, 71 51, 70 56, 68 60, 68 63, 
          66 65, 65 66, 63 68, 60 71, 59 71, 57 71, 55 71, 
          51 69, 45 65, 44 63, 42 62, 41 59, 41 57, 41 56, 
          41 54, 42 53 )'&lt;/span&gt;&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[






.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;style type="text/css"&gt;&lt;![CDATA[







.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="86" alt="Spatial_S_1" width="119" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_1_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STEnvelope&lt;/h2&gt;
&lt;p&gt;A bounding box is a rectangle that is defined by the combination of minimum and maximum X and Y values found in a given &lt;strong&gt;Geometry&lt;/strong&gt; instance.  The OGC standard method &lt;font face="monospace"&gt;STEnvelope()&lt;/font&gt; returns the bounding box (a Polygon) for the instance on which it is invoked.  All points of the original instance lie within the new Polygon.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: In the following T-SQL examples, I will trail the method calls with a ToString() so that we can examine the resulting WKT.  Normally, you would just work with the geometry (i.e., you wouldn't need call ToString())..&lt;/em&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STEnvelope().ToString()

Result:

POLYGON ((41 17, 71 17, 71 71, 41 71, 41 17))&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="92" alt="Spatial_S_4" width="116" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_4_3.png" /&gt; &lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: In these pictures, the original geometry is drawn in black, and the new geometry is superimposed in red.  If the new geometry has area, then that area will appear as light brown or tan.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STConvexHull&lt;/h2&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="95" alt="biten_apple_xl" width="110" align="right" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/biten_apple_xl_3.jpg" /&gt; If something is convex, then it is thought of as having a surface that is curved or rounded outward from the center.  For example, the side of an apple is convex.  If you happen to take a bite out of the apple, however, then that the portion that has been removed is considered to be concave.  To "correct" the concave portion of the half-eaten apple, we could fill in the hole with something like clay, but we would only need to restore enough material so that there was a straight line from the top of the hole to the bottom (and the same for side to side).&lt;/p&gt;
&lt;p&gt;The OGC standard method &lt;font face="monospace"&gt;STConvexHull()&lt;/font&gt; returns the minimal bounding convex Polygon for a geometry instance.  That is, any convex parts of the original instance will be preserved, and any concave parts will be "filled in", so to speak, by defining a straight line to bypass them.  Like &lt;font face="monospace"&gt;STEnvelope()&lt;/font&gt;, all points of the original instance lie within the new Polygon. &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STConvexHull().ToString()

Result:

POLYGON ((71 51, 70 56, 68 63, 66 65, 65 66, 63 68, 60 71, 
          59 71, 57 71, 55 71, 51 69, 45 65, 42 62, 41 59, 
          41 57, 41 56, 41 54, 42 26, 44 20, 45 18, 49 17, 
          51 17, 52 17, 58 17, 63 18, 67 20, 69 21, 71 51))&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="93" alt="Spatial_S_2" width="125" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_2_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STBuffer&lt;/h2&gt;
&lt;p&gt;What if you have an existing shape, and you want to make it bigger, but preserve the general... uh, shape of it?  Then you would use the OGC standard method &lt;font face="monospace"&gt;STBuffer(distance)&lt;/font&gt;!  This method returns a Polygon that inflates the &lt;u&gt;area&lt;/u&gt; around the original geometry instance by a number of units that you provide.  Note that if the original instance is a Point, then the result will be a circle with a radius of the number of units that you provided.  It is also possible to deflate an existing Polygon by supplying a negative buffer value.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STBuffer(5).ToString()

Result:

POLYGON ((49 12, 51 12, 52 12, 58 12, 
          58.246719360351562 12.00609016418457, 
          58.492688179016113 12.024333953857422, 
          58.737458229064941 12.054683685302734, 
          58.98058032989502 12.097097396850586, 
          &lt;em&gt;(... snipped for clarity ...)
&lt;/em&gt;          48.693824768066406 12.009382247924805, 49 12))&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="93" alt="Spatial_S_3" width="117" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_3_3.png" /&gt; &lt;/p&gt;
&lt;p&gt;What happens if the amount of buffering forces the inflated area to overlap with itself?  The resulting Polygon may develop holes (the area within a hole is still considered part of the exterior of the Polygon).  Here the buffer increases to 8, creating a hole:&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STBuffer(8).ToString()

Result:

POLYGON (( exterior &lt;em&gt;ring points&lt;/em&gt; ), ( &lt;em&gt;interior ring points&lt;/em&gt; ))&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="93" alt="Spatial_S_5" width="125" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_5_3.png" /&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;STExteriorRing, STInteriorRingN&lt;/h2&gt;
&lt;p&gt;The result of the very last example was a Polygon with one interior hole.  OGC standards provide a way to access the various components of a polygon (exterior ring and interior rings) individually.  &lt;br /&gt;
&lt;br /&gt;
&lt;font face="monospace"&gt;STExteriorRing()&lt;/font&gt; returns just the closed LineString of the Polygon itself.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STBuffer(8).STExteriorRing().ToString()

Result:

LINESTRING (&lt;strong&gt;49 9&lt;/strong&gt;, 51 9, 52 9, 58 9, 
            58.394750595092773 9.0097446441650391, 
            &lt;em&gt;(... snipped for clarity ...)
&lt;/em&gt;            48.022533416748047 9.0599403381347656, 
            48.5101203918457 9.0150127410888672, &lt;strong&gt;49 9&lt;/strong&gt;)&lt;/pre&gt;
&lt;p&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="93" alt="Spatial_S_6" width="124" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_6_3.png" /&gt; &lt;/p&gt;
&lt;p&gt;Similarly, &lt;font face="monospace"&gt;STInteriorRingN(n)&lt;/font&gt; is used to return the closed LineString of interior rings.  &lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: This method is accessing a member of a GeomCollection by index, which I plan to cover in a later post.  What's important to know now is that indexing starts at 1, and you will get an error if you specify an index that does not actually exist in the collection.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;em&gt;&lt;/em&gt;&lt;/pre&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.STBuffer(8).STInteriorRingN(1).ToString()

Result:

LINESTRING (48.893725268961383 48.937175344014442, 
            49.084709167480469 49.279642105102539, 
            &lt;em&gt;(... snipped for clarity ...)&lt;/em&gt;
            48.893725268961383 48.937175344014442)&lt;/pre&gt;
&lt;pre class="csharpcode"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="89" alt="Spatial_S_7" width="123" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_7_3.png" /&gt; &lt;/pre&gt;
&lt;pre class="csharpcode"&gt; &lt;/pre&gt;
&lt;h2&gt;Extended Methods&lt;/h2&gt;
&lt;p&gt;Microsoft has implemented some additional methods on &lt;strong&gt;Geometry&lt;/strong&gt; instances to perform tasks that are beyond the scope of the OGC standards.&lt;/p&gt;
&lt;p&gt;&lt;font face="monospace"&gt;Reduce(tolerance)&lt;/font&gt; is a method that will simplify a given instance using the &lt;a target="_blank" href="http://everything2.com/index.pl?node_id=859282"&gt;Douglas-Peucker algorithm&lt;/a&gt;.  The result is a an approximation of the original instance containing a fewer number of points.  The accuracy of the new shape improves as the provided tolerance value approaches zero, but more points are necessary to provide that accuracy.  &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.Reduce(5).ToString()

Result:

LINESTRING (69 26, 49 17, 42 26, 42 35, 70 48, 
            60 71, 42 62, 42 53)
&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="90" alt="Spatial_S_8" width="124" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_8_3.png" /&gt; &lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;font face="monospace"&gt;BufferWithTolerance(distance, tolerance, relative)&lt;/font&gt; is very similar in function to &lt;font face="monospace"&gt;STBuffer()&lt;/font&gt;, only it gives you more control over the accuracy of the result.  The tolerance parameter, like in the Reduce() method, controls the amount of acceptable error a resulting line segment can be from what is ideal.  &lt;/p&gt;
&lt;p&gt;To understand how tolerance factors into the result, then picture a Point that is buffered into a circle.  To truly represent a circle, we would need a Polygon consisting of a infinite number of points.  This is not practical, but by allowing for error, we can generate a regular Polygon with any number of sides that behaves like a circle.  Smaller tolerances result in Polygons with a very large number of very small sides.  Likewise, larger tolerances result in Polygons with fewer sides, and your circle will start to resemble things like octagons, hexagons, and even squares.&lt;/p&gt;
&lt;p&gt;Compare the image below generated with a less accurate tolerance (left image), to the &lt;font face="monospace"&gt;STBuffer(8)&lt;/font&gt; example from above (right image).  The difference is subtle, primarily because the scale that we're working with is pretty small.  But, one difference to note is that the ends of the inflated "S" on the left are straight while the ones on the right are rounded.  Overall, the the image on the right has more points, and thus is a more accurate the original "S" shape than the image on the left.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @g.BufferWithTolerance(8, 10, 0).ToString()

Result:

POLYGON (( &lt;em&gt;ring points&lt;/em&gt; ), ( &lt;em&gt;hole points&lt;/em&gt; ))&lt;/pre&gt;
&lt;pre class="csharpcode"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="97" alt="Spatial_S_9" width="131" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_9_3.png" /&gt; &lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="93" alt="Spatial_S_5" width="125" border="0" src="http://jasonfollas.com/blog/images/jasonfollas_com/blog/WindowsLiveWriter/SQLServer2008SpatialDataPart4_B98F/Spatial_S_5_6.png" /&gt; &lt;/pre&gt;
&lt;p&gt;&lt;a href="http://www.jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;SQL Server 2008: Spatial Data, Part 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/27/sql-server-2008-spatial-data-part-2.aspx"&gt;SQL Server 2008: Spatial Data, Part 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/28/sql-server-2008-spatial-data-part-3.aspx"&gt;SQL Server 2008: Spatial Data, Part 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/07/sql-server-2008-spatial-data-part-5.aspx"&gt;(next part) SQL Server 2008: Spatial Data, Part 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/11/sql-server-2008-spatial-data-part-6.aspx"&gt;SQL Server 2008: Spatial Data, Part 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/05/16/sql-server-2008-spatial-data-part-7.aspx"&gt;SQL Server 2008: Spatial Data, Part 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/06/23/sql-server-2008-spatial-data-part-8.aspx"&gt;SQL Server 2008: Spatial Data, Part 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f04%2f03%2fsql-server-2008-spatial-data-part-4.aspx"&gt;&lt;img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fjasonfollas.com%2fblog%2farchive%2f2008%2f04%2f03%2fsql-server-2008-spatial-data-part-4.aspx" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/36.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx</guid>
            <pubDate>Fri, 04 Apr 2008 00:31:31 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/36.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx#feedback</comments>
            <slash:comments>6</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/36.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/36.aspx</trackback:ping>
        </item>
        <item>
            <title>SQL Server 2008: Spatial Data, Part 3</title>
            <link>http://jasonfollas.com/blog/archive/2008/03/28/sql-server-2008-spatial-data-part-3.aspx</link>
            <description>&lt;p&gt;In the previous parts of this series (&lt;a title="SQL Server 2008: Spatial Data, Part 1" target="_blank" href="http://jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;Part 1&lt;/a&gt;, &lt;a title="SQL Server 2008: Spatial Data, Part 2" target="_blank" href="http://jasonfollas.com/blog/archive/2008/03/27/sql-server-2008-spatial-data-part-2.aspx"&gt;Part 2&lt;/a&gt;), I introduced the &lt;strong&gt;Geometry&lt;/strong&gt; and &lt;strong&gt;Geography&lt;/strong&gt; data types, the various subclasses (Point, LineString, Polygon, etc), and demonstrated a little bit of the Well-Known Text (WKT) syntax.  These two posts were primarily informational in nature, and didn't touch SQL Server at all.  Let's change that!&lt;/p&gt;
&lt;h2&gt;Instantiating the UDT&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Geometry&lt;/strong&gt; and &lt;strong&gt;Geography&lt;/strong&gt; data types are implemented as User Defined Types (UDT) written in .NET.  They are automatically installed with the server, and are available for use by any SQL Server 2008 database.  For this post, I will use the &lt;strong&gt;Geometry&lt;/strong&gt; type to demonstrate capability, &lt;strike&gt;but the &lt;strong&gt;Geography&lt;/strong&gt; type has the very same capabilities&lt;/strike&gt; (the primary difference between the two types is how distance and area is calculated). &lt;em&gt; &lt;strong&gt;Edit&lt;/strong&gt;: After taking a hard look at Geography, I realized that a lot of methods that Geometry offers are not implemented.  So, I was incorrect in my assertion that everything available in Geometry is also available in Geography.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Any variable, parameter, or table column can be declared as type &lt;strong&gt;Geometry&lt;/strong&gt;.  Notice that the type name is not case sensitive.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&lt;/span&gt; &lt;/pre&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g Geometry&lt;/pre&gt;
&lt;pre class="csharpcode"&gt; &lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;
&lt;p&gt;As mentioned before, &lt;strong&gt;Geometry&lt;/strong&gt; by itself is abstract.  In order to do something interesting, you need to instantiate the type as one of the concrete subclasses of the &lt;strong&gt;Geometry&lt;/strong&gt; type.  Adhering to the OGC standards, you can use the &lt;font face="monospace"&gt;STGeomFromText()&lt;/font&gt; static method to parse data provided in valid WKT syntax, and then your variable will take on the characteristics of the defined subclass (i.e., it will be instantiated as one of the concrete types).  Note that in SQL syntax, CLR method names &lt;strong&gt;&lt;u&gt;are&lt;/u&gt;&lt;/strong&gt; case sensitive, so &lt;font face="monospace"&gt;stgeomfromtext()&lt;/font&gt; will not work.&lt;/p&gt;
&lt;p&gt;Now, in C# and VB.NET, it is common to invoke static methods of a type using a dot notation, as in &lt;font face="monospace"&gt;geometry.STGeomFromText()&lt;/font&gt;.  However, this is not the case in T-SQL, because that particular syntax implies that you're calling a User Defined Function belonging to the "geometry" schema.  Instead, when calling a static method belonging to a type, you separate the type name and the method name using two colons (::).&lt;/p&gt;
&lt;p&gt;For example, to use &lt;font face="monospace"&gt;STGeomFromText()&lt;/font&gt; to create a LineString, you would do the following:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g Geometry
&lt;span class="kwrd"&gt;SET&lt;/span&gt; @g = Geometry::STGeomFromText(&lt;span class="str"&gt;'LINESTRING(0 0, 10 10, 21 2)'&lt;/span&gt;,&lt;br /&gt;                                  0)&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;You might be wondering, "What's that weird zero at the end all about?"  In &lt;a title="SQL Server 2008: Spatial Data, Part 1" target="_blank" href="http://jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;Part 1&lt;/a&gt;, I touched on the concept of a Spatial Reference System that defines things such as the unit of measure and the dimensions of the world being represented, etc.  The zero in this method call is the Spatial Reference ID (SRID) parameter, and it is required that the SRID being used is declared alongside any piece of spatial information.  SQL Server 2008 will not perform calculations on pieces of spatial information that belong to separate Spatial Reference Systems (because one system may use centimeters, and another may use miles, and SQL Server simply does not have the means to automatically convert units).  For the &lt;strong&gt;Geometry&lt;/strong&gt; type, it is common to just use zero for the SRID when all of your data is from the same Spatial Reference System (&lt;strong&gt;Geography&lt;/strong&gt; uses 4326 as the default, to be explained later).&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Note: A linebreak was inserted between the parameters in order to make it obvious to the eye.  There is no requirement in T-SQL that requires the linebreak.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Since we were declaring a LineString specifically, we could have also used a static method that only accepts valid LineStrings as input:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g Geometry
&lt;span class="kwrd"&gt;SET&lt;/span&gt; @g = Geometry::STLineFromText(&lt;span class="str"&gt;'LINESTRING(0 0, 10 10, 21 2)'&lt;/span&gt;,&lt;br /&gt;                                  0)&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;If we tried to supply something that was not valid WKT for a LineString, like &lt;font face="monospace"&gt;POINT(0 0)&lt;/font&gt;,then a .NET FormatException would have been thrown:&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#ff0000" size="1"&gt;Msg 6522, Level 16, State 1, Line 2 &lt;br /&gt;
A .NET Framework error occurred during execution of user-defined routine or aggregate "geometry": &lt;br /&gt;
System.FormatException: 24142: Expected LINESTRING at position 0. The input has POINT(0 0). &lt;br /&gt;
System.FormatException: &lt;br /&gt;
   at Microsoft.SqlServer.Types.OpenGisWktReader.RecognizeToken(String token) &lt;br /&gt;
   at Microsoft.SqlServer.Types.OpenGisWktReader.ParseLineStringTaggedText() &lt;br /&gt;
   at Microsoft.SqlServer.Types.OpenGisWktReader.ReadLineString() &lt;br /&gt;
   at Microsoft.SqlServer.Types.SqlGeometry.STLineFromText(SqlChars lineStringTaggedText, Int32 srid)&lt;/font&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The entire list of OGC standard static methods on the Geometry type include: STGeomFromText, STPointFromText, STLineFromText, STPolyFromText, STMPointFromText, STMLineFromText, STMPolyFromText, STGeomCollFromText, STGeomFromWKB, STPointFromWKB, STLineFromWKB, STPolyFromWKB, STMPointFromWKB, STMLineFromWKB, STMPolyFromWKB, and STGeomCollFromWKB.  &lt;em&gt;Note: The "FromWKB" methods are the "Well-Known Binary" equivalents to the "FromText" methods, and accept a specially crafted array of bytes as the input.  Using binary representations for initialization improves performance, but is much harder for humans to work with and comprehend.&lt;/em&gt; &lt;/p&gt;
&lt;p&gt;There is one other trick to be aware of when initializing a spatial data type.  By contract, a UDT in SQLCLR must support serialization to and from a string.  That is, a UDT must implement a &lt;font face="monospace"&gt;ToString()&lt;/font&gt; method and a &lt;font face="monospace"&gt;Parse(string)&lt;/font&gt; method that are called implicitly when a conversion is required, but these two methods can also be explicitly invoked.  It just so happens that the string format used by the &lt;strong&gt;Geometry&lt;/strong&gt; type is WKT (actually, the &lt;font face="monospace"&gt;Parse()&lt;/font&gt; method is identical to STGeomFromText() with an implicit SRID of zero). &lt;/p&gt;
&lt;p&gt;All of the following are functionally equivalent:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g Geometry

&lt;span class="kwrd"&gt;SET&lt;/span&gt; @g = Geometry::STGeomFromText(&lt;span class="str"&gt;'LINESTRING(0 0, 10 10, 21 2)'&lt;/span&gt;,&lt;br /&gt;                                  0)
&lt;span class="kwrd"&gt;SET&lt;/span&gt; @g = Geometry::Parse(&lt;span class="str"&gt;'LINESTRING(0 0, 10 10, 21 2)'&lt;/span&gt;)
&lt;span class="kwrd"&gt;SET&lt;/span&gt; @g = &lt;span class="str"&gt;'LINESTRING(0 0, 10 10, 21 2)'&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;
&lt;p&gt;&lt;em&gt;Note: The third example implicitly invokes the Parse(string) method.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;  &lt;/p&gt;
&lt;h2&gt;Working with a UDT Instance&lt;/h2&gt;
&lt;p&gt;Once you have created an instance, then you can use a dot notation to access instance properties and methods:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @g Geometry
&lt;span class="kwrd"&gt;SET&lt;/span&gt; @g = Geometry::STLineFromText(&lt;span class="str"&gt;'LINESTRING(0 0, 10 10, 21 2)'&lt;/span&gt;,&lt;br /&gt;                                  0)

&lt;span class="kwrd"&gt;PRINT&lt;/span&gt; @g.STLength() &lt;font color="#008000"&gt;-- Result: 27.7436&lt;/font&gt;&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Up to this point, my example code has been declaring and instantiating spatial data as variables within a batch.  But, columns in a table can also be declared as spatial, and queries can access instance properties and methods:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;CREATE&lt;/span&gt; &lt;span class="kwrd"&gt;TABLE&lt;/span&gt; #demo
(
    ID    &lt;span class="kwrd"&gt;INT&lt;/span&gt; &lt;span class="kwrd"&gt;IDENTITY&lt;/span&gt;(1,1) &lt;span class="kwrd"&gt;NOT&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;,
    G     GEOMETRY
)

INSERT &lt;span class="kwrd"&gt;INTO&lt;/span&gt; #demo (G)
&lt;span class="kwrd"&gt;VALUES&lt;/span&gt;    (&lt;span class="str"&gt;'LINESTRING(0 0, 10 10, 21 2)'&lt;/span&gt;),
          (&lt;span class="str"&gt;'LINESTRING(1 1, 11 11, 22 3)'&lt;/span&gt;),
          (&lt;span class="str"&gt;'POINT(5 5)'&lt;/span&gt;)

&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; ID, G.ToString() &lt;span class="kwrd"&gt;AS&lt;/span&gt; WKT, G.STLength() &lt;span class="kwrd"&gt;AS&lt;/span&gt; LENGTH
&lt;span class="kwrd"&gt;FROM&lt;/span&gt; #demo

&lt;span class="kwrd"&gt;DROP&lt;/span&gt; &lt;span class="kwrd"&gt;TABLE&lt;/span&gt; #demo&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;
&lt;p&gt;Results:&lt;/p&gt;
&lt;table cellspacing="0" cellpadding="2" width="400" border="1"&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td valign="top"&gt;&lt;strong&gt;ID&lt;/strong&gt;&lt;/td&gt;
            &lt;td valign="top"&gt;&lt;strong&gt;WKT&lt;/strong&gt;&lt;/td&gt;
            &lt;td valign="top"&gt;&lt;strong&gt;LENGTH&lt;/strong&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top"&gt;1&lt;/td&gt;
            &lt;td valign="top"&gt;LINESTRING (0 0, 10 10, 21 2)&lt;/td&gt;
            &lt;td valign="top"&gt;27.7436061324664&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top"&gt;2&lt;/td&gt;
            &lt;td valign="top"&gt;LINESTRING (1 1, 11 11, 22 3) &lt;/td&gt;
            &lt;td valign="top"&gt;27.7436061324664&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top"&gt;3&lt;/td&gt;
            &lt;td valign="top"&gt;POINT (5 5)&lt;/td&gt;
            &lt;td valign="top"&gt;0&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Instance methods are the exciting part of using the spatial data types in SQL Server 2008.  In the next part of this series, I will cover individual methods in detail.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.jasonfollas.com/blog/archive/2008/03/14/sql-server-2008-spatial-data-part-1.aspx"&gt;SQL Server 2008: Spatial Data, Part 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/03/27/sql-server-2008-spatial-data-part-2.aspx"&gt;SQL Server 2008: Spatial Data, Part 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/03/sql-server-2008-spatial-data-part-4.aspx"&gt;(next part) SQL Server 2008: Spatial Data, Part 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/07/sql-server-2008-spatial-data-part-5.aspx"&gt;SQL Server 2008: Spatial Data, Part 5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/04/11/sql-server-2008-spatial-data-part-6.aspx"&gt;SQL Server 2008: Spatial Data, Part 6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/05/16/sql-server-2008-spatial-data-part-7.aspx"&gt;SQL Server 2008: Spatial Data, Part 7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jasonfollas.com/blog/archive/2008/06/23/sql-server-2008-spatial-data-part-8.aspx"&gt;SQL Server 2008: Spatial Data, Part 8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fwww.jasonfollas.com%2fblog%2farchive%2f2008%2f03%2f28%2fsql-server-2008-spatial-data-part-3.aspx"&gt;&lt;img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.jasonfollas.com%2fblog%2farchive%2f2008%2f03%2f28%2fsql-server-2008-spatial-data-part-3.aspx" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://jasonfollas.com/blog/aggbug/34.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jason Follas</dc:creator>
            <guid>http://jasonfollas.com/blog/archive/2008/03/28/sql-server-2008-spatial-data-part-3.aspx</guid>
            <pubDate>Fri, 28 Mar 2008 20:06:01 GMT</pubDate>
            <wfw:comment>http://jasonfollas.com/blog/comments/34.aspx</wfw:comment>
            <comments>http://jasonfollas.com/blog/archive/2008/03/28/sql-server-2008-spatial-data-part-3.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://jasonfollas.com/blog/comments/commentRss/34.aspx</wfw:commentRss>
            <trackback:ping>http://jasonfollas.com/blog/services/trackbacks/34.aspx</trackback:ping>
        </item>
    </channel>
</rss>