<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'><id>tag:blogger.com,1999:blog-14400377</id><updated>2008-09-05T10:13:19.060+01:00</updated><title type='text'>Jane's Technical Stuff</title><subtitle type='html'>A blog of mostly technical findings found in day to day working life.
Note: all opinions are entirely mine.</subtitle><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/blog.html'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default?start-index=26&amp;max-results=25'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml'/><author><name>Jane</name><uri>http://www.blogger.com/profile/02966820600973863543</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>227</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-14400377.post-6227464355200418108</id><published>2008-09-05T10:00:00.007+01:00</published><updated>2008-09-05T10:13:19.072+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='madgex'/><category scheme='http://www.blogger.com/atom/ns#' term='oauth'/><title type='text'>OAuth.net</title><content type='html'>As I write this, &lt;a href="http://www.glennjones.net/Home/"&gt;Glenn&lt;/a&gt; should be doing the welcome talk at &lt;a href="http://2008.dconstruct.org/"&gt;dConstruct&lt;/a&gt;, and he should be revealing &lt;a href="http://madgex.com/"&gt;Madgex&lt;/a&gt;'s first foray into open sourcing our software - &lt;a href="http://lab.madgex.com/oauth-net/"&gt;OAuth.net&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;From the &lt;a href="http://lab.madgex.com/"&gt;Madgex Lab&lt;/a&gt; site:&lt;br /&gt;&lt;blockquote&gt;"OAuth.net is a .net library which provides full OAuth consumer and provider support. The library facilitates secure API authentication in a simple and standard method for desktop and web applications.&lt;br /&gt;&lt;br /&gt;OAuth allows user to grant and deny one application access to the data stored in another application. It forms one of the foundation blocks of the data portability concept, which has the aim of allowing users to easily move their personal data around the web."&lt;/blockquote&gt;&lt;br /&gt;From what I've seen and heard, &lt;a href="http://oauth.net/"&gt;OAuth&lt;/a&gt; is a great solution to the &lt;a href="http://microformats.org/wiki/social-network-anti-patterns"&gt;password anti-pattern&lt;/a&gt; problem.  An immediately obvious place for this to be applied within Madgex will be to enhance the &lt;a href="http://www.backnetwork.com/"&gt;backnetwork&lt;/a&gt; to offer the ability to find your contacts using the &lt;a href="http://lab.madgex.com/oauth-net/googlecontacts/"&gt;Google contacts API&lt;/a&gt; - don't hold your breath mind you as we're all a bit busy delivering &lt;a href="http://madgex.com/job-boards/"&gt;job boards&lt;/a&gt; in time for the January rush.&lt;br /&gt;&lt;br /&gt;As a company, I think we're going to learn a lot through opening this code out to the community and I'm really, really excited about it. &lt;a href="http://siliconbea.ch/"&gt;Bruce&lt;/a&gt; will be doing a talk about OAuth at &lt;a href="http://barcampbrighton.org/"&gt;BarCamp Brighton&lt;/a&gt; over the weekend - provisionally titled "OAuth versus the Password Anti-Pattern" - so if you're going to be there and are interested in learning more then pop along and hear what he has to say.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/09/oauthnet.html' title='OAuth.net'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=6227464355200418108&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/6227464355200418108'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/6227464355200418108'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-6771943474963808912</id><published>2008-09-03T21:36:00.003+01:00</published><updated>2008-09-03T22:04:21.031+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='event'/><category scheme='http://www.blogger.com/atom/ns#' term='Brighton'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><title type='text'>"Grow your wiki" with Stewart Mader</title><content type='html'>This evening, I headed over to &lt;a href="http://thewerks.org.uk/"&gt;The Werks&lt;/a&gt; to hear &lt;a href="http://www.ikiw.org/"&gt;Stewart Mader&lt;/a&gt;, author of &lt;a href="http://www.wikipatterns.com/display/wikipatterns/Wikipatterns"&gt;Wiki Patterns&lt;/a&gt;, talk about &lt;a href="http://www.whuffieclub.com/2008/07/24/grow-your-wiki/"&gt;using a wiki successfully within a work environment&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I have been using &lt;a href="http://jane.dallaway.com/blog/2007/09/tiddlywiki.html"&gt;TiddlyWiki&lt;/a&gt; to collaborate on a project level for about a year or so now - and the most recent project has probably been the most successful to date as we've sent each other links to wiki pages, both frequently updated pages as more information is uncovered, and used it as a central point.  I was interested to find out how I can use a wiki more, and more effectively.  I was hoping for some organisation strategies and tips, but that wasn't really what the evening was about so I will have to do some research myself on that.&lt;br /&gt;&lt;br /&gt;Stewart was a really engaging speaker, and is obviously passionate about his subject.  He had some really good uses for a wiki which I'd not thought of but actually make a whole lot of sense - things like meeting agendas.  His example was that you write up an agenda and email it out to the attendees.  You get three replies asking for changes to that agenda - someone wants an item removed and two others want items added.  So now as the meeting organiser you've got 3 changes to make, and then you have to send it out again.  As he said, you can almost guarantee that someone will have already printed out the agenda before the revised version is sent out and so will end up at the meeting with an out of date version.  Instead, if you put your agenda on a wiki, then every attendee can check it out, and make changes as appropriate.  During the meeting minutes can be typed into the wiki directly if it is a laptop friendly meeting by any or all attendees.  So, the whole process immediately is a lot more collaborative.&lt;br /&gt;&lt;br /&gt;During the Q&amp;A session someone mentioned using &lt;a href="http://docs.google.com/"&gt;google docs&lt;/a&gt; as a more collaborative mechanism for documents.  As Steward pointed out this isn't the same thing as a wiki, and he is also concerned that google docs is quite geeky.  He mentioned &lt;a href="https://buzzword.acrobat.com/"&gt;Buzzword&lt;/a&gt; as a less geeky, more UI friendly alternative - so I'm off to have a look at that.&lt;br /&gt;&lt;br /&gt;We also discussed enterprise wiki tools, and the following were mentioned as being worth a look &lt;ul&gt;&lt;li&gt;&lt;a href="http://www.atlassian.com/software/confluence/"&gt;confluence&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.jivesoftware.com/products/clearspace"&gt;clearspace&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.socialtext.com/"&gt;socialtext&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;In summary, a great talk, which resulted in me adding lots of "consider a wiki for x" tasks to my &lt;a href="http://www.rememberthemilk.com/"&gt;remember the milk&lt;/a&gt; task list to give some thought to when and where a wiki would be a benefit in my working life</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/09/grow-your-wiki-with-stewart-mader.html' title='&quot;Grow your wiki&quot; with Stewart Mader'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=6771943474963808912&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/6771943474963808912'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/6771943474963808912'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-8177721973674711142</id><published>2008-09-03T09:36:00.001+01:00</published><updated>2008-09-03T10:02:04.119+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tool'/><category scheme='http://www.blogger.com/atom/ns#' term='database change management'/><category scheme='http://www.blogger.com/atom/ns#' term='review'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Database Comparison tools: Redgate SQL Compare Review</title><content type='html'>I first started using &lt;a href="http://www.red-gate.com/products/SQL_Compare/index.htm"&gt;Redgate SQL Compare&lt;/a&gt; when I was at Glass's in 2003/04 and I'm still using it 4 years later.  Over those years the product has evolved and new products have been released from Redgate.  I have only ever used it for SQL Server (7, 2000 and 2005) and so can't comment on it's appropriateness or reliableness targeting other database products.&lt;br /&gt;&lt;br /&gt;At the time that I first found SQL Compare, there weren't that many tools available that did a reliable job of comparing objects and producing scripts of the differences.  I know that one of my colleagues at &lt;a href="http://madgex.com/"&gt;Madgex&lt;/a&gt; is a fan of &lt;a href="http://www.sqldelta.com/"&gt;SQL Delta&lt;/a&gt;.  One day we sat down to compare the tools and discovered that there really wasn't much to choose between them.&lt;br /&gt;&lt;br /&gt;I have always used SQL Compare more as a tool to check my scripted updates, rather than to produce those scripts.  After a bad experience at a previous company when a member of my team managed to empty an articles table rather than update it when manipulating the database via Enterprise Manager I've habitually manually created &lt;a href="http://jane.dallaway.com/blog/2008/03/defensive-sql-updates.html"&gt;defensive SQL&lt;/a&gt; change scripts with appropriate transactions and error handling.&lt;br /&gt;&lt;br /&gt;SQL Compare is simple to use, works quickly and produces a detailed list of differences between database objects.  The differences that are detected are configurable, so you can choose to ignore or report upon white space or comment differences.&lt;br /&gt;&lt;br /&gt;When you first open the screen you are presented with a screen offering options of what you want to compare.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm4.static.flickr.com/3011/2824594840_e2b7d06139.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;Once you have registered a couple of databases to compare, it is a short wait until the differences are displayed.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm4.static.flickr.com/3055/2823758755_39aab9d97a.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;The display of differences are divided into:&lt;ul&gt;&lt;li&gt;objects that appear on the source database but not the target&lt;/li&gt;&lt;li&gt;objects that appear on the target database but not the source&lt;/li&gt;&lt;li&gt;objects that appear on both databases but are different&lt;/li&gt;&lt;li&gt;objects that are the same on both databases&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm4.static.flickr.com/3026/2823758895_7cc2d94bba.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;For each object that is different you can choose to view the details of that object which will display a SQL based description of the object with the differences identified by a highlight line - sometimes it might be the whole object, a part of an object (i.e.a column definition) and sometimes it will be a constraint, or a grant statement that is missing.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm4.static.flickr.com/3275/2824595084_3949475e59.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;From the summary screen you can select the changes that you're interested in and get SQL Compare to automatically synchonise your target database to match the source, or vice versa.  You can also get it to generate some SQL to do the generation to allow you to put your SQL upgrade scripts into a build process, or under source control.  In the majority of cases I will use this tool to check my upgrade scripts, rather than to produce them and if I spot any difference I tend to write my own, defensive, SQL to make the upgrade scripts.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm4.static.flickr.com/3220/2823770081_3c695d62af.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;In the 4 or so years I've been using SQL Compare I've found it to be a stable, evolving product which I have come to rely upon completely to check upgrade processes.  I made the most use of this tool at Glass's where I was working on a product which was client/server application where it couldn't be relied upon that the customer was always running the latest version.  Consequently, our preparation for release process involved installing the earliest supported version of the system, and upgrading it to the release candidate on one machine and installing a clean, new build of the release candidate from scratch and comparing the two and producing upgrade SQL scripts for any identified differences before starting the process again.  We also included a call to the command line version into the automated &lt;a href="http://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx"&gt;MSBuild&lt;/a&gt; process producing an HTML report of the differences which could be used to fail the build if anything other than 0 differences was reported.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/09/database-comparison-tools-redgate-sql.html' title='Database Comparison tools: Redgate SQL Compare Review'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=8177721973674711142&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/8177721973674711142'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/8177721973674711142'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-4401296310951704174</id><published>2008-08-31T09:07:00.004+01:00</published><updated>2008-08-31T10:16:37.922+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='london'/><category scheme='http://www.blogger.com/atom/ns#' term='event'/><title type='text'>Event: In The Brain of Gojko Adzic: Testing Web Applications with Selenium &amp; Selenium Remote Control</title><content type='html'>On Thursday evening I attended the &lt;a href="http://skillsmatter.com/"&gt;Skills Matter&lt;/a&gt; event &lt;a href="http://skillsmatter.com/podcast/open-source-dot-net/testing-web-applications-with-selenium-selenium-remote-control"&gt;In The Brain of Gojko Adzic: Testing Web Applications with Selenium &amp; Selenium Remote Control&lt;/a&gt; which was a great follow up to &lt;a href="http://kerrybuckley.org/"&gt;Kerry Buckley&lt;/a&gt;'s Web Testing with Selenium talk he did at &lt;a href="http://jane.dallaway.com/blog/2007/09/barcamp-brighton-day-2.html"&gt;Barcamp Brighton last September&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blog.spiralarm.com/richard/"&gt;Richard&lt;/a&gt; has been to a few &lt;a href="http://skillsmatter.com/go/java-jee"&gt;Skills Matter Java &amp; JEE&lt;/a&gt; events before and had thought them useful, so when I spotted the Selenium talk I thought I'd go along and see what I could gain from it.  My previous experience with &lt;a href="http://selenium.openqa.org/"&gt;Selenium&lt;/a&gt; had involved the &lt;a href="http://selenium-ide.openqa.org/"&gt;Selenium IDE&lt;/a&gt; for firefox and recording various scripts to be used as a very basic regression suite - intended to be ran after a new deployment to a live server to ensure that the deployment hadn't broken anything and that response times were acceptable.  So, I felt my knowledge was pretty basic and that this event should broaden my appreciation of the various aspects of Selenium as a tool.&lt;br /&gt;&lt;br /&gt;The evening was broken into 3 parts, presented by 3 people and in total lasting an hour and a half, from 6.30pm to around 8pm.  &lt;br /&gt;&lt;br /&gt;Part One was an introduction to Selenium by &lt;a href="http://gojko.net/"&gt;Gojko Adzic&lt;/a&gt; and introduced the concepts and associated tools.  Gojko has put together a &lt;a href="http://gojko.net/2008/08/28/links-and-slides-from-testing-web-applications-with-selenium/"&gt;blog entry&lt;/a&gt; detailing all the links he mentioned, and also links to the other speakers.  From this introduction I heard about a few tools I didn't know off:&lt;ul&gt;&lt;li&gt;&lt;a href="http://storytestiq.solutionsiq.com/wiki/Main_Page"&gt;StoryTestIQ&lt;/a&gt; - a mashup of Selenium and &lt;a href="http://www.fitnesse.org/FitNesse.OneMinuteDescription"&gt;FitNesse&lt;/a&gt; which sounds like it is more tester friendly but with the ability to script database access for setup and teardown tasks&lt;/li&gt;&lt;li&gt;&lt;a href="http://fitnesse.info/webtest"&gt;WebTest Fixtures&lt;/a&gt; - an extension to FitNesse that implement a customer-friendly language for web testing, utilising Selenium Remote Control&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Part Two was Milan Bogdanovic, a tester from &lt;a href="http://www.sqs-uk.com/"&gt;SQS-UK&lt;/a&gt;.  This talk focussed on the Selenium IDE, which, as I mentioned above, is the only bit of Selenium I've every really played with and so I didn't gain as much from this part.  I did, however, learn about the ability to use XPath expressions as the target and also got pointed at a useful Firefox extension &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/1095"&gt;XPath Checker&lt;/a&gt; to help work out the correct XPath expression for an element to check or select.  I also found out about the ability to make use of the &lt;a href="http://wiki.openqa.org/display/SEL/Contributed+User-Extensions"&gt;user-extensions.js&lt;/a&gt; file to store javascript functions and execute them via the IDE.&lt;br /&gt;&lt;br /&gt;Part Three was &lt;a href="http://www.isanchez.net/"&gt;Ivan Sanchez&lt;/a&gt; and was focussed on &lt;a href="http://selenium-rc.openqa.org/"&gt;Selenium RC&lt;/a&gt;.  As with Gojko he has put together a &lt;a href="http://isanchez.net/2008/08/29/slides-from-my-talk-at-skillsmatter/"&gt;blog post of links based on his session&lt;/a&gt;.  I knew very little about this, although I knew that &lt;a href="http://robochick.co.uk/"&gt;Emily&lt;/a&gt; had made some good progress using it.   There were quite a few hints and tips coming out of this session, many relating to the architecting of the tests:&lt;ul&gt;&lt;li&gt;Ideally start a new browser for each test - this ensures a clean base, but does make the process slow&lt;/li&gt;&lt;li&gt;Extract configuration details into an external properties file - otherwise, as with all other areas of development, your code ends up littered with "special" values&lt;/li&gt;&lt;li&gt;Make use of the &lt;a href="http://code.google.com/p/webdriver/wiki/PageObjects"&gt;PageObjects&lt;/a&gt; design pattern which presents each page as an object comprised of the services that the page offers - thus decoupling the HTML elements from the functional elements&lt;/li&gt;&lt;li&gt;Think carefully about when and how often these tests get run, as mentioned above they can be slow to execute so don't put them into a continuous integration environment to be run at each check-in, instead do them in batch overnight or a couple of times during the day&lt;/li&gt;&lt;li&gt;Consider using the &lt;a href="http://selenium-grid.openqa.org/"&gt;Selenium Grid&lt;/a&gt; option to perform the tests across multiple machines to reduce the time to execute - &lt;a href="http://selenium-grid.openqa.org/run_the_demo_on_ec2.html"&gt;this can also work using Amazon's EC2 service&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;All in all a good evening, well worth attending and I'll be keeping an eye on future events being run by Skills Matter as part of the &lt;a href="http://skillsmatter.com/go/open-source-dot-net"&gt;Open Source .NET&lt;/a&gt; series</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/08/event-in-brain-of-gojko-adzic-testing.html' title='Event: In The Brain of Gojko Adzic: Testing Web Applications with Selenium &amp; Selenium Remote Control'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=4401296310951704174&amp;isPopup=true' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4401296310951704174'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4401296310951704174'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-213438709602882603</id><published>2008-08-27T17:26:00.003+01:00</published><updated>2008-08-27T17:44:46.049+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tool'/><category scheme='http://www.blogger.com/atom/ns#' term='ilp'/><title type='text'>Review: Clone Detective</title><content type='html'>&lt;a href="http://blog.cwa.me.uk/2008/08/20/the-morning-brew-162/"&gt;The Morning Brew #162&lt;/a&gt; mentioned a tool &lt;a href="http://www.codeplex.com/CloneDetectiveVS"&gt;Clone Detective&lt;/a&gt; which &lt;blockquote&gt;is a Visual Studio integration that allows you to analyze C# projects for source code that is duplicated somewhere else. Having duplicates can easily lead to inconsistencies and often is an indicator for poorly factored code.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Today, a colleague and I spent some &lt;a href="http://jane.dallaway.com/blog/labels/ilp.html"&gt;ILP&lt;/a&gt; time investigating the tool to determine whether it would help us in the &lt;a href="http://madgex.com/"&gt;Madgex&lt;/a&gt; environment or not.&lt;br /&gt;&lt;br /&gt;We tried the tool out on a couple of projects, but due to it being a Visual Studio 2008 only plug-in our options were a bit limited.  After installing the plug-in, the next thing we did was watch the &lt;a href="http://www.codeplex.com/CloneDetectiveVS/Wiki/View.aspx?title=Videos&amp;referringTitle=Home"&gt;video&lt;/a&gt;. This gave us a good overview of how it works, and where to find it (hiding under View -&gt; Other Windows -&gt; Clone Explorer). During the video it explained that effectively this tool breaks code down into a series of tokens, and then looks for other pieces of code which can also be broken down into the same series of tokens. The terminology took a little while to get a grasp of - there are clones and there are clone classes. A clone class is a series of tokens which is/may be repeated throughout the solution. A clone is an instance of this fragment. So, if there was a line of code which did something like a = b + c, the clone class would be &lt;span class="code"&gt;[var] = [var] + [var]&lt;/span&gt;, and the clones could be lines of code which are &lt;span class="code"&gt;a = b + c&lt;/span&gt; or &lt;span class="code"&gt;d = e + f&lt;/span&gt; (irrespective of whitespace or variable names etc).&lt;br /&gt;&lt;br /&gt;It works across an entire solution, so we set it running and looked at the clones it found. Unfortunately, a lot of the clones that were detected for us were false positives - like properties which when tokenised are the same, but which can't really be re-factored. Additionally, some of the clone classes that it detected, are actually multiple presentations of the same code. For instance, one file I looked at had the following lines all marked as clones of different clone classes:&lt;ul&gt;&lt;li&gt;lines 20 - 43&lt;/li&gt;&lt;li&gt;lines 20 - 59&lt;/li&gt;&lt;li&gt;lines 24 - 43&lt;/li&gt;&lt;li&gt;lines 24 - 44&lt;/li&gt;&lt;li&gt;lines 37 - 59&lt;/li&gt;&lt;/ul&gt;which basically tells us that the area of code from lines 20 - 59 should be re-factored into (probably) one new piece of code which can then be re-analysed to find out if other re-factorings are also worthwhile.&lt;br /&gt;&lt;br /&gt;The interface is easy to get to grips with, and is accessed either via its own panels (Clone Explorer, Clone Intersections and Clone Results) or by an indication in the code window with a context sensitive menu item Find Clones, or Show Clone Intersections. Clicking on an item in the explorer allows you to navigate into more detail and find the clone class or find all instances of it. In a big solution, with a lot of clones (false positives or otherwise) the right click to get clone class listing can take quite a while - in the order of 10 - 20 seconds - which can result in lots of re-right-clicking when one is impatient. This may indicate a problem with scalability.&lt;br /&gt;&lt;br /&gt;The tool would have proved a lot more useful to us with a few extras:&lt;ul&gt;&lt;li&gt;Ability to ignore properties&lt;/li&gt;&lt;li&gt;Ability to list all clone classes at once, rather than having to right click on a file in the Clone Explorer and choose Show Clone Intersections. Then picking a file in there and right clicking to select Clone Class x -&gt; Find All Occurrences&lt;/li&gt;&lt;li&gt;Ability to mark clone classes as "ignore" so it doesn't report on them again&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;So, our summary is, a nice idea but gives too many false positives and isn't refined enough at the moment to be of use within our environment on a regular basis. It may, however, still be useful as a one-off exercise (with patience) to find the key areas that should be addressed. In a less complex code base it might be worth another look. Additionally, if when working on some code we detect something that looks like its been copied and pasted, then it might well be worth running Clone Explorer to find other areas that could benefit from our refactoring.&lt;br /&gt;&lt;br /&gt;This leaves us with the question, how else can we detect clones in our code base? Until we find something else, this is better than nothing.  Does anyone have any suggestions of other tools which could address this area?</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/08/review-clone-detective.html' title='Review: Clone Detective'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=213438709602882603&amp;isPopup=true' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/213438709602882603'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/213438709602882603'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-7724561654122354087</id><published>2008-08-26T15:09:00.000+01:00</published><updated>2008-08-26T15:10:38.599+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='localisation'/><category scheme='http://www.blogger.com/atom/ns#' term='localization'/><title type='text'>Localisation vs Internationalisation</title><content type='html'>A great car manufacturing analogy from &lt;a href="http://www.amazon.co.uk/Beyond-Borders-Globalization-Strategies-Voices/dp/0735712085/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1219759262&amp;sr=8-1"&gt;Beyond Borders: web globalization strategies&lt;/a&gt; has really helped me get the difference between Localisation and Internationalisation clear in my head:&lt;br /&gt;&lt;br /&gt;When a car is designed and built, it is designed to be as appropriate for international markets as possible, so items like the steering wheel can be positioned at the right or left hand side of the car.  So, Internationalisation is the behind the scenes work to make the item configurable and customisable as appropriate for a specific location.&lt;br /&gt;&lt;br /&gt;When you buy a car, you generally buy a car that has been localised for your country - so here in the UK I would buy a right hand drive car, whilst my cousins in the US would buy a left hand drive car.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/08/localisation-vs-internationalisation.html' title='Localisation vs Internationalisation'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=7724561654122354087&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7724561654122354087'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7724561654122354087'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-4100539575529350838</id><published>2008-08-20T20:17:00.002+01:00</published><updated>2008-08-20T20:21:50.437+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>SQL Snap</title><content type='html'>At last September's BarCamp Brighton I saw the &lt;a href="http://www.flickr.com/photos/janed/1352499862/"&gt;CSS Specificity Snap cards&lt;/a&gt; and this gave me an idea.  So, armed with a concept of showing the different ways of producing the same output using the SQL Server 2005 flavour of SQL I started generating SQL statements.  &lt;a href="http://www.beautyandruin.com/monkeys/"&gt;Alex&lt;/a&gt; was kind enough to make them pretty, and last week I ran a &lt;a href="http://jane.dallaway.com/blog/labels/ilp.html"&gt;Madgex ILP&lt;/a&gt; session to play.&lt;br /&gt;&lt;br /&gt;There are 26 cards in my pack, each card has a SQL statement, and a letter.  The letter is used for the crib sheet to allow me to easily spot the matches.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm4.static.flickr.com/3233/2781283523_ca55c563e3.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;I prepared by producing two piles of 13 cards, with a match in both piles.  I then put one pile in the middle of the table face down, and dealt out the rest of the cards to the 5 people who were playing.  I placed the 3 spare cards on the table face up so that we could all see them and proceeded to turn them over one by one.  When a card was turned over everyone looked at the SQL on the overturned card, and checked their cards and the spare cards to see if there was a match.  If a match wasn't spotted then I explained what the SQL was doing, and at only one point did I have to say which letter the matching card would have.  During this I had quite a few comments about functions people didn't recognise - especially &lt;a href="http://www.mssqltips.com/tip.asp?tip=1521"&gt;COALESCE&lt;/a&gt; and &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/09/27/sql-nullif-function.aspx"&gt;NULLIF&lt;/a&gt;.  I also explained some of the performance, and functional differences between the SQL statements when they were seen - so for instance I explained the differences between &lt;a href="http://vadivel.blogspot.com/2004/06/delete-vs-truncate-statement.html"&gt;DELETE and TRUNCATE&lt;/a&gt; but I still I have these as a match because they can provide the same result and provided an interesting talking point.&lt;br /&gt;&lt;br /&gt;The second game we played was a memory game where I placed all of the cards on the table face down and everyone turned over 2 cards at a time.  If the SQL on the cards resulted in the same output, then it was a match and they took the cards away.  If they didn't then the cards got turned back over and the next person had a go.&lt;br /&gt;&lt;br /&gt;All in all this session took about 30 minutes, and resulted in quite a lot of noise and laughter (so much so that someone came and closed the door of the room we were in).  &lt;br /&gt;&lt;br /&gt;The following table contains the Letter Code, SQL statement, and the matching Letter code.  They are ordered so that the matches are grouped together.  &lt;br /&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Code&lt;/td&gt;&lt;td&gt;SQL&lt;/td&gt;&lt;td&gt;Match&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tr&gt;&lt;td&gt;V&lt;/td&gt;&lt;td&gt;SELECT CAST(GETDATE() AS VARCHAR(11))&lt;/td&gt;&lt;td&gt;G&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;G&lt;/td&gt;&lt;td&gt;SELECT CONVERT (VARCHAR(11), GETDATE())&lt;/td&gt;&lt;td&gt;V&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Z&lt;/td&gt;&lt;td&gt;SELECT name FROM sysobjects WHERE xtype = 'P'&lt;/td&gt;&lt;td&gt;Q&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Q&lt;/td&gt;&lt;td&gt;SELECT name FROM sys.procedures&lt;/td&gt;&lt;td&gt;Z&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;D&lt;/td&gt;&lt;td&gt;SELECT sysobjects.name, syscolumns.name FROM sysobjects INNER JOIN syscolumns ON sysobjects.id = syscolumns.id WHERE sysobjects.xtype = 'u'&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;SELECT sysobjects.name, syscolumns.name FROM sysobjects, syscolumns WHERE sysobjects.id = syscolumns.id AND sysobjects.xtype = 'u'&lt;/td&gt;&lt;td&gt;D&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;U&lt;/td&gt;&lt;td&gt;SELECT getdate()&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;EXEC('SELECT getdate()')&lt;/td&gt;&lt;td&gt;U&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;J&lt;/td&gt;&lt;td&gt;DELETE FROM Test&lt;/td&gt;&lt;td&gt;T&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;T&lt;/td&gt;&lt;td&gt;TRUNCATE TABLE Test&lt;/td&gt;&lt;td&gt;J&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;SELECT ISNULL(NULL,1)&lt;/td&gt;&lt;td&gt;W&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;W&lt;/td&gt;&lt;td&gt;SELECT COALESCE(NULL,1)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;P&lt;/td&gt;&lt;td&gt;SELECT [name], xtype FROM sysobjects ORDER BY xtype&lt;/td&gt;&lt;td&gt;M&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;M&lt;/td&gt;&lt;td&gt;SELECT [name], xtype FROM sysobjects ORDER BY 2&lt;/td&gt;&lt;td&gt;P&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;H&lt;/td&gt;&lt;td&gt;SELECT NULLIF(1,1)&lt;/td&gt;&lt;td&gt;R&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;R&lt;/td&gt;&lt;td&gt;SELECT CASE 1 WHEN 1 THEN NULL ELSE 1 END&lt;/td&gt;&lt;td&gt;H&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;SELECT [name], xtype FROM sysobjects ORDER BY 1&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;SELECT [name], xtype FROM sysobjects ORDER BY [name]&lt;/td&gt;&lt;td&gt;I&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;SELECT CAST(GETDATE() AS VARCHAR(20))&lt;/td&gt;&lt;td&gt;F&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;F&lt;/td&gt;&lt;td&gt;SELECT CONVERT (VARCHAR(20), GETDATE())&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;S&lt;/td&gt;&lt;td&gt;SELECT name FROM syscolumns WHERE id = ( SELECT id FROM sysobjects WHERE xtype = 'u' AND name = 'Jobs' )&lt;/td&gt;&lt;td&gt;K&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;K&lt;/td&gt;&lt;td&gt;WITH objects (id) AS&lt;br /&gt;( SELECT id FROM sysobjects WHERE xtype = 'u' AND name = 'Jobs' ) SELECT name FROM syscolumns INNER JOIN objects ON syscolumns.id = objects.id&lt;/td&gt;&lt;td&gt;S&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;L&lt;/td&gt;&lt;td&gt;INSERT INTO Job (JobID, PrimaryJobTypeID) VALUES (1,1)&lt;/td&gt;&lt;td&gt;B&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;INSERT INTO Job (JobID, PrimaryJobTypeID) SELECT 1,1&lt;/td&gt;&lt;td&gt;L&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C&lt;/td&gt;&lt;td&gt;DECLARE @sMessage AS VARCHAR(20) SET @sMessage = 'Hello'&lt;/td&gt;&lt;td&gt;E&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;E&lt;/td&gt;&lt;td&gt;DECLARE @sMessage AS VARCHAR(20) SELECT @sMessage = 'Hello'&lt;/td&gt;&lt;td&gt;C&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/08/sql-snap.html' title='SQL Snap'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=4100539575529350838&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4100539575529350838'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4100539575529350838'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-5767080014315337287</id><published>2008-08-20T18:36:00.006+01:00</published><updated>2008-08-20T18:55:27.921+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='spu_generateinsert'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><title type='text'>Generating insert statements from table data - Updated</title><content type='html'>Yesterday &lt;a href="http://jane.dallaway.com/blog/2007/11/generate-sql-insert-statement-from.html?showComment=1219174920000#c6992035307857457643"&gt;Christian left me a comment&lt;/a&gt; containing an enhancement for the spu_generateinsert SQL.  His suggestion was adding a new parameter @GenerateOneLinePerColumn to allow more "pretty" SQL to be produced - producing all the columns on separate lines which makes it easier for file comparisons to spot differences when comparing data values.  I thought it made sense, so this morning I used an hour of my &lt;a href="http://jane.dallaway.com/blog/labels/ilp.html"&gt;Madgex ILP&lt;/a&gt; time to make this change.&lt;br /&gt;By calling &lt;span class="code"&gt;EXEC spu_GenerateInsert @table = 'Detail',@GenerateOneLinePerColumn = 0&lt;/span&gt; it produces&lt;br /&gt;&lt;span class="code"&gt;-- ** Start of Inserts&lt;br /&gt; INSERT INTO [Detail] ([ID], [Age]) VALUES (1,36)&lt;br /&gt;INSERT INTO [Detail] ([ID], [Age]) VALUES (2,40)&lt;br /&gt;-- ** End of Inserts&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;but by calling &lt;span class="code"&gt;EXEC spu_GenerateInsert @table = 'Detail',@GenerateOneLinePerColumn = 1&lt;/span&gt; it produces&lt;br /&gt;&lt;span class="code"&gt;&lt;br /&gt;-- ** Start of Inserts&lt;br /&gt;INSERT INTO [Detail]&lt;br /&gt;(&lt;br /&gt;[ID],&lt;br /&gt;[Age]&lt;br /&gt;)&lt;br /&gt;VALUES&lt;br /&gt;(&lt;br /&gt;1,&lt;br /&gt;36&lt;br /&gt;)&lt;br /&gt; &lt;br /&gt;INSERT INTO [Detail]&lt;br /&gt;(&lt;br /&gt;[ID],&lt;br /&gt;[Age]&lt;br /&gt;)&lt;br /&gt;VALUES&lt;br /&gt;(&lt;br /&gt;2,&lt;br /&gt;40&lt;br /&gt;)&lt;br /&gt;-- ** End of Inserts&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;Thanks to Christian for this suggestion.  The updated script can be found &lt;a href="http://jane.dallaway.com/downloads/SQL/spu_generateInsert.sql"&gt;here&lt;/a&gt; and related blog posts can be found &lt;a href="http://jane.dallaway.com/blog/labels/spu_generateinsert.html"&gt;here&lt;/a&gt;.  &lt;br /&gt;Note: this is now only working on SQL 2000 as during my testing I found that VARCHAR(8000) just wasn't long enough so I've made it VARCHAR(max).  This is the only SQL 2005 specific piece of SQL in this procedure and I've put comments in the code to indicate &lt;span class="code"&gt;-- change this to be (8000) for SQL Server 2000&lt;/span&gt;.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/08/generating-insert-statements-from-table.html' title='Generating insert statements from table data - Updated'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=5767080014315337287&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/5767080014315337287'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/5767080014315337287'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-549597067159507455</id><published>2008-08-14T17:19:00.003+01:00</published><updated>2008-08-14T17:26:10.733+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer'/><title type='text'>TSQL: sp_executesql vs exec</title><content type='html'>There are 2 methods of executing dynamic SQL :&lt;ul&gt;&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/aa258848(SQL.80).aspx"&gt;Exec&lt;/a&gt; which executes a scalar-valued, user-defined function, a system procedure, a user-defined stored procedure, or an extended stored procedure&lt;/li&gt;&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/aa933299(SQL.80).aspx"&gt;sp_executesql&lt;/a&gt; which executes a SQL statement or batch that can be reused many times, or that has been built dynamically&lt;/li&gt;&lt;/ul&gt;Example SQL Statements - both returning the same results:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="code"&gt;Exec ('Select * from Items')&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="code"&gt;DECLARE @strQuery AS NVARCHAR(50)&lt;br /&gt;SET @strQuery = 'Select * from Items'&lt;br /&gt;EXEC sp_executesql @strQuery&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;As you can see sp_executesql demands a parameter to be sent rather than just a string.  This leads to improved security and improved performance because the SQL statement itself remains constant and only the parameter values change which means that the SQL Server query optimizer is likely to reuse the execution plan it generates for the first execution.&lt;br /&gt;&lt;br /&gt;In addition, when writing a parameterized query, the additional parameters can be specified separately which again adds to the security, see the following which is based on the example on MSDN:&lt;br /&gt;&lt;span class="code"&gt;DECLARE @SQLString NVARCHAR(500)&lt;br /&gt;DECLARE @ParmDefinition NVARCHAR(500)&lt;br /&gt;DECLARE @TitleDefinition NVARCHAR(50) &lt;br /&gt;&lt;br /&gt;/* Build the SQL string once.*/&lt;br /&gt;SET @SQLString =N'SELECT * FROM ItemsWHERE Title like ''%'' + @title + ''%'''&lt;br /&gt;SET @ParmDefinition = N'@title nvarchar(50)' &lt;br /&gt;&lt;br /&gt;* Execute the string with the first parameter value. */ &lt;br /&gt;SET @TitleDefinition = 'S'&lt;br /&gt;EXECUTE sp_executesql @SQLString, @ParmDefinition, @title = @TitleDefinition &lt;br /&gt;&lt;br /&gt;/* Execute the same string with the second parameter value. */&lt;br /&gt;SET @TitleDefinition = 'P'&lt;br /&gt;EXECUTE sp_executesql @SQLString, @ParmDefinition, @title = @TitleDefinition&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So, if you have an option, use sp_executesql instead of exec</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/08/tsql-spexecutesql-vs-exec.html' title='TSQL: sp_executesql vs exec'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=549597067159507455&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/549597067159507455'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/549597067159507455'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-8250876602808704155</id><published>2008-08-07T19:41:00.004+01:00</published><updated>2008-08-07T19:51:07.686+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Learning'/><category scheme='http://www.blogger.com/atom/ns#' term='ilp'/><category scheme='http://www.blogger.com/atom/ns#' term='madgex'/><title type='text'>ILP Time - How did I spend mine?</title><content type='html'>So, the first period of &lt;a href="http://jane.dallaway.com/blog/2008/03/ideas-and-learning-project.html"&gt;ILP&lt;/a&gt; time is over, and I've managed to use 50-60 hours of my allocation attending workshops, watching presentations, learning stuff and doing ideas project work.&lt;br /&gt; &lt;em&gt;Learning&lt;/em&gt;:&lt;ul&gt;&lt;li&gt;Started reading through the &lt;a href="http://www.opera.com/wsc/"&gt;Opera web standards curriculum&lt;/a&gt; to ensure my knowledge was up to date&lt;/li&gt;&lt;li&gt;Attended various presentations including: Stress Management, Zen and the craft of software development, Introduction to unit testing with nUnit, CSS3: Third time's the charm, Comet (a presentation by &lt;a href="http://simonwillison.net/"&gt;Simon Willson&lt;/a&gt; who came in kindly to talk to us), Basic NLP and hypnosis, SQL Server Advanced, Vision and Goal setting, Theories of Management and Javascript inheritance&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt; &lt;em&gt;Ideas&lt;/em&gt;:&lt;ul&gt;&lt;li&gt;Looked into &lt;a href="http://code.google.com/p/dbverse/"&gt;DBVerse&lt;/a&gt;, a database deployment tool, to see if this would help us.  Review &lt;a href="http://jane.dallaway.com/blog/2008/04/dbverse.html"&gt;here&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Looked into &lt;a href="http://code.google.com/p/tarantino/wiki/DatabaseChangeManagement"&gt;Tarantino&lt;/a&gt;, another database deployment tool.  Review &lt;a href="http://jane.dallaway.com/blog/2008/06/database-change-management-tarantino.html"&gt;here&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Worked with &lt;a href="http://siliconbea.ch/"&gt;Bruce&lt;/a&gt; to start writing a database deployment tool to meet our specific requirements.  This is still ongoing and will continue into the next few months&lt;/li&gt;&lt;li&gt;Attended &lt;a href="http://madgex.com/news/2008/madgex-hackday-3/"&gt;Hackday 3&lt;/a&gt; and worked on some keyword searching work with Chris&lt;/li&gt;&lt;li&gt;Looked into &lt;a href="http://code.msdn.microsoft.com/sourceanalysis"&gt;Stylecop&lt;/a&gt;, a code style analyser.  Review &lt;a href="http://jane.dallaway.com/blog/2008/05/stylecop.html"&gt;here&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt; Now I have to start planning out the next years allocation - I'm cashing in a couple of days to attend the &lt;a href="http://www.microsoft.com/uk/remix08/"&gt;ReMix UK conference&lt;/a&gt;, I'm going to continue attending &lt;a href="http://justseventhings.com/"&gt;Simon&lt;/a&gt;'s management and leadership courses, as well as his NLP related ones.  I'm also hosting 2 sessions in the next month - one which is SQL Snap, based on &lt;a href="http://flickr.com/photos/janed/1352499862/"&gt;CSS Specificity snap&lt;/a&gt; and one which is an experimental SQL Coding dojo.  I'm sure I'll report back on both of these afterwards.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/08/ilp-time-how-did-i-spend-mine.html' title='ILP Time - How did I spend mine?'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=8250876602808704155&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/8250876602808704155'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/8250876602808704155'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-6861900449496538904</id><published>2008-08-06T18:28:00.006+01:00</published><updated>2008-08-06T19:53:14.653+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Intersect vs Inner Join - Timings</title><content type='html'>I read the article &lt;a href="http://blog.sqlauthority.com/2008/08/03/sql-server-2005-difference-between-intersect-and-inner-join-intersect-vs-inner-join/"&gt;SQL SERVER - 2005 - Difference Between INTERSECT and INNER JOIN - INTERSECT vs. INNER JOIN&lt;/a&gt; today and was interested in the relative speed of Inner join vs Intersect for simple matcjhes and so thought I'd make &lt;a href="http://jane.dallaway.com/blog/2008/07/tsql-timings.html"&gt;another use&lt;/a&gt; of my &lt;a href="http://jane.dallaway.com/downloads/SQL/Timings.sql"&gt;timing code&lt;/a&gt; to see the difference.&lt;br /&gt;&lt;br /&gt;I produced a couple of very simple tables:&lt;br /&gt;&lt;span class="code"&gt;Summary ([ID] INT, [Name] NVARCHAR(10))&lt;br /&gt;Detail  ([ID] INT, [Age] INT)&lt;/span&gt;&lt;br /&gt;both with primary keys on ID and populated them both as follows:&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;INSERT INTO Summary&lt;br /&gt;SELECT 1, 'Jane' UNION ALL&lt;br /&gt;SELECT 2, 'Richard'&lt;br /&gt;&lt;br /&gt;INSERT INTO Detail&lt;br /&gt;SELECT 1, 36 UNION ALL&lt;br /&gt;SELECT 2,40&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I chose to do a very simple task - retrieve the ID for an entry which is in both Summary and Detail - note: this assumes that the ID is a foreign key constraint and the IDs are relating to the same item.&lt;br /&gt;&lt;br /&gt;Using my rudimentary timing code, I think that intersect is more performant than the inner join - based on running the same query 100000 times&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT Summary.ID&lt;br /&gt;FROM Summary&lt;br /&gt;INNER JOIN Detail&lt;br /&gt;ON Summary.ID = Detail.ID&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;and&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT ID FROM Summary&lt;br /&gt;INTERSECT&lt;br /&gt;SELECT ID FROM Detail &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;which has the following results:&lt;br /&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Description&lt;/td&gt;&lt;td&gt;TimeInMS&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Join&lt;/td&gt;11746&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Intersect&lt;/td&gt;&lt;td&gt;10203&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;I ran them both together and took a look at the actual execution plans and it does indicate that the intersect takes a little less effort - which must be down to the Seek instead of Scan in the Join'd table detail&lt;br /&gt;&lt;img src="http://farm4.static.flickr.com/3219/2738377527_7651f80e84_o.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;So, another option for me to remember to consider when determining the best way to get at some data</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/08/intersect-vs-inner-join-timings.html' title='Intersect vs Inner Join - Timings'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=6861900449496538904&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/6861900449496538904'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/6861900449496538904'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-448215978771503935</id><published>2008-08-01T12:27:00.002+01:00</published><updated>2008-08-03T12:32:38.806+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Learning'/><category scheme='http://www.blogger.com/atom/ns#' term='ilp'/><title type='text'>Learning</title><content type='html'>One of the &lt;a href="http://www.madgex.com"&gt;Madgex&lt;/a&gt; value words is Passionate, and this got me to thinking what am I the most passionate about in my working life and it has to be learning.  And by that I mean both learning myself and encouraging and enabling learning amongst team members and colleagues.  &lt;br /&gt;&lt;br /&gt;In &lt;a href="http://www.amazon.co.uk/Eat-That-Frog-Important-Things/dp/0340835044/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1217414776&amp;sr=8-1"&gt;Eat that frog&lt;/a&gt;  - which I'm currently reading - there is a lovely quote &lt;blockquote&gt;"Continuous learning is the minimum requirement for success in any field"&lt;/blockquote&gt;&lt;br /&gt;One of the things I try and do is read a lot, mostly in the form of blogs where I find useful titbits of information, or clues, or things to go and find out more about, but also books relating to more static skills.  My google reader list now includes more leadership and time management focussed blogs as well due to my recent role change.  I've also cashed in some of my &lt;a href="http://jane.dallaway.com/blog/labels/ilp.html"&gt;ILP&lt;/a&gt; time to follow the &lt;a href="http://dev.opera.com/articles/view/1-introduction-to-the-web-standards-cur/"&gt;Web Standards Curriculum&lt;/a&gt; to ensure that I have a full understanding of the implications of web standards.&lt;br /&gt;&lt;br /&gt;This also leads to me keeping a keen eye on what is going on event-wise within the community - both locally, here in Brighton, and further afield (well near London main-line stations if I'm honest) - that could form an introduction to new techniques, technologies and methodologies.  I've just started sending out a weekly email to Madgex containing all of my finds, so that others can benefit from shared learning experiences.  My key resources for this are upcoming searches for &lt;a href="http://upcoming.yahoo.com/metro/uk/bri/bh/"&gt;Brighton &amp; Hove&lt;/a&gt;, &lt;a href="http://upcoming.yahoo.com/search/?type=Events&amp;rt=0&amp;q=geek&amp;loc=United+Kingdom"&gt;geek&lt;/a&gt;, &lt;a href="http://upcoming.yahoo.com/search/?type=Events&amp;q=.net&amp;loc=United%20Kingdom&amp;rt=0"&gt;.Net&lt;/a&gt;, &lt;a href="http://upcoming.yahoo.com/search/?type=Events&amp;q=barcamp&amp;loc=United%20Kingdom&amp;rt=0"&gt;Barcamp&lt;/a&gt;, &lt;a href="http://upcoming.yahoo.com/search/?type=Events&amp;q=sql&amp;loc=United%20Kingdom&amp;rt=0"&gt;sql&lt;/a&gt;, &lt;a href="http://upcoming.yahoo.com/search/?type=Events&amp;q=developer&amp;loc=United%20Kingdom&amp;rt=0"&gt;developer&lt;/a&gt;  along with &lt;a href="http://msdn.microsoft.com/en-gb/bb905504.aspx"&gt;MSDN events&lt;/a&gt;, &lt;a href="http://www.vbug.co.uk/events/"&gt;VBUG events&lt;/a&gt; and &lt;a href="http://skillsmatter.com/"&gt;Skills Matter&lt;/a&gt; events.&lt;br /&gt;&lt;br /&gt;I'm planning to go to &lt;a href="http://www.microsoft.com/uk/remix08/"&gt;Remix UK&lt;/a&gt; in September, which I'm hoping will be a great way to catch up with new technologies and new methodologies.  I've been to a &lt;a href="http://jane.dallaway.com/blog/labels/WebDD.html"&gt;WebDD&lt;/a&gt;, a &lt;a href="http://jane.dallaway.com/blog/labels/DDD6.html"&gt;DDD&lt;/a&gt; and &lt;a href="http://jane.dallaway.com/blog/2007/10/sqlbits-conference.html"&gt;SQLBits&lt;/a&gt; community conferences before so it will be great to see how Remix differs.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/08/learning.html' title='Learning'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=448215978771503935&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/448215978771503935'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/448215978771503935'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-4569045812533794298</id><published>2008-07-28T16:30:00.005+01:00</published><updated>2008-07-28T16:50:53.393+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='full text index'/><category scheme='http://www.blogger.com/atom/ns#' term='localisation'/><category scheme='http://www.blogger.com/atom/ns#' term='localization'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><title type='text'>Full Text Indexing - the impact of index time and query time language choice</title><content type='html'>Following on from my &lt;a href="http://jane.dallaway.com/blog/2008/07/more-on-sql-server-2005-full-text-index.html"&gt;More on SQL Server 2005 Full Text Index Service&lt;/a&gt; post the other day, I thought I'd give an example of how it works&lt;br /&gt;&lt;h2&gt;Setup&lt;/h2&gt;&lt;br /&gt;I created a table LanguageData which consisted of 2 fields liID and sValue&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;CREATE TABLE [dbo].[LanguageData]&lt;br /&gt;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;[ID] [int] IDENTITY(1,1) NOT NULL,&lt;br /&gt;&amp;nbsp;&amp;nbsp;[Value] [nvarchar](50) NOT NULL,&lt;br /&gt;&amp;nbsp;&amp;nbsp;CONSTRAINT [PK_LanguageData] PRIMARY KEY CLUSTERED&lt;br /&gt;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;[ID] ASC&lt;br /&gt;&amp;nbsp;&amp;nbsp;) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]&lt;br /&gt;) ON [PRIMARY]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I entered some sample data as follows&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;INSERT INTO [LanguageData](Value)&lt;br /&gt;SELECT 'the' UNION &lt;br /&gt;SELECT 'przed' UNION &lt;br /&gt;SELECT 'jakby' &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;where 'the' is featured in the English and Neutral language noise word files, 'przed' and 'jakby' are in the Polish language noise files.  Note: You'll need to have &lt;a href="http://msdn.microsoft.com/en-us/library/ms345188.aspx"&gt;installed the Polish full text index&lt;/a&gt; to make this work.&lt;br /&gt;&lt;br /&gt;Next enable the full text indexing on the database&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;sp_fulltext_database 'enable'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;and then create a full text catalog and an index for the table LanguageData &lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;CREATE FULLTEXT CATALOG LanguageData AS DEFAULT&lt;br /&gt;CREATE FULLTEXT INDEX ON LanguageData ([Value] LANGUAGE 1045 )&lt;br /&gt;KEY INDEX [PK_LanguageData]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;where 1045 indicates the language Polish - retrieved from &lt;br /&gt;&lt;span class="code"&gt;SELECT alias, lcid FROM Sys.syslanguages&lt;br /&gt;WHERE alias = 'Polish'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Scenarios&lt;/h2&gt;&lt;br /&gt;Now, time to run some tests, &lt;br /&gt;&lt;br /&gt;1) Check that all is initially correct, get everything&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;which returns 3 rows, as expected&lt;br /&gt;&lt;br /&gt;2) Get everything which matches the noise word 'jakby'&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*,'jakby')&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns no rows as the word 'jakby' was stripped out at index time, and is also stripped out at query time, and a warning message "Informational: The full-text search condition contained noise word(s)."&lt;br /&gt;&lt;br /&gt;3) Get everything which matches the noise word 'jakby' specifying Polish (1045) in the CONTAINS clause&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'jakby', language 1045 )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns no rows as the word 'jakby' was stripped out at index time, and is also stripped out at query time, and a warning message "Informational: The full-text search condition contained noise word(s)."&lt;br /&gt;&lt;br /&gt;4) Get everything which matches the word 'jakby' specifying US English (1033) in the CONTAINS clause&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'jakby', language 1033 )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns no rows as the word 'jakby' was stripped out at index time.  No warning message is displayed though as 'jakby' is not a noise word for US English&lt;br /&gt;&lt;br /&gt;5) Get everything which matches the word 'the'&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'the')&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns one row, as 'the' isn't a noise word in Polish and so wasn't stripped out at index time or at query time&lt;br /&gt;&lt;br /&gt;6)  Get everything which matches the word 'the' specifying Polish in the CONTAINS clause&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'the', language 1045 )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns one row, as 'the' isn't a noise word in Polish and so wasn't stripped out at index time or at query time&lt;br /&gt;&lt;br /&gt;7)  Get everything which matches the word 'the' specifying US English in the CONTAINS clause&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'the', language 1033 )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns no rows as 'the' is a noise word in US English and therefore is excluded at query time.  A warning message "Informational: The full-text search condition contained noise word(s)." is displayed&lt;br /&gt;&lt;br /&gt;Now to make it more interesting, lets add some data which combines noise words with normal words&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;INSERT INTO [LanguageData] (Value)&lt;br /&gt;VALUES&lt;br /&gt;('jakby przed the test')&lt;/span&gt;&lt;br /&gt;which includes 2 polish noise words, one english noise word and one remaining word&lt;br /&gt;&lt;br /&gt;8) Get everything which matches the word 'jakby'&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'jakby')&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns no rows as the word 'jakby' was stripped out at index time, and is also stripped out at query time, and a warning message "Informational: The full-text search condition contained noise word(s)."  is displayed&lt;br /&gt;&lt;br /&gt;9) Get everything which matches the word 'the' &lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'the')&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns 2 rows, both the individual 'the' entry and the new 'jakby przed the test' rows.  No message is displayed.&lt;br /&gt;&lt;br /&gt;10) Get everything which matches the word 'the' using an explicit query language of Polish&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'the', language 1045)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns 2 rows, both the individual 'the' entry and the new 'jakby przed the test' rows.  No message is displayed.&lt;br /&gt;&lt;br /&gt;11) Get everything which matches the word 'the' using an explicit query language of English&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'the', language 1033 )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;returns no rows as the word 'the' was stripped at query time according to the noise words for 1033.  A warning message "Informational: The full-text search condition contained noise word(s)."  is displayed&lt;br /&gt;&lt;br /&gt;And then to make it even more interesting, lets add a new word 'jane' to the LanguageData dataset, and to the noisewords file for the Neutral language (LCID 0) which (on my machine at least) is at C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\FTData\noiseNEU.txt&lt;br /&gt;&lt;br /&gt;To get the full text indexing service to pick up the changes to the noise files, you need to restart the service via the Control Panel -&gt; Administrative Tools -&gt; Service dialog&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;INSERT INTO LanguageData (Value)&lt;br /&gt;VALUES ('jane')&lt;br /&gt;&lt;br /&gt;12) Get everything which matches the word 'jane' using the implicit query language (Polish)&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'jane')&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;which returns 1 row, as 'jane' isn't a polish noise word and wasn't stripped out at either index or query time&lt;br /&gt;&lt;br /&gt;13) Get everything which matches the word 'jane' using the explicit query language English&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'jane', language 1033 )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;which returns 1 row, as 'jane' isn't a polish noise word and so wasn't stripped out an index time, neither is it an english noise word so isn't stripped out at query time either&lt;br /&gt;&lt;br /&gt;14) Get everything which matches the word 'jane' using the explicit query language Neutral&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'jane', language 0 )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;which returns 0 rows as 'jane' is a neutral noise word and so is stripped out at index time.  A warning message "Informational: The full-text search condition contained noise word(s)."  is displayed&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Summary&lt;/h2&gt;&lt;br /&gt;What this shows, is that when you choose a language to set your full text index up as, this impacts the words which will be stripped out of the index as anything defined as noise will be removed.  This has an impact on the choice of language when different language content is being indexed as we need to be clear that what is one languages noise word, isn't another ones non-noise word.  &lt;ul&gt;&lt;li&gt;When querying a full text index, it is possible to specify that the query you are running is for a particular language, but if you do and if the language is different to that you set the index up as, then you'll remove 2 sets of noise words from your search - both those that were set up when the index was defined, but also those based on the language specified in the query&lt;/li&gt;&lt;li&gt;The noise files are defined on an instance by instance basis and so any alterations to the noise file will affect all full text indexes on an instance.&lt;/li&gt;&lt;li&gt;To pick up changes to the noise files, the service needs to be restarted.&lt;/li&gt;&lt;li&gt;SQL Server 2008 seems to change this and so more research will be required - it relies on STOPLISTs instead.&lt;/li&gt;&lt;/ul&gt;</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/full-text-indexing-impact-of-index-time.html' title='Full Text Indexing - the impact of index time and query time language choice'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=4569045812533794298&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4569045812533794298'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4569045812533794298'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-7978895338603430228</id><published>2008-07-24T20:02:00.002+01:00</published><updated>2008-07-24T20:20:16.862+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='full text index'/><category scheme='http://www.blogger.com/atom/ns#' term='localisation'/><category scheme='http://www.blogger.com/atom/ns#' term='localization'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><title type='text'>More on SQL Server 2005 Full Text Index Service</title><content type='html'>In my previous post about &lt;a href="http://jane.dallaway.com/blog/2008/07/how-to-work-out-which-are-valid-full.html"&gt;How to work out which are valid full text languages on a SQL Server 2005 instance&lt;/a&gt; I referred to &lt;span class="code"&gt;sys.syslanguages&lt;/span&gt; and &lt;span class="code"&gt;sys.fulltext_languages&lt;/span&gt; in my queries, but didn't really say much more about them, so here goes&lt;br /&gt;&lt;h2&gt;sys.syslanguages&lt;/h2&gt;&lt;br /&gt;In the &lt;a href="http://msdn.microsoft.com/en-us/library/ms190303.aspx"&gt;definition on MSDN&lt;/a&gt; it states&lt;blockquote&gt;"Contains one row for each language present in the instance of SQL Server 2005. Although U.S. English is not in syslanguages, it is always available to SQL Server."&lt;/blockquote&gt;&lt;br /&gt;And one thing on the choice of U.S. English vs UK English.  The &lt;a href="http://www.simple-talk.com/sql/learn-sql-server/sql-server-full-text-search-language-features/"&gt;SQL Server Full Text Search: Language Features&lt;/a&gt; says &lt;blockquote&gt;"In actual fact UK English does not refer to the Queen's English or the English used in the United Kingdom, but International English; the English that is used in all other English speaking countries other than US English."&lt;/blockquote&gt;&lt;br /&gt;As an English person, living in England and speaking English I find this a somewhat grating use of the phrase UK English.  Bah!&lt;br /&gt; &lt;h2&gt;sys.fulltext_languages&lt;/h2&gt;&lt;br /&gt;In the &lt;a href="http://msdn.microsoft.com/en-us/library/ms176076.aspx"&gt;definition on MSDN&lt;/a&gt; it states  &lt;blockquote&gt;"This catalog view contains one row per language available for full-text indexing/querying operations. Each row provides an unambiguous representation of the available full-text linguistic resources that are registered with Microsoft SQL Server. The name or lcid can be specified in the full-text queries and full-text index DDL."&lt;/blockquote&gt;&lt;br /&gt;The list in this table, doesn't match those in sys.syslanguages.  These are purely the full-text-indexable languages.  As I mentioned in my &lt;a href="ttp://jane.dallaway.com/blog/2008/07/how-to-work-out-which-are-valid-full.html"&gt;previous post&lt;/a&gt; 6 languages can be added by following &lt;a href="http://msdn.microsoft.com/en-us/library/ms345188.aspx"&gt;these instructions&lt;/a&gt;.  The line &lt;blockquote&gt;"The name or lcid can be specified in the full-text queries and full-text index DDL."&lt;/blockquote&gt; refers to the ability to issue the following SQL:&lt;br /&gt;&lt;span class="code"&gt;SELECT * &lt;br /&gt;FROM LanguageData&lt;br /&gt;WHERE CONTAINS(*, 'the', language 1045 )&lt;/span&gt; &lt;br /&gt;which indicates that the locale used for querying should be 1045, which equates to Polish.  I have some sample SQL to post in the next few days which demonstrates the difference between indexing and querying language choices.&lt;br /&gt;&lt;h2&gt;In General&lt;/h2&gt;&lt;br /&gt;I've been doing quite a bit of work with trying to understand how the SQL Server 2005 full text index works, and how the language choice impacts it.  My knowledge of full text indexing as a whole to this stage hasn't been great, so I've done quite a lot of background reading.  Amongst the best resources I've found are:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.simple-talk.com/sql/learn-sql-server/sql-server-full-text-search-language-features/"&gt;SQL Server Full Text Search: Language Features&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.simple-talk.com/sql/learn-sql-server/sql-server-full-text-search-language-features,-part-2/"&gt;SQL Server Full Text Search Language Features, Part 2&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; both by &lt;a href="http://www.simple-talk.com/author/hilary-cotter/"&gt;Hillary Cotter&lt;/a&gt; which provide a really simple, but yet pretty comprehensive introduction to the various features of indexing and querying using the Full Text Index service.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/more-on-sql-server-2005-full-text-index.html' title='More on SQL Server 2005 Full Text Index Service'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=7978895338603430228&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7978895338603430228'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7978895338603430228'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-4568711581912411457</id><published>2008-07-24T19:36:00.002+01:00</published><updated>2008-07-24T20:22:46.763+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='full text index'/><category scheme='http://www.blogger.com/atom/ns#' term='localisation'/><category scheme='http://www.blogger.com/atom/ns#' term='localization'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><title type='text'>How to work out which are valid full text languages on a SQL Server 2005 instance</title><content type='html'>Despite SQL Server 2005 supporting 33 languages (found by issuing &lt;span class="code"&gt;SELECT * FROM sys.syslanguages&lt;/span&gt;), not all of these are available for the full text index service.  To find out which ones are run the query:&lt;br /&gt;&lt;span class="code"&gt;SELECT *&lt;br /&gt;FROM sys.fulltext_languages&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;On my machine, this returns the following languages: &lt;ul&gt;&lt;li&gt;British English&lt;/li&gt;&lt;li&gt;Chinese (Hong Kong SAR, PRC)&lt;/li&gt;&lt;li&gt;Chinese (Macau SAR)&lt;/li&gt;&lt;li&gt;Chinese (Singapore)&lt;/li&gt;&lt;li&gt;Simplified Chinese&lt;/li&gt;&lt;li&gt;Traditional Chinese&lt;/li&gt;&lt;li&gt;Dutch&lt;/li&gt;&lt;li&gt;English&lt;/li&gt;&lt;li&gt;French&lt;/li&gt;&lt;li&gt;German&lt;/li&gt;&lt;li&gt;Italian&lt;/li&gt;&lt;li&gt;Japanese&lt;/li&gt;&lt;li&gt;Korean&lt;/li&gt;&lt;li&gt;Neutral&lt;/li&gt;&lt;li&gt;Spanish&lt;/li&gt;&lt;li&gt;Swedish&lt;/li&gt;&lt;li&gt;Thai&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;An additional &lt;a href="http://msdn.microsoft.com/en-us/library/ms345188.aspx"&gt;6 languages are supported and available for a separate install&lt;/a&gt;.  These are :&lt;ul&gt;&lt;li&gt;Danish&lt;/li&gt;&lt;li&gt;Polish&lt;/li&gt;&lt;li&gt;Português (Brasil)&lt;/li&gt;&lt;li&gt;Portuguese&lt;/li&gt;&lt;li&gt;Russian&lt;/li&gt;&lt;li&gt;Turkish&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt; To install these, follow the instructions &lt;a href="http://msdn.microsoft.com/en-us/library/ms345188.aspx"&gt;here&lt;/a&gt;.&lt;br /&gt; The following languages are not supported for full text searching at all within SQL Server 2005:&lt;ul&gt;&lt;li&gt;Arabic&lt;/li&gt;&lt;li&gt;Bulgarian&lt;/li&gt;&lt;li&gt;Croatian&lt;/li&gt;&lt;li&gt;Czech&lt;/li&gt;&lt;li&gt;Estonian&lt;/li&gt;&lt;li&gt;Finnish&lt;/li&gt;&lt;li&gt;Greek&lt;/li&gt;&lt;li&gt;Hungarian&lt;/li&gt;&lt;li&gt;Latvian&lt;/li&gt;&lt;li&gt;Lithuanian&lt;/li&gt;&lt;li&gt;Norwegian&lt;/li&gt;&lt;li&gt;Romanian&lt;/li&gt;&lt;li&gt;Slovak&lt;/li&gt;&lt;li&gt;Slovenian&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;SQL Server 2008 offers &lt;a href="http://msdn.microsoft.com/en-us/library/ms142509(SQL.100).aspx"&gt;more full text language support&lt;/a&gt; bringing the total of available languages to 50.  It would appear that Danish, Polish and Turkish remain &lt;a href="http://msdn.microsoft.com/en-us/library/ms345188(SQL.100).aspx"&gt;installable additions&lt;/a&gt;.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/how-to-work-out-which-are-valid-full.html' title='How to work out which are valid full text languages on a SQL Server 2005 instance'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=4568711581912411457&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4568711581912411457'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4568711581912411457'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-7116707565347070945</id><published>2008-07-23T15:43:00.005+01:00</published><updated>2008-07-23T15:54:50.204+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='localisation'/><category scheme='http://www.blogger.com/atom/ns#' term='localization'/><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio'/><title type='text'>Localisation, Javascript and extended character sets in Visual Studio 2005</title><content type='html'>I'm currently doing some work on looking into producing a localised version of a &lt;a href="http://www.madgex.com/job-boards/"&gt;Madgex job board&lt;/a&gt; (not dis-similar to the &lt;a href="http://jane.dallaway.com/blog/2007/06/localisation.html"&gt;work I did this time last year&lt;/a&gt;)and am mainly looking at the SQL Server and javascript areas whilst a colleague looks at the .NET side.  &lt;a href="http://www.glennjones.net/Home/"&gt;Glenn&lt;/a&gt; gave me a tip off that when he'd been doing something similar, he'd had problems with Visual Studio 2005 not saving his javascript files as UTF.&lt;br /&gt;&lt;br /&gt;So, within VS2005 I created a javascript file and put 2 lines into it.  They were simply:&lt;br /&gt;&lt;span class="code"&gt;alert ('hello world');&lt;br /&gt;alert('Zarys gramatyki por¢wnawczej jezyk¢w slowianskich');&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I then linked this into a (very) basic HTML page&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" &amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;title&amp;gt;i18n&amp;lt;/title&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;script type="text/javascript" src="js/i18n.js" language="javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;so that on pageload 2 alert boxes are displayed, one saying 'Hello World' and the one saying 'Zarys gramatyki por¢wnawczej jezyk¢w slowianskich'.&lt;br /&gt;&lt;br /&gt;Unfortunately what is displayed instead is:&lt;br /&gt;&lt;IMG src="http://farm4.static.flickr.com/3193/2696000168_cfbd0cdd9e_o.jpg"&gt;&lt;br /&gt;which isn't exactly what I had in mind.&lt;br /&gt;&lt;br /&gt;I opened the file in &lt;a href="http://notepad-plus.sourceforge.net/uk/about.php"&gt;Notepad++&lt;/a&gt; (my text editor of choice) to take a look at the file type and it is, as I'd expected, saved as ANSI, not UTF-8 or UTF-16&lt;br /&gt;&lt;IMG src="http://farm4.static.flickr.com/3075/2696000258_ba8195aaeb.jpg"&gt;&lt;br /&gt;&lt;br /&gt;I used Notepad++'s menu item Format -&gt; Convert to UTF-8 to convert this file from ANSI into UTF-8, and then re-ran my test and all works correctly as expected.  Hurrah!&lt;br /&gt;&lt;br /&gt;I then repeated this using VS2008 and found that this is one of the fixes over VS2005.&lt;br /&gt;&lt;br /&gt;So, the alert now correctly displays:&lt;br /&gt;&lt;IMG src="http://farm4.static.flickr.com/3175/2695182803_95b968c8df_o.jpg"&gt;&lt;br /&gt;and when opened in Notepad++ the file is now, correctly, UTF-8.&lt;br /&gt;&lt;IMG src="http://farm4.static.flickr.com/3165/2696000328_bbf4f011e6.jpg"&gt;</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/localisation-javascript-and-extended.html' title='Localisation, Javascript and extended character sets in Visual Studio 2005'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=7116707565347070945&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7116707565347070945'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7116707565347070945'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-7848134733027714080</id><published>2008-07-21T19:31:00.003+01:00</published><updated>2008-07-21T19:34:37.108+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='article'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Post-Implementation/Post-Project Reviews</title><content type='html'>In my almost 15 years of development, I've found little more beneficial than a well run Post-Implementation Review meeting.  I find them a great to way to learn, improve and ensure that the next project goes more smoothly than the one before.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;What is a Post-Implementation/Post-Project Review?&lt;/h2&gt;&lt;br /&gt;It is a meeting held at the end of a project at which people who have contributed to the project as a whole get an opportunity to discuss the highs and lows of the project.&lt;br /&gt;&lt;br /&gt;My preparation normally involves thinking back over the course of the project and thinking about:&lt;ul&gt;&lt;li&gt;what went well?&lt;/li&gt;&lt;li&gt;what didn't go so well?&lt;/li&gt;&lt;li&gt;what we could do better next time and what lessons we can learn&lt;/li&gt;&lt;li&gt;How well the project was analysed and specified&lt;/li&gt;&lt;li&gt;how well the project was managed&lt;/li&gt;&lt;li&gt;how well the testing phase went - bugs found in testing vs UAT vs post-live&lt;/li&gt;&lt;li&gt;how well the handover to support went&lt;/li&gt;&lt;li&gt;how well the original time estimates reflected reality&lt;/li&gt;&lt;li&gt;how well specified the infrastructure was - were the original estimates on page impressions etc valid&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;The most recent one I attended was run in the order of the project, so feedback was made first against the Sales process, then the Analysis process etc.  This worked pretty well, but did mean that the last few stages of the project were rushed to ensure that the meeting finished on time.  Alternatives include asking the "What went well?", "What didn't go well?" questions of every person in the room.  This ensures that everyone gets their say but does involve preparation on behalf of every attendee (no bad thing).&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Who should be there?&lt;/h2&gt;&lt;br /&gt;For me, the ideal meeting should include everyone who has been involved with the project, from start to finish - in some cases this could be a lot of people but  every function should be represented - so definitely Sales, Analysts, Project Managers, Developers, Support and Systems.  Every person should have an equal opportunity to speak.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;When should it happen?&lt;/h2&gt;&lt;br /&gt;Usually, after the project has gone live and been handed over into a support phase.  In some cases a project can last too long, and if the project is scheduled to take more than 6 months, its probably worth having 6 monthly review meetings to ensure that key learnings aren't forgotten, or that subsequent projects can learn and improve quickly.  These shouldn't replace the Post Implementation Review but should supplement it.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;What should happen afterwards?&lt;/h2&gt;&lt;br /&gt;The final part of the meeting should be a quick review of the "What went well?" items, and of the "Key learnings".  Someone should be tasked with producing a document which should then be circulated outlining the key learnings from the project, and also the highs - the lows should be kept within the team and learnt from but not circulated - it shouldn't be a shaming exercise but should be a great motivator.  Any individuals charged with process review, or implementing changes to current/ongoing projects should be informed of the key learnings to ensure these learnings are escalated and implemented as quickly as possible.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/post-implementationpost-project-reviews.html' title='Post-Implementation/Post-Project Reviews'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=7848134733027714080&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7848134733027714080'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7848134733027714080'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-7577778962136742829</id><published>2008-07-18T13:24:00.008+01:00</published><updated>2008-07-18T14:43:27.291+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>TSQL: Enumations and constants</title><content type='html'>&lt;a href="http://siliconbea.ch/"&gt;Bruce&lt;/a&gt; sent me a link the other day to an article &lt;a href="http://www.olegsych.com/2008/07/t4-template-for-generating-sql-view-from-csharp-enumeration/"&gt;T4 template for generating SQL view from C# enumeration&lt;/a&gt; which I found interesting from a modelling constants/enumerations in SQL viewpoint.&lt;br /&gt;&lt;br /&gt;The example used was modelling an enumeration of ContactType which has valid items of Individual and Organisation.&lt;br /&gt;&lt;br /&gt;The article used a view to model this, as per&lt;span class="code"&gt;&lt;br /&gt;CREATE VIEW enumContactType&lt;br /&gt;AS&lt;br /&gt;&amp;nbsp;&amp;nbsp;SELECT&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0 AS Individual,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1 AS Organization&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;and then using it within a SELECT as&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;SELECT * &lt;br /&gt;FROM Contact&lt;br /&gt;WHERE Type = (SELECT Organization FROM enumContactType)&lt;/span&gt;&lt;br /&gt;(Note: in the original article Oleg used a schema called enum, but I'm just ignoring this at the moment and have thus changed the name from enum.ContactType to enumContactType)&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;An alternative&lt;/h2&gt;&lt;br /&gt;In my previous company, we used &lt;a href="http://www.sqlteam.com/article/user-defined-functions"&gt;Scalar-Valued Functions&lt;/a&gt; to mimic constants, and I guess this could be extended to enumerations.  I thought I'd re-create the above example and give it a try to see how it looks and compares.&lt;br /&gt;&lt;br /&gt;So, to model the enumeration ContactType, I've created two functions as follows:&lt;span class="code"&gt;&lt;br /&gt;CREATE FUNCTION enumContactTypeIndividual()&lt;br /&gt;RETURNS INT&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;&amp;nbsp;&amp;nbsp;RETURN 0&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;CREATE FUNCTION enumContactTypeOrganisation()&lt;br /&gt;RETURNS INT&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;&amp;nbsp;&amp;nbsp;RETURN 1&lt;br /&gt;END&lt;br /&gt;GO&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;And then to reproduce the SELECT query I wrote:&lt;br /&gt;&lt;span class="code"&gt;SELECT *&lt;br /&gt;FROM Contact&lt;br /&gt;WHERE Type = dbo.enumContactTypeOrganisation()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The resulting data matches that used in the VIEW model and provides an alternative.  I'm sure that a template could be written to produce those functions as an output as per the end part of &lt;a href="http://www.olegsych.com/2008/07/t4-template-for-generating-sql-view-from-csharp-enumeration/"&gt;Oleg's article&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Performance and timings&lt;/h2&gt;&lt;br /&gt;I was interested in the relative performance of these two methods, so armed with my &lt;a href="http://jane.dallaway.com/downloads/SQL/Timings.sql"&gt;timing code&lt;/a&gt; from &lt;a href="http://jane.dallaway.com/blog/2008/07/tsql-timings.html"&gt;last week&lt;/a&gt; I checked them out.  I amended the SELECT to bring back the COUNT(*) FROM Contact into a local integer variable, and ran it 1000000 times.&lt;br /&gt;&lt;br /&gt;The results are as follows:&lt;br /&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Code&lt;/td&gt;&lt;td&gt;Description&lt;/td&gt;&lt;td&gt; TimeInMS&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tr&gt;&lt;td&gt;EnumView&lt;/td&gt;&lt;td&gt;Using the view&lt;/td&gt;&lt;td&gt;13010&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;EnumUDF&lt;/td&gt;&lt;td&gt;Using the UDF&lt;/td&gt;&lt;td&gt;21450&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;showing that the view method is more performant.&lt;br /&gt;&lt;br /&gt;I then changed the &lt;a href="http://blogs.msdn.com/sqlprogrammability/archive/2006/05/12/596424.aspx"&gt;function&lt;/a&gt; to make use of SCHEMABINDING.  The new functions look like:&lt;br /&gt;&lt;span class="code"&gt;CREATE FUNCTION enumContactTypeIndividual()&lt;br /&gt;RETURNS INT&lt;br /&gt;WITH SCHEMABINDING&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;&amp;nbsp;&amp;nbsp;RETURN 0&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;CREATE FUNCTION enumContactTypeOrganisation()&lt;br /&gt;RETURNS INT&lt;br /&gt;WITH SCHEMABINDING&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;&amp;nbsp;&amp;nbsp;RETURN 1&lt;br /&gt;END&lt;br /&gt;GO&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;And the timings change to be:&lt;br /&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Code&lt;/td&gt;&lt;td&gt;Description&lt;/td&gt;&lt;td&gt; TimeInMS&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tr&gt;&lt;td&gt;EnumView&lt;/td&gt;&lt;td&gt;Using the view&lt;/td&gt;&lt;td&gt;13010&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;EnumUDF&lt;/td&gt;&lt;td&gt;Using the UDF&lt;/td&gt;&lt;td&gt;20280&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;which do reduce the time taken for the UDF but still means that the view is faster.&lt;br /&gt;&lt;br /&gt;For interests sake I then ran a comparison timing against the code using the literal as:&lt;br /&gt;&lt;span class="code"&gt;SELECT *&lt;br /&gt; FROM Contact&lt;br /&gt; WHERE Type = 1&lt;/span&gt;&lt;br /&gt;which resulted in&lt;br /&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Code&lt;/td&gt;&lt;td&gt;Description&lt;/td&gt;&lt;td&gt; TimeInMS&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tr&gt;&lt;td&gt;EnumLiteral&lt;/td&gt;&lt;td&gt;Using the literal&lt;/td&gt;&lt;td&gt;12043&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;showing it is faster, but not by much, than the view.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Summary&lt;/h2&gt;&lt;br /&gt;So, what has this shown?&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Using a view is quite efficient and effective for modelling enumerations&lt;/li&gt;&lt;li&gt;Using a UDF is an alternative, but is slower&lt;/li&gt;&lt;li&gt;Schema binding makes UDF usage quicker&lt;/li&gt;&lt;li&gt;The difference between using a VIEW and using the hard-coded literal isn't a lot in perfomance terms&lt;/li&gt;&lt;/ul&gt;</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/tsql-enumations-and-constants.html' title='TSQL: Enumations and constants'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=7577778962136742829&amp;isPopup=true' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7577778962136742829'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7577778962136742829'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-4353391686541384765</id><published>2008-07-08T14:20:00.004+01:00</published><updated>2008-07-08T14:29:46.594+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>INFORMATION_SCHEMA views</title><content type='html'>As I &lt;a href="http://jane.dallaway.com/blog/2008/06/find-string-in-stored-procedure.html"&gt;alluded&lt;/a&gt; to the other day, I'm gradually weaning myself off my dependency on (the fairly ugly) &lt;a href="http://msdn.microsoft.com/en-us/library/ms190324.aspx"&gt;sys.objects&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms176106.aspx"&gt;sys.columns&lt;/a&gt; etc as a way to query the meta data about my database.  Instead I'm using the SQL-92 compliant &lt;a href="http://msdn.microsoft.com/en-us/library/ms186778.aspx"&gt;INFORMATION_SCHEMA views&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Information schema views provide an internal, system table-independent view of the SQL Server metadata. Information schema views enable applications to work correctly although significant changes have been made to the underlying system tables. The information schema views included in SQL Server 2005 comply with the SQL-92 standard definition for the INFORMATION_SCHEMA.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;So now, when I'm writing database upgrade scripts and attempting to write &lt;a href="http://jane.dallaway.com/blog/2008/03/defensive-sql-updates.html"&gt;defensive SQL&lt;/a&gt; (which is my usual position these days, regardless of whether I think the script will be run more than once - lets just say I've learnt from making such assumptions) I usually wrap &lt;br /&gt;&lt;span class="code"&gt;ALTER TABLE&lt;/span&gt; &lt;br /&gt;statements within &lt;br /&gt;&lt;span class="code"&gt;IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE Table_Name = 'MyTable' AND Column_Name = 'MyNewColumn')&lt;/span&gt;, &lt;br /&gt;&lt;span class="code"&gt; CREATE TABLE&lt;/span&gt; &lt;br /&gt;statements within &lt;br /&gt;&lt;span class="code"&gt;IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE Table_Name = 'MyTable')&lt;/span&gt; etc&lt;br /&gt;&lt;br /&gt;The main area that I have to revert to the sys views for is indexes, and finding out what columns are included in which index, which is the uglier, but no-less-effective&lt;br /&gt;&lt;span class="code"&gt;SELECT&lt;br /&gt;&amp;nbsp;&amp;nbsp;OBJECT_NAME (i.object_id) AS Tablename,&lt;br /&gt;&amp;nbsp;&amp;nbsp;i.name AS IndexName,&lt;br /&gt;&amp;nbsp;&amp;nbsp;c.name AS ColumnName,&lt;br /&gt;&amp;nbsp;&amp;nbsp;CASE ic.is_descending_key&lt;br /&gt;&amp;nbsp;&amp;nbsp;WHEN 1 THEN 'DESC'&lt;br /&gt;&amp;nbsp;&amp;nbsp;ELSE 'ASC'&lt;br /&gt;&amp;nbsp;&amp;nbsp;END as ColumnSort&lt;br /&gt;FROM sys.indexes i&lt;br /&gt;INNER JOIN sys.index_columns ic &lt;br /&gt;ON i.object_id = ic.object_id&lt;br /&gt;AND i.index_id = ic.index_id&lt;br /&gt;INNER JOIN sys.columns c &lt;br /&gt;ON ic.object_id = c.object_id&lt;br /&gt;AND ic.column_id = c.column_id&lt;br /&gt;INNER JOIN sys.objects o&lt;br /&gt;on c.object_id = o.object_id&lt;br /&gt;WHERE o.type = 'U'&lt;br /&gt;ORDER BY TableName, indexName, ic.key_ordinal&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;MSDN has an interesting article &lt;a href="http://msdn.microsoft.com/en-us/library/ms345522.aspx"&gt;Querying the SQL Server System Catalog FAQ&lt;/a&gt; which has examples for finding out (using the various &lt;a href="http://msdn.microsoft.com/en-us/library/ms189783.aspx"&gt;object catalog views&lt;/a&gt;) many different areas of meta data across a SQL Server 2005 database and is worth using as a starting point.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/informationschema-views.html' title='INFORMATION_SCHEMA views'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=4353391686541384765&amp;isPopup=true' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4353391686541384765'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/4353391686541384765'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-7892202753718479043</id><published>2008-07-07T19:11:00.009+01:00</published><updated>2008-07-07T19:35:54.934+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>TSQL - Timings</title><content type='html'>A while ago I blogged about &lt;a href="http://jane.dallaway.com/blog/2008/05/tsql-how-to-get-date-element-of.html"&gt;how to get the date element of a datetime column&lt;/a&gt; in TSQL.  In that post I said&lt;br /&gt;&lt;blockquote&gt;I would probably have done it via a CONVERT/CAST operation, converting to a VARCHAR and then back to a DATETIME, but this is a much more efficient method.&lt;/blockquote&gt;&lt;br /&gt;but I didn't prove it at the time.  I gave it some more thought and wanted to know what the differences were, so I wrote some &lt;a href="http://jane.dallaway.com/downloads/SQL/Timings.sql"&gt;timings&lt;/a&gt; code.  &lt;br /&gt;&lt;br /&gt;This script creates one table &lt;span class="code"&gt;Timings&lt;/span&gt; with columns of &lt;span class="code"&gt;Code&lt;/span&gt;, &lt;span class="code"&gt;Description&lt;/span&gt;, &lt;span class="code"&gt;ActionTime&lt;/span&gt; and &lt;span class="code"&gt;IsComplete&lt;/span&gt;.  It has a combined primary key of Code and IsComplete.  Code must be unique - and can be a string of up to 10 characters long to uniquely identify the action being timed.  IsComplete is used to differentiate between the start time and end time of the process being monitored.&lt;br /&gt;&lt;br /&gt;The script also creates 3 stored procedures:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="code"&gt;up_RecordStart&lt;/span&gt; which takes 2 parameters - the unique code and optional description.  This is used to record the start of the activity being monitored.&lt;/li&gt;&lt;li&gt;&lt;span class="code"&gt;up_RecordEnd&lt;/span&gt; which takes just 1 parameter - the code - should match the code used in up_RecordStart.  This is used to record the end of the activity being monitored.&lt;/li&gt;&lt;li&gt;&lt;span class="code"&gt;up_GetTimings&lt;/span&gt; which again takes just 1 parameter - the code to return the timings from.  It then returns the Code, Description and the length of time the action took in ms.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I wrote some script to then use these objects to test the assertion I made that FLOOR and combinations of converting DATETIME to FLOAT etc would be more efficient than using either CAST or CONVERT to VARCHAR(12) and back again to a DATETIME.&lt;br /&gt;&lt;br /&gt;&lt;span class="code"&gt;------------------------------&lt;br /&gt;-- Clean up before we start --&lt;br /&gt;------------------------------&lt;br /&gt;DELETE FROM Timings&lt;br /&gt;WHERE Code IN ('FLR','CONVERT','CAST')&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;---------------------&lt;br /&gt;-- Try using Floor --&lt;br /&gt;---------------------&lt;br /&gt;EXEC up_RecordStart @Code='FLR', @Description='SELECT CONVERT(DATETIME,FLOOR(CONVERT(FLOAT,GETDATE())))'&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;DECLARE @i AS INTEGER&lt;br /&gt;DECLARE @floorDate AS DATETIME&lt;br /&gt;SET @i = 0&lt;br /&gt; &lt;br /&gt;WHILE @i &lt; 1000000 -- try the next statement for 1000000 times - this should be enough to see some differences&lt;br /&gt;BEGIN&lt;br /&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;SET @floorDate = CONVERT(DATETIME,FLOOR(CONVERT(FLOAT,GETDATE())))&lt;br /&gt;&amp;nbsp;&amp;nbsp;SET @i = @i + 1&lt;br /&gt;END&lt;br /&gt;PRINT @floorDate&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;EXEC up_RecordEnd @Code='FLR'&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;-----------------------&lt;br /&gt;-- Try using convert --&lt;br /&gt;-----------------------&lt;br /&gt;EXEC up_RecordStart @Code='CONVERT', @Description='SELECT CONVERT(DATETIME,CONVERT(VARCHAR(12),GETDATE()))'&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;DECLARE @i AS INTEGER&lt;br /&gt;DECLARE @floorDate AS DATETIME&lt;br /&gt;SET @i = 0&lt;br /&gt;WHILE @i &lt; 1000000&lt;br /&gt;BEGIN&lt;br /&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;SET @floorDate = CONVERT(DATETIME,CONVERT(VARCHAR(12),GETDATE()))&lt;br /&gt;&amp;nbsp;&amp;nbsp;SET @i = @i + 1&lt;br /&gt;END&lt;br /&gt;PRINT @floorDate&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;EXEC up_RecordEnd @Code='CONVERT'&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;--------------------&lt;br /&gt;-- Try using Cast --&lt;br /&gt;--------------------&lt;br /&gt;EXEC up_RecordStart @Code='CAST', @Description='SELECT CAST(CAST(GETDATE() AS VARCHAR(12)) AS DATETIME)'&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;DECLARE @i AS INTEGER&lt;br /&gt;DECLARE @floorDate AS DATETIME&lt;br /&gt;SET @i = 0&lt;br /&gt;WHILE @i &lt; 1000000&lt;br /&gt;BEGIN&lt;br /&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;SET @floorDate = CAST(CAST(GETDATE() AS VARCHAR(12)) AS DATETIME)&lt;br /&gt;&amp;nbsp;&amp;nbsp;SET @i = @i + 1&lt;br /&gt;END&lt;br /&gt;PRINT @floorDate&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;EXEC up_RecordEnd @Code='CAST'&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;-------------------------&lt;br /&gt;-- Now get the timings --&lt;br /&gt;-------------------------&lt;br /&gt;EXEC up_GetTimings 'FLR'&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;EXEC up_GetTimings 'CONVERT'&lt;br /&gt;GO&lt;br /&gt; &lt;br /&gt;EXEC up_GetTimings 'CAST'&lt;br /&gt;GO&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;This results in the following data being returned:&lt;br /&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Code&lt;/td&gt;&lt;td&gt;Description&lt;/td&gt;&lt;td&gt; TimeInMS&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tr&gt;&lt;td&gt;FLR&lt;/td&gt;&lt;td&gt;SELECT CONVERT(DATETIME,FLOOR(CONVERT(FLOAT,GETDATE())))&lt;/td&gt;&lt;td&gt;1313&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CONVERT&lt;/td&gt;&lt;td&gt;SELECT CONVERT(DATETIME,CONVERT(VARCHAR(12),GETDATE()))&lt;/td&gt;&lt;td&gt;3236&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CAST&lt;/td&gt;&lt;td&gt;SELECT CAST(CAST(GETDATE() AS VARCHAR(12)) AS DATETIME)&lt;/td&gt;&lt;td&gt;3203&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;which shows that the method using FLOOR &lt;strong&gt;is&lt;/strong&gt; more efficient, and that there isn't a lot to chose between CONVERT and CAST</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/tsql-timings.html' title='TSQL - Timings'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=7892202753718479043&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7892202753718479043'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7892202753718479043'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-2867936971844649927</id><published>2008-07-04T19:13:00.002+01:00</published><updated>2008-07-04T19:15:01.988+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='event'/><category scheme='http://www.blogger.com/atom/ns#' term='vbug'/><category scheme='http://www.blogger.com/atom/ns#' term='Brighton'/><title type='text'>VBUG Brighton: Understanding LINQ with Mike Taulty</title><content type='html'>&lt;div style="float: right; margin-left: 10px; margin-bottom: 10px;"&gt;&lt;a href="http://www.flickr.com/photos/janed/2636264041/" title="photo sharing"&gt;&lt;img src="http://farm4.static.flickr.com/3072/2636264041_e270e3e36f_m.jpg" alt="" style="border: solid 2px #000000;" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size: 0.9em; margin-top: 0px;"&gt;&lt;a href="http://www.flickr.com/photos/janed/2636264041/"&gt;Explaining LINQ syntax&lt;/a&gt;&lt;br /&gt;Originally uploaded by &lt;a href="http://www.flickr.com/people/janed/"&gt;Jane Dallaway&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;Last night &lt;a href="http://madgex.com/"&gt;Madgex&lt;/a&gt; hosted an excellent &lt;a href="http://www.vbug.co.uk/"&gt;VBUG&lt;/a&gt; Brighton session by &lt;a href="http://miketaulty.com"&gt;Mike Taulty&lt;/a&gt; on &lt;a href="http://en.wikipedia.org/wiki/Language_Integrated_Query"&gt;LINQ&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Despite the sunny, warm evening we managed to pack 25 or so Microsoft technologies developers into our boardroom and &lt;a href="http://www.flickr.com/photos/janed/2636262475/"&gt;listened intently&lt;/a&gt; whilst Mike talked and demo'd his way around LINQ, explaining some of the newer C#/VB9 language features as he went.  Whilst not being the exact same slide deck, after a rummage around Mike's site I found a &lt;a href="http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2008/03/20/10255.aspx"&gt;post&lt;/a&gt; about a similar sounding talk complete with presentation in &lt;a href="http://mtaulty.com/downloads/MTLaunchLINQ.pdf"&gt;PDF&lt;/a&gt; format.&lt;br /&gt;&lt;br /&gt;I remain slightly dissapointed by the syntax for Linq to XML &lt;br /&gt;&lt;span class="code"&gt;var query = from c in data.DescendantsAndSelf("customer")&lt;br /&gt; select (string)c.Attribute("id");&lt;/span&gt;&lt;br /&gt; which as Mike said, involves a bit too much of hoping and praying (relying on no underlying changes, no strong typing etc).&lt;br /&gt;&lt;br /&gt;However, I'm really encouraged by the idea of &lt;a href="http://blogs.msdn.com/xmlteam/archive/2008/02/21/linq-to-xsd-alpha-0-2.aspx"&gt;Linq to XSD&lt;/a&gt; which seems like a much better idea, tying the query to a schema rather than a document.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://weblogs.asp.net/fmarguerie/"&gt;Fabrice&lt;/a&gt; has &lt;a href="http://weblogs.asp.net/fmarguerie/archive/2007/01/15/linq-to-xsd-typed-xml-programming-with-linq.aspx"&gt;some sample code based on Linq to XML and Linq to XSD&lt;/a&gt; as follows, which goes to show the improvement using the XSD version&lt;br /&gt;&lt;br /&gt;Here is a LINQ to XML query:&lt;br /&gt;&lt;span class="code"&gt;from item in purchaseOrder.Elements("Item")&lt;br /&gt;select (double)item.Element("Price") * (int)item.Element("Quantity")&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here is the same query as above, but written using LINQ to XSD:&lt;br /&gt;&lt;span class="code"&gt;from item in purchaseOrder.Item&lt;br /&gt;select item.Price * item.Quantity&lt;/span&gt;&lt;br /&gt;which I think looks much more elegant and less clunky.&lt;br clear="all" /&gt;</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/vbug-brighton-understanding-linq-with.html' title='VBUG Brighton: Understanding LINQ with Mike Taulty'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=2867936971844649927&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/2867936971844649927'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/2867936971844649927'/><author><name>Jane</name><uri>http://www.blogger.com/profile/02966820600973863543</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-1859587806097406087</id><published>2008-07-04T18:08:00.004+01:00</published><updated>2008-07-05T22:07:38.181+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Monty Hall problem'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Monty Hall problem - TSQL</title><content type='html'>Following on from &lt;a href="http://jane.dallaway.com/blog/2008/07/monty-hall-problem-php.html"&gt;this morning's post about the Monty Hall problem&lt;/a&gt;, and proving it in PHP I figured I'd prove it in TSQL as well.&lt;br /&gt;&lt;br /&gt;So &lt;a href="http://jane.dallaway.com/downloads/SQL/MontyHall.sql"&gt;here&lt;/a&gt; is my SQL version.&lt;br /&gt;&lt;br /&gt;To maintain consistency with &lt;a href="http://jane.dallaway.com/downloads/PHP/MontyHall.php"&gt;my PHP version&lt;/a&gt;, I've made it output similar text, so the results are along the lines of:&lt;br /&gt;&lt;span class="code"&gt;Monty Hall Problem&lt;br /&gt;This is a simple TSQL query to prove the Monty Hall problem [http://en.wikipedia.org/wiki/Monty_hall_problem]&lt;br /&gt; &lt;br /&gt;------------------------&lt;br /&gt;The Results are in:&lt;br /&gt;------------------------&lt;br /&gt;Out of 10000 games, the contestant was right to swap 66.94% of the time and wrong 33.06% of the time&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The TSQL version is a bit more elegant with regards to working out which door to open for the contestant, as it is a simple statement of &lt;br /&gt;&lt;span class="code"&gt;SELECT TOP 1 @Opened = DoorNumber&lt;br /&gt;FROM @Doors&lt;br /&gt;WHERE DoorNumber NOT IN (@Prize, @Picked)&lt;br /&gt;ORDER BY NEWID()&lt;/span&gt;&lt;br /&gt;making the most of set theory to enable the exclusion of the @Prize door and the @Picked door as opposed to the same thing in my PHP code&lt;br /&gt;&lt;span class="code"&gt; $remaining = array();&lt;br /&gt;/* the gameshow host opens a door which has nothing behind it, so the gameshow host knows where the prize is&lt;br /&gt;but can't choose to open the door the contestant has chosen, so remove both picked and prize from the options, &lt;br /&gt;this leaves either one of two doors that can be opened, so pick one randomly */&lt;br /&gt; for ($i=0; $i&lt;3; $i ++)&lt;br /&gt; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;switch($doors[$i])&lt;br /&gt; &amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case $prize:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case $picked:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;default:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;array_push($remaining,$doors[$i]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}  &lt;br /&gt; }&lt;br /&gt; $opened = $remaining[array_rand($remaining)];&lt;/span&gt;&lt;br /&gt;which is all a bit more procedural and, at least to my mind, less elegant - but then again I like the syntax of SQL which either makes me a freak or a masochist (according to at least one colleague)</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/monty-hall-problem-tsql.html' title='Monty Hall problem - TSQL'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=1859587806097406087&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/1859587806097406087'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/1859587806097406087'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-2043995685766604903</id><published>2008-07-04T00:25:00.003+01:00</published><updated>2008-07-04T18:19:50.844+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Monty Hall problem'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Monty Hall problem - PHP</title><content type='html'>This evening, after returning from an excellent VBUG: Brighton (of which more in a later post), Richard was talking about the &lt;a href="http://en.wikipedia.org/wiki/Monty_hall_problem"&gt;Monty Hall problem&lt;/a&gt; after listening to a discussion about it on the BBC podcast &lt;a href="http://www.bbc.co.uk/radio4/history/inourtime/"&gt;In Our Time&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For those who don't know the Monty Hall problem it is this:&lt;ul&gt;&lt;li&gt;A gameshow set has 3 doors.&lt;/li&gt;&lt;li&gt;Behind one of the doors is a prize.&lt;/li&gt;&lt;li&gt;Behind the other 2 doors is nothing.&lt;/li&gt;&lt;li&gt;The contestant choses a door.&lt;/li&gt;&lt;li&gt;The gameshow hosts, knowing where the prize is, and which door the contestant has chosen, opens a door which he knows hasn't got the prize behind it and the contestant hasn't chosen.&lt;/li&gt;&lt;li&gt;The contestant is then offered the opportunity to trade their door for the one remaining door.&lt;/li&gt;&lt;/ul&gt;Should the contestant switch?  The answer is yes 2/3rds of the time.  See &lt;a href="http://en.wikipedia.org/wiki/Monty_hall_problem#Solution"&gt;here&lt;/a&gt; for the reasons.&lt;br /&gt;&lt;br /&gt;Richard and I both set about proving it in the tools we had available, I chose PHP, Richard chose Scala.  And we both can successfully demonstrate that by always switching doors the contestant is more likely to win.&lt;br /&gt;&lt;br /&gt;My PHP version is available for demo &lt;a href="http://jane.dallaway.com/downloads/PHP/MontyHall.php"&gt;here&lt;/a&gt; and downloadable via a zip file (right click, save as) &lt;a href="http://jane.dallaway.com/downloads/PHP/MontyHall.php.zip"&gt;here&lt;/a&gt;.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/07/monty-hall-problem-php.html' title='Monty Hall problem - PHP'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=2043995685766604903&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/2043995685766604903'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/2043995685766604903'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-7651933926091952551</id><published>2008-06-30T14:53:00.003+01:00</published><updated>2008-06-30T14:59:33.297+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer2005'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Find a string in a stored procedure - Revisited</title><content type='html'>A little over 2 years ago I &lt;a href="http://jane.dallaway.com/blog/2006/07/sql-server-2000-find-string-in-stored.html"&gt;posted&lt;/a&gt; a couple of methods of finding the instances of a string within stored procedures - at the time I'd left some debug 'Print @' code in at least one stored procedure and needed to find it before releasing the code.&lt;br /&gt; &lt;br /&gt;Today, I wanted to do the same thing, and as over the past 2 years I've been weaning myself off accessing the sysobjects tables, instead making use of the &lt;a href="http://msdn.microsoft.com/en-us/library/ms186778.aspx"&gt;INFORMATION_SCHEMA views&lt;/a&gt;, I decided it was time to add an update to that earlier post.&lt;br /&gt;&lt;br /&gt;So, using &lt;a href="http://msdn.microsoft.com/en-us/library/ms188757.aspx"&gt;INFORMATION_SCHEMA.ROUTINES&lt;/a&gt; it is coded as :&lt;br /&gt;&lt;span class="code"&gt;&amp;nbsp;&amp;nbsp;SELECT SPECIFIC_NAME&lt;br /&gt;&amp;nbsp;&amp;nbsp;FROM INFORMATION_SCHEMA.ROUTINES&lt;br /&gt;&amp;nbsp;&amp;nbsp;WHERE ROUTINE_DEFINITION LIKE '%PRINT @%'&lt;/span&gt;</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/06/find-string-in-stored-procedure.html' title='Find a string in a stored procedure - Revisited'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=7651933926091952551&amp;isPopup=true' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7651933926091952551'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/7651933926091952551'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-14400377.post-106862972536995119</id><published>2008-06-24T18:55:00.006+01:00</published><updated>2008-06-24T22:25:35.330+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='london'/><category scheme='http://www.blogger.com/atom/ns#' term='event'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer'/><title type='text'>SQL Server User Group Review</title><content type='html'>On Thursday myself and Dave (the Madgex DBA) headed off to the Microsoft offices in Victoria to attend the&lt;a href="http://www.sqlserverfaq.com/"&gt; SQL Server User Group&lt;/a&gt; meeting which was the launch event for SQL Server 2008.&lt;br /&gt;&lt;br /&gt; There were 2 key speakers (blurb taken from an email from SQL Server User Group):&lt;ul&gt;&lt;li&gt;Jasper Smith did the first stint on Admin stuff. He’s been an MVP for quite a while and has until recently been working at Nationwide. His blog can be found &lt;a href="http://sqlblogcasts.com/blogs/sqldbatips"&gt;here&lt;/a&gt;  and his website &lt;a href="http://www.sqldbatips.com"&gt;here&lt;/a&gt;. He’s got some great utilities like server manager for vista and Reporting Services scripter.&lt;/li&gt;&lt;li&gt;Simon Sabin did the last session on dev stuff. He is also an MVP and is a freelance consultant and works as part of SQL Know How and SQL Skills. His blog can be found &lt;a href="http://sqlblogcasts.com/blogs/SimonS"&gt;here&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;a href="http://siliconbea.ch/"&gt;Bruce&lt;/a&gt; posted a link to the Dev Team earlier this week which covers a lot of the same ground, &lt;a href="http://angryhacker.com/blog/archive/2008/06/20/10-reasons-why-sql-server-2008-is-going-to-rock.aspx"&gt;10 reasons why SQL Server 2008 is going to rock&lt;/a&gt;, and so I'm going to use this as a starter and just supplement it with the additional areas covered.&lt;ul&gt;&lt;li&gt;Database encryption – another enterprise only tool.  You can now encrypt a database against a user defined key (stored as a certificate on the filing system).  This will also result in tempdb being encrypted.  When encrypted the mdf and ldf files are also encrypted, as are backups.&lt;/li&gt;&lt;li&gt;Debugging has improved in the Management studio, can debug through a series of statements not just stored procedures.&lt;/li&gt;&lt;li&gt;Using Inline variable assignment you can use a rowset to insert multiple data items in one line – Insert into &lt;tablename&gt; values (1), (2), (3) – at the back end this gets translated into a union statement (see more &lt;a href="http://www.simple-talk.com/community/blogs/andras/archive/2008/02/28/44645.aspx"&gt;here&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;Merge is introduced (finally) – see &lt;a href="http://sqlblogcasts.com/blogs/simons/archive/tags/MERGE/default.aspx"&gt;here&lt;/a&gt; for some posts about it.  But the short version is the ability to do&lt;br /&gt;&lt;span class="code"&gt;Merge Into &lt;tablename&gt;&lt;br /&gt;Using &lt;key&gt;&lt;br /&gt;When matched then&lt;br /&gt;&amp;nbsp;&amp;nbsp;Update set&lt;br /&gt;When target not matched then&lt;br /&gt;&amp;nbsp;&amp;nbsp;Insert&lt;/span&gt;&lt;br /&gt;Basically doing an insert and update in one statement.  Merge is deterministic, and appears to be quicker but mileage may vary.&lt;/li&gt;&lt;li&gt;CLR now handles larger data types (&gt; 8000 bytes)&lt;/li&gt;&lt;/ul&gt;Another good evening, this time complete with pizza and beer.&lt;br /&gt;&lt;em&gt;Update&lt;/em&gt;: &lt;a href="http://sqlblogcasts.com/blogs/simons/"&gt;Simon&lt;/a&gt; has posted a &lt;a href="http://sqlblogcasts.com/blogs/simons/archive/2008/06/19/Usergroup-meeting-follow-up-post.aspx"&gt;follow-up containing the answers to questions he didn't get time to answer&lt;/a&gt;.</content><link rel='alternate' type='text/html' href='http://jane.dallaway.com/blog/2008/06/sql-server-user-group-review.html' title='SQL Server User Group Review'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14400377&amp;postID=106862972536995119&amp;isPopup=true' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://jane.dallaway.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/106862972536995119'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14400377/posts/default/106862972536995119'/><author><name>Jane</name><uri>http://www.blogger.com/profile/10837649671422240948</uri><email>noreply@blogger.com</email></author></entry></feed>