<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://www2.sqlblog.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Adam Machanic : SQLCLR</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx</link><description>Tags: SQLCLR</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>SQLCLR Performance Session at TechEd US</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2013/05/08/sqlclr-performance-session-at-teched-us.aspx</link><pubDate>Wed, 08 May 2013 16:14:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:48998</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>0</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/48998.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=48998</wfw:commentRss><description>I am super-excited to visit New Orleans next month for Microsoft TechEd; it will be my sixth time speaking at the show. My session takes an in-depth look at some of the techniques I've developed for using SQLCLR modules -- and some of the great performance...(&lt;a href="http://www2.sqlblog.com/blogs/adam_machanic/archive/2013/05/08/sqlclr-performance-session-at-teched-us.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=48998" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Performance/default.aspx">Performance</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/TechEd/default.aspx">TechEd</category></item><item><title>CloudSeeder: CLR Stored Procedures For Creating CPU Pressure</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2012/10/23/cloudseeder-clr-stored-procedures-for-creating-cpu-pressure.aspx</link><pubDate>Tue, 23 Oct 2012 19:49:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:45743</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>0</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/45743.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=45743</wfw:commentRss><description>Sometimes, in the interest of testing various scenarios that your server might encounter, it's useful to be able to quickly simulate some condition or another. I/O, memory, CPU pressure, and so on. This latter one is something I've been playing with a...(&lt;a href="http://www2.sqlblog.com/blogs/adam_machanic/archive/2012/10/23/cloudseeder-clr-stored-procedures-for-creating-cpu-pressure.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=45743" width="1" height="1"&gt;</description><enclosure url="http://www2.sqlblog.com/blogs/adam_machanic/attachment/45743.ashx" length="11043" type="application/octet-stream" /><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/cpu/default.aspx">cpu</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Testing/default.aspx">Testing</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/threads/default.aspx">threads</category></item><item><title>Performance, Discounts, and an Excuse to Visit New York City</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2011/06/02/performance-discounts-and-an-excuse-to-visit-new-york-city.aspx</link><pubDate>Thu, 02 Jun 2011 14:21:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:36040</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>0</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/36040.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=36040</wfw:commentRss><description>A couple of weeks ago I announced a two-day advanced performance seminar in New York City, which will be delivered in July. This seminar will cover SQLCLR and parallelism techniques to help you take performance well beyond the levels that typical tuning...(&lt;a href="http://www2.sqlblog.com/blogs/adam_machanic/archive/2011/06/02/performance-discounts-and-an-excuse-to-visit-new-york-city.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=36040" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/new+york/default.aspx">new york</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/parallelism/default.aspx">parallelism</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Performance/default.aspx">Performance</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/training/default.aspx">training</category></item><item><title>Two Days of Advanced Performance Techniques - July 14-15, New York City</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2011/05/18/two-day-of-advanced-performance-techniques-july-14-15-new-york-city.aspx</link><pubDate>Wed, 18 May 2011 16:19:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:35709</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>5</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/35709.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=35709</wfw:commentRss><description>I am pleased to announce that I will be delivering two days of training in New York City, July 14 and 15. This seminar focuses on achieving "next-level" performance--going beyond that which you can gain via normal tuning methodologies . The vehicles for...(&lt;a href="http://www2.sqlblog.com/blogs/adam_machanic/archive/2011/05/18/two-day-of-advanced-performance-techniques-july-14-15-new-york-city.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=35709" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/data+education/default.aspx">data education</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/new+york/default.aspx">new york</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/parallelism/default.aspx">parallelism</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Performance/default.aspx">Performance</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category></item><item><title>Webcast - September 28, 2010 - Next-Level SQLCLR: Parallel Processing and Bulk Load</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2010/09/27/webcast-september-28-2010-next-level-sqlclr-parallel-processing-and-bulk-load.aspx</link><pubDate>Mon, 27 Sep 2010 18:24:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:29004</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>0</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/29004.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=29004</wfw:commentRss><description>Tomorrow at 12:00 p.m. EDT (4:00 p.m. GMT) I'll be doing a webcast for the PASS Application Development Virtual Chapter . Next-Level SQLCLR: Parallel Processing and Bulk Load The power of SQLCLR as a performance tool has been well-documented at this point;...(&lt;a href="http://www2.sqlblog.com/blogs/adam_machanic/archive/2010/09/27/webcast-september-28-2010-next-level-sqlclr-parallel-processing-and-bulk-load.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=29004" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/parallel+processing/default.aspx">parallel processing</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/parallelism/default.aspx">parallelism</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/PASS/default.aspx">PASS</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/webcasts/default.aspx">webcasts</category></item><item><title>TechEd 2010 Thanks and Demos</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2010/06/10/teched-2010-thanks-and-demos.aspx</link><pubDate>Thu, 10 Jun 2010 17:00:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:26082</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>2</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/26082.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=26082</wfw:commentRss><description>Thank you to everyone who attended my three sessions at this year's TechEd show in New Orleans. I had a great time presenting and answering the really great questions posed by attendees. My sessions were: DAT317 T-SQL Power! The OVER Clause: Your Key...(&lt;a href="http://www2.sqlblog.com/blogs/adam_machanic/archive/2010/06/10/teched-2010-thanks-and-demos.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=26082" width="1" height="1"&gt;</description><enclosure url="http://www2.sqlblog.com/blogs/adam_machanic/attachment/26082.ashx" length="31422" type="application/x-zip-compressed" /><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/audit/default.aspx">audit</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/change+data+capture/default.aspx">change data capture</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/change+tracking/default.aspx">change tracking</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/demos/default.aspx">demos</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/OVER+clause/default.aspx">OVER clause</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/TechEd/default.aspx">TechEd</category></item><item><title>Visual Studio 2010 and SQLCLR: Some Good, Some Bad</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2010/05/02/visual-studio-2010-and-sqlclr-some-good-some-bad.aspx</link><pubDate>Sun, 02 May 2010 21:40:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:24754</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>7</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/24754.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=24754</wfw:commentRss><description>This past week I've been trying out Visual Studio 2010 for SQLCLR development. Verdict: A couple of nice things, a couple not so nice. In the interest of keeping things somewhat positive around here, we'll start with the good stuff : Pre-deployment and...(&lt;a href="http://www2.sqlblog.com/blogs/adam_machanic/archive/2010/05/02/visual-studio-2010-and-sqlclr-some-good-some-bad.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=24754" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/deploy/default.aspx">deploy</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/deployment/default.aspx">deployment</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/problems/default.aspx">problems</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Visual+Studio+2010/default.aspx">Visual Studio 2010</category></item><item><title>T-SQL Tuesday #001: Exploring "Fuzzy" Interval Islands Using SQLCLR</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2009/12/08/t-sql-tuesday-001-exploring-fuzzy-interval-islands-using-sqlclr.aspx</link><pubDate>Tue, 08 Dec 2009 19:13:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:19621</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>2</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/19621.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=19621</wfw:commentRss><description>When working with time intervals, we often want to ask a couple of basic questions: Which time periods are not covered by our intervals? These are known as "gaps". What are the time ranges that we are fully covering? These are known as "islands". If you're...(&lt;a href="http://www2.sqlblog.com/blogs/adam_machanic/archive/2009/12/08/t-sql-tuesday-001-exploring-fuzzy-interval-islands-using-sqlclr.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=19621" width="1" height="1"&gt;</description><enclosure url="http://www2.sqlblog.com/blogs/adam_machanic/attachment/19621.ashx" length="2111" type="application/x-zip-compressed" /><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/date/default.aspx">date</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/datetime/default.aspx">datetime</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/gaps/default.aspx">gaps</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/islands/default.aspx">islands</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Performance/default.aspx">Performance</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/time/default.aspx">time</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/T-SQL+Tuesday/default.aspx">T-SQL Tuesday</category></item><item><title>Full Outline for my PASS Pre-Con: SQLCLR From Beginner to Expert</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2009/08/17/full-outline-for-my-pass-pre-con-sqlclr-from-beginner-to-expert.aspx</link><pubDate>Mon, 17 Aug 2009 14:00:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:16053</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>2</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/16053.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=16053</wfw:commentRss><description>Since the announcement of my SQLCLR pre-conference seminar at this November's PASS conference I have received a few e-mails asking for more detail about what I will be covering. In addition to the Q&amp;amp;A I did with PASS , I thought it might be helpful...(&lt;a href="http://www2.sqlblog.com/blogs/adam_machanic/archive/2009/08/17/full-outline-for-my-pass-pre-con-sqlclr-from-beginner-to-expert.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=16053" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/PASS+2009/default.aspx">PASS 2009</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Performance/default.aspx">Performance</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category></item><item><title>SQLCLR String Splitting Part 2: Even Faster, Even More Scalable</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2009/04/28/sqlclr-string-splitting-part-2-even-faster-even-more-scalable.aspx</link><pubDate>Tue, 28 Apr 2009 18:54:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:13612</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>22</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/13612.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=13612</wfw:commentRss><description>
&lt;p&gt;Two days ago, after posting what I thought was &lt;a href="http://sqlblog.com/blogs/adam_machanic/archive/2009/04/26/faster-more-scalable-sqlclr-string-splitting.aspx"&gt;a pretty solid SQLCLR string splitting method&lt;/a&gt;, I received a comment telling me about a &lt;a href="http://www.sqlservercentral.com/Forums/Topic695508-338-1.aspx?Update=1"&gt;big thread on SQLServerCentral&lt;/a&gt; dedicated to the question of how best to split strings. So I jumped in, and went back and forth, and back and forth, and back... and forth... &lt;/p&gt;
&lt;p&gt;Many, many messages and several revisions and re-revisions later, I am happy to present the newer, better, more bug-free SQLCLR string splitting function. Not only is it faster than the previous one I posted, but it also handles multi-character delimiters, ignores consecutive delimiters as well as pre and post delimiters, and best of all, it won't get itself stuck in an infinite loop in certain cases (always a really great stability feature).&lt;/p&gt;
&lt;p&gt;This version walks the SqlChars character array rather than using the IndexOf method on the string, which we found to be a somewhat faster technique--and it's certainly the most scalable and memory efficient method I can imagine. &lt;/p&gt;
&lt;p&gt;Special thanks to SQLServerCentral member Florian Reischl, who was the main person keeping the thread going with me during the last couple of days, and who managed to re-write my versions and eke out even better performance by modifying the algorithms.&amp;nbsp; Great stuff--it was definitely the most fun I've had on a technical forum in quite a long time.&lt;/p&gt;
&lt;p&gt;But without further ado, the code:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;p&gt;using System;&lt;br&gt;using System.Collections;&lt;br&gt;using System.Data;&lt;br&gt;using System.Data.SqlClient;&lt;br&gt;using System.Data.SqlTypes;&lt;br&gt;using Microsoft.SqlServer.Server;&lt;br&gt;&lt;br&gt;public partial class UserDefinedFunctions&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Microsoft.SqlServer.Server.SqlFunction(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FillRowMethodName = "FillRow_Multi",&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TableDefinition = "item nvarchar(4000)"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ]&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static IEnumerator SplitString_Multi(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [SqlFacet(MaxSize = -1)]&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlChars Input,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [SqlFacet(MaxSize = 255)]&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlChars Delimiter&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (Input.IsNull || Delimiter.IsNull) ?&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new SplitStringMulti(new char[0], new char[0]) :&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new SplitStringMulti(Input.Value, Delimiter.Value));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void FillRow_Multi(object obj, out SqlString item)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; item = new SqlString((string)obj);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class SplitStringMulti : IEnumerator&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public SplitStringMulti(char[] TheString, char[] Delimiter)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; theString = TheString;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; stringLen = TheString.Length;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; delimiter = Delimiter;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; delimiterLen = (byte)(Delimiter.Length);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; isSingleCharDelim = (delimiterLen == 1);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lastPos = 0;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nextPos = delimiterLen * -1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #region IEnumerator Members&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public object Current&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; get&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new string(theString, lastPos, nextPos - lastPos);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public bool MoveNext()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (nextPos &amp;gt;= stringLen)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return false;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lastPos = nextPos + delimiterLen;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (int i = lastPos; i &amp;lt; stringLen; i++)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bool matches = true;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //Optimize for single-character delimiters&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (isSingleCharDelim)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (theString[i] != delimiter[0])&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; matches = false;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (byte j = 0; j &amp;lt; delimiterLen; j++)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (((i + j) &amp;gt;= stringLen) || (theString[i + j] != delimiter[j]))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; matches = false;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (matches)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nextPos = i;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //Deal with consecutive delimiters&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ((nextPos - lastPos) &amp;gt; 0)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; i += (delimiterLen-1);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lastPos += delimiterLen;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lastPos = nextPos + delimiterLen;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nextPos = stringLen;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ((nextPos - lastPos) &amp;gt; 0)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return false;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Reset()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lastPos = 0;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nextPos = delimiterLen * -1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #endregion&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int lastPos;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int nextPos;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly char[] theString;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly char[] delimiter;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly int stringLen;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly byte delimiterLen;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly bool isSingleCharDelim;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;};&lt;/p&gt;
&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;Enjoy! &lt;br&gt;&lt;/p&gt;&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=13612" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Performance/default.aspx">Performance</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/string+splitting/default.aspx">string splitting</category></item><item><title>Faster, More Scalable SQLCLR String Splitting</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2009/04/26/faster-more-scalable-sqlclr-string-splitting.aspx</link><pubDate>Sun, 26 Apr 2009 23:59:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:13570</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>17</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/13570.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=13570</wfw:commentRss><description>&lt;p&gt;It seems like every couple of months we see yet another post on SQLCLR string splitting routines. Many bloggers, I suppose, are still struggling, even three years later, to find that "perfect" use case for SQLCLR. Is string splitting it? Probably not. And with SQL Server 2008 table-valued parameters now available, SQLCLR string splitting has become an even less interesting exercise than it was before. None the less, a recent post on the &lt;a href="http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/split-string-in-sql-server-2005-clr-vs-t"&gt;Less Than Dot&lt;/a&gt; site has inspired me to counter with my own best SQLCLR string splitting method, for those of you who are still interested in solving this problem.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;I've noticed that almost invariably, the methods posted online stress how very &lt;span style="font-style:italic;"&gt;easy &lt;/span&gt;it is to do string splitting in .NET, thanks to the String.Split method. And while this easy method tends to work pretty well for small strings and on workloads that don't need to scale, it quickly breaks down when any amount of load is introduced (something that, unfortunately, most writers don't bother considering). The Less Than Dot writer, "onpnt" did do some testing, and discovered that--surprise, surprise--String.Split isn't all that great.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;The issue? Well, it all comes down to large memory allocations and the art of scalable .NET programming--an area about which many SQL Server developers can (and do) remain blissfully naïve. In .NET, reduction of memory utilization--especially large allocation done en masse--is king, and String.Split does exactly the wrong thing. It takes the input string, breaks it into N chunks, and allocates all of the memory needed to store those chunks and pointers to those chunks, in one big huge operation. This can't possibly scale, and indeed it doesn't.&amp;nbsp; I did a quick &lt;a href="http://datamanipulation.net/sqlquerystress/"&gt;SQLQueryStress&lt;/a&gt; test of a TVF based on String.Split and saw fairly good performance when the input sentences were small (in the 40-400 byte range--see below), but after a certain point the AppDomains began recycling and performance became abysmal. Protections put in place for stability of the CLR host include memory leak detection, and this kicks in quite readily when we force allocation of so much memory in one shot--a good thing for the SQL Server instance as a whole, but not great when we're trying to really split a huge string.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Students of .NET who are concerned with scale (and really, everyone should be) are urged to look at the way problems are handled in LINQ. Here the vast majority of requests are internally handled using streaming iterator patterns, rather than moving around huge chunks of memory. This turns out to a much more scalable option for several reasons: Lower in-flight memory utilization, fewer large object heap allocations, and better access by the garbage collector to collect intermediate data that is no longer needed.&lt;/p&gt;

&lt;p&gt;So how can we apply streaming to the string splitting problem? Rather than break the string up into all of its component parts upfront, we can walk the string step-by-step, only finding the next piece as required. In order to handle this, I created the following worker class:&lt;/p&gt;

&lt;blockquote&gt;
&lt;pre&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class splitIt : IEnumerator&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public splitIt(string theString, char delimiter)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.theString = theString;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.delimiter = delimiter;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.lastPos = -1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.nextPos = -1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #region IEnumerator Members&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public object Current&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; get { return theString.Substring(lastPos, nextPos - lastPos).Trim(); }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public bool MoveNext()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (nextPos &amp;gt;= theString.Length)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return false;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lastPos = nextPos + 1;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (lastPos == theString.Length)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return false;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nextPos = theString.IndexOf(delimiter, lastPos);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (nextPos == -1)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nextPos = theString.Length;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return true;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Reset()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.lastPos = -1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.nextPos = -1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #endregion&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int lastPos;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int nextPos;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private string theString;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private char delimiter;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;br&gt;&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;
This class is a simple enumerator implementation that looks for the next delimiter on each iteration, only when requested. Splitting strings in this way, rather than using String.Split, means that no huge upfront allocation takes place. Aside from the sentence itself, only one "chunk" is in play at any given time, and any chunks that have already been used can be garbage collected as needed when memory is tight. &lt;br&gt;&lt;/p&gt;
&lt;p&gt;Wiring this class up in a SQLCR TVF is just about as simple as when using String.Split:&lt;/p&gt;
&lt;pre&gt;&lt;p style="margin-left:40px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "FillIt", TableDefinition = "output nvarchar(4000)")]&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static IEnumerator faster_split&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlChars instr, &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [SqlFacet(IsFixedLength=true, MaxSize=1)]&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlString delimiter&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (instr.IsNull || delimiter.IsNull) ? &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new splitIt("", ',') : &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new splitIt(instr.ToSqlString().Value, Convert.ToChar(delimiter.Value)));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void FillIt(object obj, out SqlString output)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; output = (new SqlString((string)obj));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/pre&gt;
&lt;p&gt;I've enhanced this example slightly compared with most of the usual suspects: A SqlFacet attribute is used to make sure that the delimiter is only a single character, and I've added a bit of additional code in the main method to deal with NULL inputs.&lt;/p&gt;&lt;p&gt;The scalability difference between this method and the String.Split method is staggering in the simple tests I ran today on my SQL Server 2008 test server.&amp;nbsp; With small sentences, even under moderate load (100 concurrent threads), each method performs more or less equivalently.&amp;nbsp; But as sentence size increases to 50KB, the String.Split method begins slowing, taking almost 2 seconds per iteration, and the occasional AppDomain recycle is seen in the SQL Server log.&amp;nbsp; The streaming method, on the other hand, continues to complete its job in just over 1/10th of a second, and causes no AppDomain recycles. Increasing to 500KB sentences, String.Split causes numerous AppDomain recycles and time per iteration increases to over 30 seconds, while the streaming method averages just 16 seconds per iteration. Jumping to 5MB sentences, String.Split causes almost continuous AppDomain recycles, and each iteration takes almost 6 &lt;span style="font-style:italic;"&gt;minutes &lt;/span&gt;to complete. Yet with the streaming method, even with sentences of this size I am still unable to cause an AppDomain recycle to occur, and iterations complete in around 55 seconds. &lt;/p&gt;&lt;p&gt;The test I did was quite simple, and I won't post too many details here as I prefer that you test with your own workloads and draw your own conclusion about how this method fares when compared with T-SQL versions or the naïve String.Split method. I hope that if you do test you'll post back here with your results so that we can all learn the best way to handle these problems--whether or not string splitting really is all that interesting in the post-SQL Server 2005 world.&lt;br&gt;&lt;/p&gt;&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=13570" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Performance/default.aspx">Performance</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/scalability/default.aspx">scalability</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/string+splitting/default.aspx">string splitting</category></item><item><title>Speaking Season About to Begin: MCM, Roadshow, PASS, and SQLCLR Precon at DevTeach</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2008/10/21/speaking-season-about-to-begin-mcm-roadshow-pass-and-sqlclr-precon-at-devteach.aspx</link><pubDate>Tue, 21 Oct 2008 20:20:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:9583</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>2</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/9583.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=9583</wfw:commentRss><description>&lt;p&gt;Fall is now in full swing, and after a relatively quiet several months my speaking schedule is about to jump into fairly-packed mode.&amp;nbsp; Following is a list of where I'll be headed over the next couple of months:&lt;/p&gt;&lt;p&gt;Next week I'm really excited to be teaching a day of the &lt;a href="http://www.microsoft.com/learning/mcp/master/sql/default.mspx"&gt;Microsoft Certified Master: Microsoft SQL server 2008&lt;/a&gt; program's beta rotation. I'll be teaching developer topics including ADO.NET, XML, and SQLCLR. Should be a good time!&lt;/p&gt;&lt;p&gt;November 12, the &lt;a href="http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032388731&amp;amp;Culture=en-US"&gt;SQL Server 2008 Roadshow hits Boston&lt;/a&gt;, and I'll be presenting a half day on all sorts of SQL Server 2008 topics.&amp;nbsp; This one is free, so if you're in the Boston area you have nothing to lose by checking it out.&lt;/p&gt;&lt;p&gt;November 19-21 is the PASS Summit, and I'm excited to be involved in three presentations. I'm doing a &lt;a href="http://www.softconference.com/pass/ProgramSessions/program-sessiondetail.asp?SID=130656"&gt;spotlight presentation on programmatic/application concurrency&lt;/a&gt; topics, a duo with Peter DeBetta called &lt;a href="http://www.softconference.com/pass/ProgramSessions/program-sessiondetail.asp?SID=130697"&gt;"[Anti]patterns and [Mal]practices"&lt;/a&gt;, and a duo with James Luetkehoelter on &lt;a href="http://www.softconference.com/pass/ProgramSessions/program-sessiondetail.asp?SID=130937"&gt;performance metrics&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;December 1 and 2 I'll be in Montreal, speaking at the always-fun &lt;a href="http://www.devteach.com/"&gt;DevTeach&lt;/a&gt;/&lt;a href="http://sqlteach.com/"&gt;SQLTeach&lt;/a&gt; conference. Aside from speaking in the regular conference program, on December 1 I will be doing a &lt;a href="http://www.sqlteach.com/PreConference.aspx#PreSQL"&gt;full-day preconference on SQLCLR development&lt;/a&gt;. In this preconference I will start with SQLCLR beginner topics and take you all the way through to some really advanced use cases. So this one should be of interest even if you've been using SQLCLR for some time. Best practices will be stressed, and you might just learn about some of the dusty corners you've not yet had a chance to touch.&amp;nbsp; I'm also upgrading the content to cover what changes SQL Server 2008 brings to the table, so this will be a complete session on the topic.&amp;nbsp; I think that SQLCLR, while not used much in SQL Server 2005, will really pick up adoption in SQL Server 2008 and beyond.&amp;nbsp; So now is the time to pick up what will be a key skill going forward.&lt;br&gt;&lt;/p&gt;&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=9583" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Conferences/default.aspx">Conferences</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/DevTecah/default.aspx">DevTecah</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/MCM/default.aspx">MCM</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/PASS/default.aspx">PASS</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Speaking/default.aspx">Speaking</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/sql+server+2008/default.aspx">sql server 2008</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category></item><item><title>[OT] SQLTeach: Almost Here</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2008/04/24/ot-sqlteach-almost-here.aspx</link><pubDate>Fri, 25 Apr 2008 00:15:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:6407</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>1</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/6407.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=6407</wfw:commentRss><description>&lt;p&gt;I was just reviewing my calendar for the next several weeks and noticed that 
the Toronto &lt;a href="http://www.sqlteach.com/"&gt;SQLTeach &lt;/a&gt;conference is now 
only a few weeks away.&amp;nbsp; This conference includes quite a few &lt;a href="http://www.sqlteach.com/Session.aspx"&gt;interesting SQL Server-related 
sessions&lt;/a&gt;, on topics ranging from best practices, to performance, to some of 
the new SQL Server 2008 features. I fully expect this to be a great show.&lt;/p&gt;
&lt;p&gt;I am doing two breakout sessions during the main conference:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;SQL Server 2005: Authorization, Privilege, and Access Control&lt;/span&gt;.&amp;nbsp; In this 
talk I cover SQL Server 2005’s enhancements around granting permissions via 
stored modules (i.e., stored procedures, views, functions) 
&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Designing Highly Concurrent Database Applications&lt;/span&gt;.&amp;nbsp; In this talk I get 
into the business requirements behind supporting concurrent processes, and the 
areas where SQL Server (and every other database product) falls short. I then go 
on to show how to solve the problems in the database programmatically. 
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;I am also doing a &lt;strong&gt;full-day post-conference session on SQLCLR 
programming&lt;/strong&gt;. This will be the first time that I will be presenting all 
of my SQLCLR material in a single day; should be fun. I will take attendees from 
the basics all the way through some advanced applications and techniques, so if 
you’re interested in becoming a SQLCLR expert I highly recommend attending.&lt;/p&gt;
&lt;p&gt;The conference starts in just three weeks, but &lt;a href="http://www.sqlteach.com/Register.aspx"&gt;it is not too late to register&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=6407" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/canada/default.aspx">canada</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Conferences/default.aspx">Conferences</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/devteach/default.aspx">devteach</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/sqlteach/default.aspx">sqlteach</category></item><item><title>Running sums yet again: SQLCLR saves the day!</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2006/07/12/running-sums-yet-again-sqlclr-saves-the-day.aspx</link><pubDate>Thu, 13 Jul 2006 01:52:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:107</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>6</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/107.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=107</wfw:commentRss><description>Back again!&amp;nbsp; Fourth post for the month of February, making this my best 
posting month in, well, months.&amp;nbsp; Expect this trend to continue.&lt;br&gt;&lt;br&gt;After &lt;a href="http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/running-sums-redux.aspx"&gt;yesterday's 
post on running sums and the evils of&amp;nbsp;cursors&lt;/a&gt;, Jamie Thompson &lt;a href="http://blogs.conchango.com/jamiethomson/archive/2006/02/28/3001.aspx"&gt;came 
up with a faster solution than the curser I posted&lt;/a&gt;.&amp;nbsp; Alas, Jamie's solution 
uses an undocumented form of UPDATE syntax, and I am really not comfortable 
using it.&amp;nbsp; So I set out to find still another solution.&amp;nbsp; As promised at the end 
of my last post, SQLCLR is where I looked.&amp;nbsp; And my instinct proved 
correct.&lt;br&gt;&lt;br&gt;Jamie's solution runs in 4 seconds on my laptop.&amp;nbsp; Compared to 14 
seconds for the cursor I posted, that's a great enhancement.&amp;nbsp; But I knew that we 
could do better still, and without undocumented syntax and temp tables.
&lt;p&gt;The answer?&amp;nbsp; A SQLCLR stored procedure.&amp;nbsp; Same logic as the cursor: Pull back 
the data in order, then loop over the rows and maintain the running sum in a 
variable.&amp;nbsp; But thanks to the SqlPipe's SendResults methods, we don't need a 
temporary table for this one -- the results can be sent back one row at a time, 
and will still show up&amp;nbsp;on the client&amp;nbsp;as a single result set.&lt;/p&gt;
&lt;p&gt;Here's how I did it:&lt;/p&gt;
&lt;blockquote dir="ltr" style="margin-right:0px;"&gt;
&lt;p&gt;using System;&lt;br&gt;using System.Data;&lt;br&gt;using System.Data.SqlClient;&lt;br&gt;using 
System.Data.SqlTypes;&lt;br&gt;using Microsoft.SqlServer.Server;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;public partial class StoredProcedures&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
[Microsoft.SqlServer.Server.SqlProcedure]&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void 
TransactionHistoryRunningSum()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; using (SqlConnection conn = 
new SqlConnection("context connection=true;"))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
SqlCommand comm = new SqlCommand();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; comm.Connection = 
conn;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; comm.CommandText = @"" +&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "SELECT 
TransactionID, ActualCost " +&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "FROM 
Production.TransactionHistory " +&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "ORDER BY 
TransactionID";&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlMetaData[] columns = new SqlMetaData[3];&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
columns[0] = new SqlMetaData("TransactionID", SqlDbType.Int);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
columns[1] = new SqlMetaData("ActualCost", SqlDbType.Money);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
columns[2] = new SqlMetaData("RunningTotal", SqlDbType.Money);&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; decimal RunningSum = 0;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlDataRecord record = new SqlDataRecord(columns);&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlContext.Pipe.SendResultsStart(record);&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; conn.Open();&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlDataReader reader = comm.ExecuteReader();&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (reader.Read())&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; decimal 
ActualCost = (decimal)reader[1];&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RunningSum += 
ActualCost;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; record.SetInt32(0, (int)reader[0]);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
record.SetDecimal(1, ActualCost);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
record.SetDecimal(2, RunningSum);&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlContext.Pipe.SendResultsRow(record);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlContext.Pipe.SendResultsEnd();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
}&lt;br&gt;};&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p dir="ltr"&gt;Results?&amp;nbsp; 3 seconds on my&amp;nbsp;laptop.&amp;nbsp; 25% better than the&amp;nbsp;previous best 
solution.&amp;nbsp; Not bad!&lt;/p&gt;
&lt;p dir="ltr"&gt;And, I even did better than that.&amp;nbsp; By creating a&amp;nbsp;CLR table-valued 
user-defined function&amp;nbsp;that&amp;nbsp;uses a custom class wrapping a SqlDataReader, I was 
able to get this operation down to 2 seconds on my laptop.&amp;nbsp; However, due to 
restrictions related to passing around context connections, that solution can 
not use a context connection and is therefore highly suboptimal.&amp;nbsp; Until I figure 
out how to pass around a context connection (or if it's&amp;nbsp;even possible), I'll 
keep that one off the blog.&lt;/p&gt;
&lt;p dir="ltr"&gt;So to recap: We certainly have not gotten rid of the cursor.&amp;nbsp; This 
SQLCLR solution is really just a cursor in disguise.&amp;nbsp; But we've built a 
&lt;i&gt;better&lt;/i&gt;&amp;nbsp;cursor, because this one doesn't require temporary tables.&amp;nbsp; And 
that I can live with--for now.&lt;/p&gt;
&lt;p dir="ltr"&gt;Thanks&amp;nbsp;to Jamie Thompson for prompting me to not wait several weeks 
before following up as I usually do with these posts!&lt;/p&gt;&lt;br&gt;&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=107" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/Performance/default.aspx">Performance</category><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category></item><item><title>Using static properties in SQLCLR UDTs</title><link>http://www2.sqlblog.com/blogs/adam_machanic/archive/2006/07/12/using-static-properties-in-sqlclr-udts.aspx</link><pubDate>Thu, 13 Jul 2006 01:48:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:104</guid><dc:creator>Adam Machanic</dc:creator><slash:comments>3</slash:comments><comments>http://www2.sqlblog.com/blogs/adam_machanic/comments/104.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/adam_machanic/commentrss.aspx?PostID=104</wfw:commentRss><description>I spoke at the Beantown .NET user group meeting tonight, on the topic of SQLCLR in SQL Server 2005.&lt;br&gt;


&lt;br&gt;


One of the questions that came up during the UDT part of the talk was
whether static properties are supported.&amp;nbsp; Unfortunately, I had no
answer at the time--it's not something I'd yet thought to try.&lt;br&gt;


&lt;br&gt;


The answer, as it turns out, is yes: they are supported.&amp;nbsp; But they must be defined as &lt;b&gt;readonly&lt;span style="font-weight:bold;"&gt;&lt;/span&gt;&lt;/b&gt;, e.g.:&lt;br&gt;

&lt;blockquote&gt;&lt;font face="Courier New"&gt;&lt;span style="font-family:courier new;"&gt;public static readonly int foo;&lt;/span&gt;&lt;/font&gt;&lt;/blockquote&gt;


As it turns out, this means that they can also only be initialized from a &lt;b&gt;static constructor&lt;/b&gt;:&lt;br&gt;

&lt;blockquote&gt;&lt;font face="Courier New"&gt;&lt;span style="font-family:courier new;"&gt;static myType()&lt;/span&gt;&lt;br style="font-family:courier new;"&gt;
  &lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br style="font-family:courier new;"&gt;
  &lt;span style="font-family:courier new;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; foo = 1;&lt;/span&gt;&lt;br style="font-family:courier new;"&gt;
  &lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/font&gt;


&lt;/blockquote&gt;



... which means that value will stick around from the time the type
is first used, until the AppDomain is reset (for example, if SQL Server
is restarted).&lt;br&gt;


&lt;br&gt;

In my opinion, this greatly limits many use cases.&amp;nbsp; One might, I
suppose, have some expensive, yet rarely-modified data to initialize
the member with, and get that data on the first pass only. However, if
the data does chang, I'm not sure that it would be easy to reset the
AppDomain.&amp;nbsp; Do you really want to restart SQL Server in
production environments to update static members?&lt;br&gt;


&lt;br&gt;


Another use case I can think of is logging.&amp;nbsp; Perhaps there are
situations in which you'd want to log the first time a type is used.&amp;nbsp;
But that doesn't seem incredibly interesting.&lt;br&gt;


&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;br&gt;


If someone else reading has a more compelling use case, I'd be interested in hearing it!&lt;br&gt;


&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=104" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/adam_machanic/archive/tags/SQLCLR/default.aspx">SQLCLR</category></item></channel></rss>