<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>SQL</title>
        <link>http://blogs.interakting.co.uk/danmatthews/category/17.aspx</link>
        <description>SQL Server and T-SQL</description>
        <language>en-GB</language>
        <copyright>Dan Matthews</copyright>
        <managingEditor>dmatthews@businessdecision.co.uk</managingEditor>
        <generator>Subtext Version 1.9.5.177</generator>
        <item>
            <title>SQL Reporting Services Access Denied Error</title>
            <link>http://blogs.interakting.co.uk/danmatthews/archive/2008/02/28/SQL-Reporting-Services-Access-Denied-Error.aspx</link>
            <description>&lt;p&gt;When trying to access your Report Manager, you might see an Access Denied error with an entry in your application log like:&lt;/p&gt;
&lt;font face="Courier New" color="#3366ff" size="3"&gt;
&lt;p&gt;Failed to execute request because the App-Domain could not be created. Error: 0x80070005 Access is denied. &lt;/p&gt;
&lt;/font&gt;
&lt;p&gt;Thankfully it's a simple one to fix. It's caused because ASP.NET cannot access the Reporting Services folders. I'm not sure what the minimum access permissions are but this was on dev so I simply gave the Network Service full control on my 'ReportManager' and 'ReportServer' folders and that sorted things.&lt;/p&gt;&lt;img src="http://blogs.interakting.co.uk/danmatthews/aggbug/213.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dan Matthews</dc:creator>
            <guid>http://blogs.interakting.co.uk/danmatthews/archive/2008/02/28/SQL-Reporting-Services-Access-Denied-Error.aspx</guid>
            <pubDate>Thu, 28 Feb 2008 11:47:54 GMT</pubDate>
            <wfw:comment>http://blogs.interakting.co.uk/danmatthews/comments/213.aspx</wfw:comment>
            <comments>http://blogs.interakting.co.uk/danmatthews/archive/2008/02/28/SQL-Reporting-Services-Access-Denied-Error.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.interakting.co.uk/danmatthews/comments/commentRss/213.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Emptying the Deleted Items in MCMS</title>
            <link>http://blogs.interakting.co.uk/danmatthews/archive/2007/11/16/115.aspx</link>
            <description>&lt;p&gt;So you might have an MCMS site that has been kicking around for a few years... lots of content has been deleted. Site is getting sluggish. You go into Site Manager to empty the deleted items and everything you click takes 1 minute to respond. Emptying the deleted items is useless - it just crashes. The reason for this, I think, is that it retrieves XML from MCMS and when trying to manipulate the huge XML files in the XML DOM it just falls over. The DOM does have a maximum logical size of XML it can manipulate before performance degrades quickly.&lt;/p&gt;
&lt;p&gt;What to do?&lt;/p&gt;
&lt;p&gt;Well it's a nasty one as there is no API call to empty the deleted items. Microsoft - who I've logged support calls with about this - have taken over a year to come back with nothing. Basically, you're on your own.&lt;/p&gt;
&lt;p&gt;Now the MCMS schema is a bit nasty and &lt;font color="#ff0000"&gt;if you do anything to the MCMS database directly you may invalidate your Microsoft support&lt;/font&gt;. Having said that, it's the only way I could find to empty the deleted items without Site Manager.&lt;/p&gt;
&lt;p&gt;Here is the script I wrote and used. Use it ENTIRELY at your own risk. I suggest you figure out what it's doing before you run it. Good luck!&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- properties (historical)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting properties (historical)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodeProperty where NodeId in&lt;br /&gt;
(select Id from Node where ArchiveSourceGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- resources (historical)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting resources (historical)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodeResource where NodeId in&lt;br /&gt;
(select Id from Node where ArchiveSourceGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- roles (historical)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting roles (historical)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodeRole where NodeId in&lt;br /&gt;
(select Id from Node where ArchiveSourceGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- placeholder content (historical)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting placeholder content (historical)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodePlaceholderContent where NodeId in&lt;br /&gt;
(select Id from Node where ArchiveSourceGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- placeholders (historical)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting placeholders (historical)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodePlaceholder where NodeId in&lt;br /&gt;
(select Id from Node where ArchiveSourceGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- postings (historical)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting postings (historical)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from Node where ArchiveSourceGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- properties (current)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting properties (current)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodeProperty where NodeId in&lt;br /&gt;
(select Id from Node where NodeGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- resources (current)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting resources (current)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodeResource where NodeId in&lt;br /&gt;
(select Id from Node where NodeGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- roles (current)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting roles (current)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodeRole where NodeId in&lt;br /&gt;
(select Id from Node where NodeGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- placeholder content (current)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting placeholder content (current)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodePlaceholderContent where NodeId in&lt;br /&gt;
(select Id from Node where NodeGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- placeholders (current)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting placeholders (current)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodePlaceholder where NodeId in&lt;br /&gt;
(select Id from Node where NodeGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16))&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- postings (current)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting postings (current)'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from Node where NodeGUID in&lt;br /&gt;
(select FollowGUID from Node where DeletedWhen is not null and Type=16)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- pages&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting pages'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from Node where DeletedWhen is not null and Type=16&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- channel properties&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting channel properties'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodeProperty where NodeId in&lt;br /&gt;
(select Id from Node where DeletedWhen is not null and Type=4)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- channel roles&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting channel roles'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from NodeRole where NodeId in&lt;br /&gt;
(select Id from Node where DeletedWhen is not null and Type=4)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;-- channels&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print 'deleting channels'&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;delete from Node where DeletedWhen is not null and Type=4&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;&lt;/font&gt; &lt;font face="Arial"&gt;&lt;br /&gt;
&lt;/font&gt; &lt;/p&gt;&lt;img src="http://blogs.interakting.co.uk/danmatthews/aggbug/115.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dan Matthews</dc:creator>
            <guid>http://blogs.interakting.co.uk/danmatthews/archive/2007/11/16/115.aspx</guid>
            <pubDate>Fri, 16 Nov 2007 16:15:49 GMT</pubDate>
            <wfw:comment>http://blogs.interakting.co.uk/danmatthews/comments/115.aspx</wfw:comment>
            <comments>http://blogs.interakting.co.uk/danmatthews/archive/2007/11/16/115.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.interakting.co.uk/danmatthews/comments/commentRss/115.aspx</wfw:commentRss>
        </item>
        <item>
            <title>EPiServer 4.6.x and SQL Server Express 2005</title>
            <link>http://blogs.interakting.co.uk/danmatthews/archive/2007/06/06/64.aspx</link>
            <description>&lt;p&gt;...and straight off I have something to share with you :)&lt;/p&gt;
&lt;p&gt;If you're installing using EPiServer manager and using SQL Server 2005 Express, you need to be aware of the following, otherwise you get all sorts of 'SQL Server unavailable' and user authentication errors:&lt;/p&gt;
&lt;p&gt;1) Make sure you NAME THE INSTANCE in the install, otherwise it will install on your default instance (which in my case, stupidly, was an MSDE instance!)&lt;/p&gt;
&lt;p&gt;2) Make sure you have both Shared Memory and TCP/IP turned on in your SQL2005 Configuration. The installation uses Shared Memory, but the site itself uses TCP/IP.&lt;/p&gt;
&lt;p&gt;3) Make sure mixed-mode authentication is turned on for your SQL Express instance&lt;/p&gt;
&lt;p&gt;4) Make sure that your website is running as ASP.NET1.1 during the install (do the .NET2 templates and stuff afterwards)&lt;/p&gt;&lt;img src="http://blogs.interakting.co.uk/danmatthews/aggbug/64.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dan Matthews</dc:creator>
            <guid>http://blogs.interakting.co.uk/danmatthews/archive/2007/06/06/64.aspx</guid>
            <pubDate>Wed, 06 Jun 2007 10:46:52 GMT</pubDate>
            <wfw:comment>http://blogs.interakting.co.uk/danmatthews/comments/64.aspx</wfw:comment>
            <comments>http://blogs.interakting.co.uk/danmatthews/archive/2007/06/06/64.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://blogs.interakting.co.uk/danmatthews/comments/commentRss/64.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Avoiding SQL Cursors</title>
            <link>http://blogs.interakting.co.uk/danmatthews/archive/2007/05/11/56.aspx</link>
            <description>&lt;p&gt;One of my pet hates is SQL Server cursors. Yes, I know that they are part of the product for a reason, and I know that they are very handy. So why do I hate them, and what can be done to avoid them? Are they a necessary evil? I developed a technique to avoid them a few years ago, and some others people have since designed some techniques as well, such as on &lt;a href="http://www.sql-server-performance.com/dp_no_cursors.asp"&gt;this post.&lt;/a&gt; These other techniques are great, but my style of doing things has the one advantage that it can be repeated and nested in complex ways because of the use of variable tables... as you'll see below.&lt;/p&gt;
&lt;p&gt;Well, lets first create some test data. I used a server with both SQL2000 and SQL2005 server on to do some tests just to show this technique works on both. I created an empty database with an empty table, and then added 1,000 random numbers between the value of 1-100 into the table. The script I used to create the table is below:&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;CREATE TABLE [dbo].[test] (&lt;br /&gt;
 [testid] [int] IDENTITY (1, 1) NOT NULL ,&lt;br /&gt;
 [randnum] [int] NOT NULL &lt;br /&gt;
) ON [PRIMARY]&lt;br /&gt;
GO&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;&lt;font face="Courier New" color="#3366ff"&gt;ALTER TABLE [dbo].[test] WITH NOCHECK ADD &lt;br /&gt;
 CONSTRAINT [PK_test] PRIMARY KEY  CLUSTERED &lt;br /&gt;
 (&lt;br /&gt;
  [testid]&lt;br /&gt;
 )  ON [PRIMARY] &lt;br /&gt;
GO&lt;/font&gt;&lt;br /&gt;
&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;I then constructed two very simple queries which took a number as a parameter and returned the number of columns matching that number. Yes, I know that I could have done a single SELECT to get that, but that's not the point of the exercise :) Where this technique IS essential is where you find yourself doing complex nested group-bys and start to think about pivots and the like (and especially where there's no reliable numeric ID column) Basically, where row-by-row operations are needed.&lt;/p&gt;
&lt;p&gt;Firstly, the cursor script I used looked like this:&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;DECLARE @iCheckNumber int,&lt;br /&gt;
 @iCurrentNumber int,&lt;br /&gt;
 @iNumberCount int&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;SELECT @iCheckNumber = 35,&lt;br /&gt;
 @iNumberCount = 0&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;DECLARE testnum CURSOR FOR&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;SELECT randnum FROM test&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;OPEN testnum&lt;br /&gt;
 &lt;br /&gt;
FETCH testnum INTO @iCurrentNumber&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;WHILE @@FETCH_STATUS = 0&lt;br /&gt;
   BEGIN&lt;br /&gt;
 FETCH testnum INTO @iCurrentNumber&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt; IF @iCurrentNumber = @iCheckNumber&lt;br /&gt;
 BEGIN&lt;br /&gt;
  SELECT @iNumberCount = @iNumberCount + 1&lt;br /&gt;
 END&lt;br /&gt;
   END&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;CLOSE testnum&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;DEALLOCATE testnum&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;SELECT @iCurrentNumber&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;&lt;font color="#000000"&gt;&lt;/font&gt;As you can see, it's a pretty standard cursor script. I ran it from Query Analyzer (2000) or Management Studio (2005) and it ran fine and returned the results I wanted. So how do I do the same kind of thing without cursors and without creating umpteen temporary tables? Well, I can use the T-SQL support for loops, and variable tables, as below:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;DECLARE @iCheckNumber int,&lt;br /&gt;
  @iCurrentNumber int,&lt;br /&gt;
  @iNumberCount int,&lt;br /&gt;
  @iCurrentRow int&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;SELECT @iCheckNumber = 35,&lt;br /&gt;
  @iNumberCount = 0&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;DECLARE @temptest AS TABLE&lt;br /&gt;
 (&lt;br /&gt;
  iRowID int identity(1,1),&lt;br /&gt;
  randnum int &lt;br /&gt;
 )&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;INSERT INTO @temptest (randnum)&lt;br /&gt;
SELECT randnum FROM test&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;SELECT @iCurrentRow = 1&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;WHILE @iCurrentRow &amp;lt; (SELECT COUNT(*) FROM test)&lt;br /&gt;
BEGIN&lt;br /&gt;
 IF (SELECT randnum FROM @temptest WHERE iRowID = @iCurrentRow) = @iCheckNumber&lt;br /&gt;
 BEGIN&lt;br /&gt;
  SELECT @iNumberCount = @iNumberCount + 1&lt;br /&gt;
 END&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt; SELECT @iCurrentRow = @iCurrentRow + 1&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#3366ff"&gt;print @iCurrentRow&lt;br /&gt;
END&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;Note that I'm creating an identity column on my temporary table. This makes it ideal for writing queries where your data has no SEQUENTIAL numeric identity column already. One common use for this is with paging. Imagine you have a table with an ID and Name column. You want to pull back your names 500 at a time. You can get the first 500 no problem, just do a SELECT TOP 500. When you come to get 500-1000 you have problems - and that's where a cursor is normally used.&lt;/p&gt;
&lt;p&gt;With my technique, you can put all your unique IDs in the temporary table, and then just iterate through the row numbers you want.&lt;/p&gt;
&lt;p&gt;Note that the example I used here probably performs better as a cursor because it is so simple, but that's not a true reflection of cursors. They perform badly, even in 2005, and the more complex they get then, in my experience, the more useful it is to avoid &lt;font face="Arial"&gt;&lt;br /&gt;
&lt;/font&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;&lt;img src="http://blogs.interakting.co.uk/danmatthews/aggbug/56.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dan Matthews</dc:creator>
            <guid>http://blogs.interakting.co.uk/danmatthews/archive/2007/05/11/56.aspx</guid>
            <pubDate>Fri, 11 May 2007 10:53:08 GMT</pubDate>
            <wfw:comment>http://blogs.interakting.co.uk/danmatthews/comments/56.aspx</wfw:comment>
            <comments>http://blogs.interakting.co.uk/danmatthews/archive/2007/05/11/56.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://blogs.interakting.co.uk/danmatthews/comments/commentRss/56.aspx</wfw:commentRss>
        </item>
        <item>
            <title>ReturnValue from a TableAdapter (ASP.NET2)</title>
            <link>http://blogs.interakting.co.uk/danmatthews/archive/2007/05/03/53.aspx</link>
            <description>&lt;p&gt;We've been using TableAdapters created using the XSD designer in VS2005 lately, and have found they have a couple of gotchas.&lt;/p&gt;
&lt;p&gt;For example, one limitation of a TableAdapter is the lack of support for SQL stored proc return values when using a non-scalar stored proc call - or at least, lack of any useful access to them. You'd expect that maybe the return value would be the return from the auto-genned function, but you'd be wrong. The return is actually the number of rows affected, and that in itself is useless if you're turning row count off in the stored proc (as many do).&lt;/p&gt;
&lt;p&gt;So maybe the ReturnValue is a property on the TableAdapter? Again, no... you can use 'out' parameters fine (just pass the variable as ref to the auto-genned function), but thats not very nice. I don't want to throw away the return value concept and use 'out' params just because the TableAdapter isn't clever enough.&lt;/p&gt;
&lt;p&gt;So, what to do? There's some good blog posts around on the technique, but most of them only work really well when you have just one or two stored procs and table adapters (see this &lt;a href="http://blogs.msdn.com/youngjoo/archive/2006/07/25/678320.aspx"&gt;blog post&lt;/a&gt;&lt;font face="Arial"&gt; for an example&lt;/font&gt;. In the solutions we're building, we often have lots. And lots. It's a heady mixture of designer work and cut-paste fest. Ugly maybe, but it does allow flexibility. This meant we had to make some kind of vaguely generic function to get the ReturnValue available to our UI code layer.&lt;/p&gt;
&lt;p&gt;To achieve this, we made use of the TableAdapter partial classes that the XSD creates for us. Let's say that we have a set of stored procs to do with orders. You might have:&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#0000ff"&gt;AddOrder&lt;br /&gt;
UpdateOrder&lt;br /&gt;
DeleteOrder&lt;br /&gt;
GetOrder&lt;br /&gt;
GetOrders&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;You'd probably only be bothered with the return value from the stored procs that actually wrote date - the top 3. Lets say they are returning an error code, like 0 for success, 1 if they can't find the specified record, 2 if it will break a constraint etc. What you can then do is create your TableAdapter queries for each function - lets assume you called them all the same thing as their underlying stored procs. Let's say our TableAdapter is called OrderTableAdapter.&lt;/p&gt;
&lt;p&gt;We then need to do our clever jiggery pokery. Just outside our partial class for OrderTableAdapter, we put an enumeration, like so:&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;    &lt;font color="#0000ff"&gt;public enum&lt;/font&gt; &lt;font color="#3366ff"&gt;Order&lt;/font&gt;&lt;font color="#3366ff"&gt;CommandType&lt;/font&gt;&lt;br /&gt;
    {&lt;br /&gt;
        Add,&lt;br /&gt;
        Update,&lt;br /&gt;
        Delete,&lt;br /&gt;
        Get,&lt;br /&gt;
        GetAll&lt;br /&gt;
    }&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;We do this so that it's a cleaner solution in our data layer, where we'll have a manager class (I hate lobbing strings around where an enum would be better). Following this enum declaration, we have the main worker function which is a method called 'ReturnValue' which takes one parameter - the OrderCommandType. For simplicity, I've written comments inline in the method below.&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;  &lt;font color="#0000ff"&gt;partial class&lt;/font&gt; &lt;font color="#3366ff"&gt;OrderTableAdapter&lt;/font&gt;&lt;br /&gt;
    {&lt;br /&gt;
        &lt;font color="#0000ff"&gt;public int &lt;/font&gt;ReturnValue(&lt;font color="#3366ff"&gt;OrderCommandType&lt;/font&gt; Command)&lt;br /&gt;
        {&lt;br /&gt;
&lt;font color="#0000ff"&gt;            &lt;font color="#339966"&gt;// get the return value for the appropriate command&lt;/font&gt;&lt;br /&gt;
            switch&lt;/font&gt; (Command)&lt;br /&gt;
            {&lt;br /&gt;
                &lt;font color="#0000ff"&gt;case&lt;/font&gt; &lt;font color="#3366ff"&gt;OrderCommandType&lt;/font&gt;.Add:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#339966"&gt;                    // the first param is the SQL command collection of the TableAdapter, the second param is the stored proc name that we want the return value from&lt;/font&gt;&lt;br /&gt;
                    &lt;font color="#0000ff"&gt;return&lt;/font&gt; &lt;font color="#3366ff"&gt;TableAdapterHelper&lt;/font&gt;.GetReturnValue(&lt;font color="#0000ff"&gt;this&lt;/font&gt;.CommandCollection, &lt;font color="#800000"&gt;"AddOrder"&lt;/font&gt;);&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;                &lt;font color="#0000ff"&gt;case&lt;/font&gt; OrderCommandType.Update:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;                    &lt;font color="#0000ff"&gt;return&lt;/font&gt; &lt;font color="#3366ff"&gt;TableAdapterHelper&lt;/font&gt;.GetReturnValue(&lt;font color="#0000ff"&gt;this&lt;/font&gt;.CommandCollection, &lt;font color="#800000"&gt;"UpdateOrder"&lt;/font&gt;);&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;                &lt;font color="#0000ff"&gt;case&lt;/font&gt; OrderCommandType.Delete:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;                    &lt;font color="#0000ff"&gt;return&lt;/font&gt; &lt;font color="#3366ff"&gt;TableAdapterHelper&lt;/font&gt;.GetReturnValue(&lt;font color="#0000ff"&gt;this&lt;/font&gt;.CommandCollection, &lt;font color="#800000"&gt;"DeleteOrder"&lt;/font&gt;);&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;                &lt;font color="#0000ff"&gt;default&lt;/font&gt;:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;&lt;font face="Courier New"&gt;&lt;font color="#339966"&gt;                    // for cleanliness, throw an error if someone tries to get the ReturnValue from one of the method types that we have decided shouldn't return a value&lt;/font&gt;&lt;br /&gt;
                    &lt;font color="#0000ff"&gt;throw new&lt;/font&gt; &lt;font color="#3366ff"&gt;Exception&lt;/font&gt;(&lt;font color="#800000"&gt;"OrderCommandType requested does not support return values"&lt;/font&gt;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;/font&gt;&lt;br /&gt;
&lt;/font&gt;&lt;font face="Arial"&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;You'll see I refer to a TableAdapterHelper - this is important as it's the bit that actually digs out the ReturnValue itself...&lt;/font&gt;&lt;/p&gt;
&lt;font face="Arial"&gt;&lt;font face="Arial"&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;    public static class&lt;/font&gt; &lt;font color="#3366ff"&gt;TableAdapterHelper&lt;/font&gt;&lt;br /&gt;
    {&lt;br /&gt;
&lt;font color="#0000ff"&gt;        public static int&lt;/font&gt; GetReturnValue(&lt;font color="#3366ff"&gt;SqlCommand&lt;/font&gt;[] aCommands, &lt;font color="#0000ff"&gt;string&lt;/font&gt; sCommandText)&lt;br /&gt;
        {&lt;br /&gt;
            &lt;font color="#339966"&gt;// now lets find that commmand&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;            &lt;font color="#339966"&gt;// iterate through all command objects in the command collection&lt;/font&gt;&lt;br /&gt;
            &lt;font color="#0000ff"&gt;foreach&lt;/font&gt; (&lt;font color="#3366ff"&gt;SqlCommand&lt;/font&gt; oCommand &lt;font color="#0000ff"&gt;in&lt;/font&gt; aCommands)&lt;br /&gt;
            {&lt;br /&gt;
                &lt;font color="#339966"&gt;// see if the stored proc name matches&lt;/font&gt;&lt;br /&gt;
                &lt;font color="#0000ff"&gt;if&lt;/font&gt; (oCommand.CommandText.Equals(&lt;font color="#0000ff"&gt;string&lt;/font&gt;.Format("&lt;font color="#800000"&gt;dbo.{0}&lt;/font&gt;", sCommandText)))&lt;br /&gt;
                {&lt;br /&gt;
                    &lt;font color="#0000ff"&gt;if&lt;/font&gt; (oCommand.Parameters != &lt;font color="#0000ff"&gt;null&lt;/font&gt;)&lt;br /&gt;
                    {&lt;br /&gt;
                       &lt;font color="#339966"&gt; // loop thru all params&lt;/font&gt;&lt;br /&gt;
                        &lt;font color="#0000ff"&gt;foreach&lt;/font&gt; (&lt;font color="#3366ff"&gt;SqlParameter&lt;/font&gt; oParam &lt;font color="#0000ff"&gt;in&lt;/font&gt; oCommand.Parameters)&lt;br /&gt;
                        {&lt;br /&gt;
                            &lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font color="#339966"&gt;// find the param with a direction of ReturnValue (should only ever be one)&lt;br /&gt;
&lt;/font&gt;                            &lt;font color="#0000ff"&gt;if&lt;/font&gt; (oParam.Direction == System.Data.&lt;font color="#3366ff"&gt;ParameterDirection&lt;/font&gt;.ReturnValue)&lt;br /&gt;
                            {&lt;br /&gt;
                                &lt;font color="#0000ff"&gt;if&lt;/font&gt; (oParam.Value == &lt;font color="#3366ff"&gt;DBNull&lt;/font&gt;.Value || oParam.Value == &lt;font color="#0000ff"&gt;null&lt;/font&gt;) &lt;font color="#0000ff"&gt;return&lt;/font&gt; 0; &lt;font color="#339966"&gt;// assume dbnull is success&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;                                &lt;font color="#0000ff"&gt;return&lt;/font&gt; (&lt;font color="#0000ff"&gt;int&lt;/font&gt;)oParam.Value;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;      &lt;font color="#0000ff"&gt;      throw new&lt;/font&gt; &lt;font color="#3366ff"&gt;Exception&lt;/font&gt;(&lt;font color="#800000"&gt;"Could not locate Return Value"&lt;/font&gt;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;/font&gt;&lt;/p&gt;
&lt;/font&gt;
&lt;p&gt;As you'll see, my technique works on the basis that the stored proc names are known and unique across commands in a single TableAdapter, which should normally be the case. You might find it ugly that the stored proc names are embedded in code - but they're in the XSD anyway so it's still containing in one place.&lt;/p&gt;
&lt;p&gt;So the final part of the jigsaw is the actual call itself, and the getting of the ReturnValue. Here is an example of how to make the call. Note that I've made OrderID an out param. I like to throw an error if the SQL error code is non-zero.&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;&lt;font color="#3366ff"&gt;OrderTableAdapter&lt;/font&gt; &lt;font color="#000000"&gt;ta =&lt;/font&gt; new &lt;font color="#3366ff"&gt;OrderTableAdapter&lt;/font&gt;&lt;font color="#000000"&gt;()&lt;/font&gt;;&lt;br /&gt;
&lt;br /&gt;
int&lt;/font&gt; iRowsAffected = ta.AddOrder(&lt;font color="#0000ff"&gt;ref&lt;/font&gt; OrderID, OrderCode, CustomerID, OrderLineID);&lt;br /&gt;
&lt;br /&gt;
&lt;font color="#0000ff"&gt;int&lt;/font&gt; iErrorCode = ta.ReturnValue(&lt;font color="#3366ff"&gt;OrderCommandType&lt;/font&gt;.Add);&lt;br /&gt;
&lt;br /&gt;
&lt;font color="#0000ff"&gt;if&lt;/font&gt;(iErrorCode != 0)&lt;br /&gt;
{&lt;br /&gt;
 &lt;font color="#339966"&gt; // throw an exception here&lt;br /&gt;
&lt;/font&gt;}&lt;/font&gt;&lt;font face="Arial"&gt;&lt;br /&gt;
&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;You can also combine this technique with 'out' param error messages and the like to make a very powerful below-data layer in the stored procs.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;/font&gt;&lt;img src="http://blogs.interakting.co.uk/danmatthews/aggbug/53.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dan Matthews</dc:creator>
            <guid>http://blogs.interakting.co.uk/danmatthews/archive/2007/05/03/53.aspx</guid>
            <pubDate>Thu, 03 May 2007 08:42:19 GMT</pubDate>
            <wfw:comment>http://blogs.interakting.co.uk/danmatthews/comments/53.aspx</wfw:comment>
            <comments>http://blogs.interakting.co.uk/danmatthews/archive/2007/05/03/53.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://blogs.interakting.co.uk/danmatthews/comments/commentRss/53.aspx</wfw:commentRss>
        </item>
        <item>
            <title>SQL Express 2005 installation</title>
            <link>http://blogs.interakting.co.uk/danmatthews/archive/2007/04/26/42.aspx</link>
            <description>&lt;p&gt;When installing SQL Server Express 2005, you may get an error saying something like:&lt;/p&gt;
&lt;p&gt;&lt;font color="#ff0000"&gt;An installation package for the product Microsoft SQL Server Native Client cannot be found. Try the installation again using a valid copy of the installation package 'sqlncli.msi'.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;Irritatingly, it only says this after the installation file has already extracted, installed support files, checked the system, checked pre-reqs, initialised main installation, prompted you for config and installed a bunch of things (which it then rolls back). It's 5-10 mins until you get the error and there's no 'retry'.&lt;/p&gt;
&lt;p&gt;Anyway, at least it's easy to fix :) I read a couple of articles on &lt;a href="http://support.microsoft.com/kb/933833"&gt;Microsoft&lt;/a&gt; and &lt;a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=152548&amp;amp;SiteID=1"&gt;MSDN Forums&lt;/a&gt; which weren't THAT helpful. They seem to contain too much info. I found that I could fix the error by doing this (note - I was installing on a box which already had full SQL2000 installed... it might well not work if you are installing 'clean')&lt;/p&gt;
&lt;p&gt;Extract the SQL Express MSI to a temp folder. (SQLEXPR32.EXE /X:TEMPFOLDER). Then find the "SQLNCLI.MSI" file that it extracted into the 'Setup' folder. Run it, and when prompted, choose to Repair the installation. Now re-run the SQL Express installation, and it should work fine!&lt;/p&gt;&lt;img src="http://blogs.interakting.co.uk/danmatthews/aggbug/42.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dan Matthews</dc:creator>
            <guid>http://blogs.interakting.co.uk/danmatthews/archive/2007/04/26/42.aspx</guid>
            <pubDate>Thu, 26 Apr 2007 09:33:03 GMT</pubDate>
            <wfw:comment>http://blogs.interakting.co.uk/danmatthews/comments/42.aspx</wfw:comment>
            <comments>http://blogs.interakting.co.uk/danmatthews/archive/2007/04/26/42.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://blogs.interakting.co.uk/danmatthews/comments/commentRss/42.aspx</wfw:commentRss>
        </item>
    </channel>
</rss>