<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1345741839987072737</id><updated>2012-02-09T23:59:09.724-08:00</updated><title type='text'>Databases Are Fun</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>19</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-486435458804128317</id><published>2011-11-16T03:58:00.001-08:00</published><updated>2011-11-16T04:08:34.721-08:00</updated><title type='text'>Quickly set Identity_Insert off for all tables in a database</title><content type='html'>&lt;br /&gt;In some situations, you may be turning the identity_insert property on and off for tables - for example, when doing an initial setup of data. &amp;nbsp;Since only one table can have this turned on at a time (per session), here's a script to turn the identity_insert property off on all tables, so that you can turn it on for the table that you need.&lt;br /&gt;&lt;br /&gt;This only works because SQL Server won't fail if the identity_insert is not turned on.  However, it will fail when the table does not have an identity property set (this is why you can't just use sp_msforeachtable). &lt;br /&gt;&lt;br /&gt;Please note that this is a quick script - use with caution and test first! &amp;nbsp;A better way to do this would probably be just to turn the identity_insert off right after you use it.&lt;br /&gt;&lt;br /&gt;&lt;div class="mycode"&gt;&lt;pre&gt;Declare&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; @NewLine char(1)&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; ,@SQLStatement nvarchar(max)&lt;br /&gt;&lt;br /&gt;Set @NewLine = char(13) + char(10)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-- procedures&lt;br /&gt;&lt;br /&gt;select @SQLStatement = isnull( @SQLStatement + @NewLine, '' ) +&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; 'set identity_insert ' + object_name(object_id) + ' off'&lt;br /&gt;&lt;br /&gt;from SYS.IDENTITY_COLUMNS&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-- exec sp_executesql @SQLStatement&lt;br /&gt;&lt;br /&gt;Print @SQLStatement&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-486435458804128317?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/486435458804128317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2011/11/quickly-set-identityinsert-off-for-all.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/486435458804128317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/486435458804128317'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2011/11/quickly-set-identityinsert-off-for-all.html' title='Quickly set Identity_Insert off for all tables in a database'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-6917766068046541678</id><published>2011-06-01T14:05:00.000-07:00</published><updated>2011-11-16T04:05:10.441-08:00</updated><title type='text'>Masking PII (Personally Identifiable Information)</title><content type='html'>Privacy has been an increasing concern in information technology, and many companies have policies in place that prevent data that could identify a person from being easily accessed. This data is called PII (Personally Identifiable Information). Laws like HIPAA (Health Insurance Portability and Accountability Act) in particular are restrictive of how PII data can be accessed&lt;br /&gt;&lt;br /&gt;If these laws are strictly interpreted, it can cause concerns even with staightforward database tasks, such as making a copy of a production database available for development work. Some companies now forbid this common and useful practice.&lt;br /&gt;&lt;br /&gt;Using a script such as ##Common_PIIInfoDelete can diminish these concerns. This script will update all fields identified as PII to a neutral value (such as an empty string).&lt;br /&gt;&lt;br /&gt;I've included a sample that shows how to call ##Common_PIIInfoDelete. Before you run it, please note the following:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;This script must never be run in any production database. There's a validation in the script that checks the server name.  This should be configured for your environment.&lt;/li&gt;&lt;li&gt;You'll need to update the values in the ##Settings table to match the fields that you want to mask&lt;/li&gt;&lt;li&gt;The script currently runs all the updates in one chunk. It could be improved by using the primary key of the table to run in chunks.&lt;/li&gt;&lt;li&gt;This script is set up as a temporary stored procedure, which works best in my environment.  It could easily be made a regular stored procedure, or a regular sql script without stored procedure.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="mycode"&gt;&lt;pre&gt;Create Procedure ##Common_PIIInfoDelete&lt;br /&gt;as&lt;br /&gt;/*&lt;br /&gt;*********************************************************************&lt;br /&gt;Description:&lt;br /&gt;&lt;br /&gt;Mask PII info (Personally Identifiable Information).&lt;br /&gt;This temporary stored procedure expects a temp table (##Settings) to exist which&lt;br /&gt;contains the fields that should be masked.&lt;br /&gt;&lt;br /&gt;*********************************************************************&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;set nocount on&lt;br /&gt;&lt;br /&gt;-- Declarations -----------------------------------------------------------&lt;br /&gt;declare&lt;br /&gt;    @RC_FAILURE int&lt;br /&gt;    ,@RC_SUCCESS int&lt;br /&gt;    ,@ExitCode int&lt;br /&gt;    ,@ProcedureName sysname&lt;br /&gt;    ,@RC int -- Return code from called SP&lt;br /&gt;    ,@RowCount int&lt;br /&gt;    ,@ERRUNEXPECTED int&lt;br /&gt;    ,@RaiseMessage nvarchar(4000)&lt;br /&gt;    ,@ErrorNumber int&lt;br /&gt;    ,@ErrorSeverity int&lt;br /&gt;    ,@ErrorState int&lt;br /&gt;    ,@ErrorLine int&lt;br /&gt;    ,@UpdateSQL nvarchar(4000)&lt;br /&gt;    ,@SetSQL nvarchar(4000)&lt;br /&gt;    ,@Table_Schema sysname&lt;br /&gt;    ,@Table_Name sysname&lt;br /&gt;    ,@MaskValue varchar(10)&lt;br /&gt;    ,@NewLine char(2)&lt;br /&gt;&lt;br /&gt;-- Initializations -----------------------------------------------------------&lt;br /&gt;select&lt;br /&gt;    @RC_FAILURE = -100&lt;br /&gt;    ,@RC_SUCCESS = 0&lt;br /&gt;    ,@RC = 0&lt;br /&gt;    ,@ExitCode = @RC_SUCCESS&lt;br /&gt;    ,@ProcedureName = '##Common_PIIInfoDelete'&lt;br /&gt;    ,@ErrorSeverity = 16&lt;br /&gt;    ,@ErrorState = 1&lt;br /&gt;    ,@ErrorLine = 0&lt;br /&gt;    ,@RC = 0&lt;br /&gt;    ,@NewLine = char(13) + char(10)&lt;br /&gt;&lt;br /&gt;begin try&lt;br /&gt;&lt;br /&gt;-- Validations -----------------------------------------------------------&lt;br /&gt;if object_id('tempdb..##Settings') is null begin&lt;br /&gt;    Select @RaiseMessage = 'Error - Temporary table ##Settings is required.'&lt;br /&gt;    Goto ErrorHandler&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;If @@ServerName not like '%' begin&lt;br /&gt;    Select @RaiseMessage = '!!! Error - Do NOT run in production !!!'&lt;br /&gt;    Goto ErrorHandler&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;-- Loop through ##Settings table and run update to mask PII for each table&lt;br /&gt;select @RaiseMessage = 'Beginning stored procedure ' + @ProcedureName + ' at ' + convert(varchar(50), getdate(),120)&lt;br /&gt;RAISERROR (@RaiseMessage, 0, 1) WITH NOWAIT&lt;br /&gt;&lt;br /&gt;Declare Settings cursor local forward_only read_only&lt;br /&gt;For&lt;br /&gt;Select distinct&lt;br /&gt;    Table_Schema&lt;br /&gt;    ,Table_Name&lt;br /&gt;from&lt;br /&gt;##Settings&lt;br /&gt;Open Settings&lt;br /&gt;Fetch next from Settings into @Table_Schema, @Table_Name&lt;br /&gt;&lt;br /&gt;While @@fetch_status=0&lt;br /&gt;    begin&lt;br /&gt;    -- Create the sql needed for the main part of the update statement&lt;br /&gt;    Select @UpdateSQL = 'Update ' + @Table_Schema + '.' + @Table_Name + ' Set '&lt;br /&gt;    &lt;br /&gt;    -- Create the sql needed for the Set part of the update statement&lt;br /&gt;    Select @SetSQL = ''&lt;br /&gt;    Select @SetSQL = @SetSQL + ', ' + Column_Name + '=' + MaskValue from ##Settings where Table_Schema = @Table_Schema and Table_Name = @Table_Name&lt;br /&gt;    Select @SetSQL = substring(@SetSQL, 2, 4000) -- Trim the first comma from @SetSQL&lt;br /&gt;    select @UpdateSQL = @UpdateSQL + @SetSQL&lt;br /&gt;    &lt;br /&gt;    -- Execute the sql&lt;br /&gt;    select @RaiseMessage = 'Beginning sql statement: ' + convert(varchar(50), getdate(),120) + ':' + @UpdateSQL&lt;br /&gt;    RAISERROR (@RaiseMessage, 0, 1) WITH NOWAIT&lt;br /&gt;    exec sp_executesql @UpdateSQL&lt;br /&gt;    Select @Rowcount = @@Rowcount&lt;br /&gt;    select @RaiseMessage = 'Completed sql statement: ' + convert(varchar(50), getdate(),120) + '; Updated: ' + convert(varchar(10), @Rowcount)&lt;br /&gt;    RAISERROR (@RaiseMessage, 0, 1) WITH NOWAIT&lt;br /&gt;    &lt;br /&gt;    Fetch next from Settings into @Table_Schema, @Table_Name&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;Close Settings&lt;br /&gt;Deallocate Settings&lt;br /&gt;&lt;br /&gt;-- Catch block -----------------------------------------------------------&lt;br /&gt;end try&lt;br /&gt;begin catch&lt;br /&gt;    Select @RaiseMessage = 'Error on ' + @RaiseMessage&lt;br /&gt;    select &lt;br /&gt;        @ExitCode = @RC_FAILURE&lt;br /&gt;        , @RaiseMessage = @RaiseMessage + ' : ' + error_message()&lt;br /&gt;        , @ErrorNumber = error_number()&lt;br /&gt;        , @ErrorSeverity = error_severity()&lt;br /&gt;        , @ErrorState = error_state()&lt;br /&gt;        , @ErrorLine = error_line()&lt;br /&gt;    &lt;br /&gt;    goto ErrorHandler&lt;br /&gt;end catch&lt;br /&gt;goto ExitProc&lt;br /&gt;&lt;br /&gt;-- Error Handler -----------------------------------------------------------&lt;br /&gt;ErrorHandler:&lt;br /&gt;    RAISERROR (@RaiseMessage, 16, 1) WITH NOWAIT&lt;br /&gt;    goto ExitProc&lt;br /&gt;&lt;br /&gt;-- Exit Procedure -----------------------------------------------------------&lt;br /&gt;ExitProc:&lt;br /&gt;    return (@ExitCode)&lt;br /&gt;go&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;In order to use the stored procedure ##Common_PIIInfoDelete, you need to first create the ##Settings table, populate it, and then call it. Here's an example: &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="mycode"&gt;&lt;pre&gt;&lt;br /&gt;if object_id('tempdb..##Settings') is not null drop table ##Settings&lt;br /&gt;Create Table ##Settings (&lt;br /&gt;    Table_Schema sysname&lt;br /&gt;    ,Table_Name sysname&lt;br /&gt;    ,Column_Name sysname&lt;br /&gt;    ,MaskValue varchar(50)&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;-- Now, insert into the ##Settings table. These values will be used when calling the stored procedure&lt;br /&gt;Insert into ##Settings&lt;br /&gt;    -- (Table_Schema Table_Name           Column_Name               MaskValue&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;select 'dbo'          ,'CCDetailLog'      ,'FirstName'              ,''''''                     union all&lt;br /&gt;select 'dbo'          ,'CCDetailLog'      ,'LastName'               ,''''''                     union all&lt;br /&gt;select 'dbo'          ,'CCDetailLog'      ,'Address1'               ,''''''                     union all&lt;br /&gt;select 'dbo'          ,'CCDetailLog'      ,'CustomerEmailAddress'   ,''''''                     union all&lt;br /&gt;select 'dbo'          ,'CCDetailLog'      ,'CreditCardNbr'          ,'convert(varbinary, '''')' union all&lt;br /&gt;select 'Payment'      ,'CCDetailLog'      ,'PhoneAreaCodeNbr'       ,''''''                     union all&lt;br /&gt;select 'Payment'      ,'CCDetailLog'      ,'PhoneLocalNbr'          ,''''''                     union all&lt;br /&gt;select 'Payment'      ,'CCDetailLog'      ,'PhoneExtensionNbr'      ,''''''                     union all&lt;br /&gt;select 'Stage'        ,'CCDetailLogStage' ,'LastName'               ,''''''                     union all&lt;br /&gt;select 'Stage'        ,'CCDetailLogStage' ,'Address1'               ,''''''                     union all&lt;br /&gt;select 'Stage'        ,'CCDetailLogStage' ,'CustomerEmailAddress'   ,''''''&lt;br /&gt;&lt;br /&gt;-- Call the stored procedure to mask the PII values&lt;br /&gt;exec ##Common_PIIInfoDelete&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-6917766068046541678?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/6917766068046541678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2011/06/masking-pii-personally-identifiable.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/6917766068046541678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/6917766068046541678'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2011/06/masking-pii-personally-identifiable.html' title='Masking PII (Personally Identifiable Information)'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-8392133760651271792</id><published>2011-02-01T21:40:00.001-08:00</published><updated>2011-02-03T10:00:15.037-08:00</updated><title type='text'>Using Temporary Stored Procedures</title><content type='html'>Does it make sense to use temporary stored procedures?  My answer before running into this particular situation would be been, "No—why would you, when you can create a regular stored procedure?".&lt;br /&gt;&lt;br /&gt;However, I know now that there ARE some cases where it makes sense to use a temporary stored procedure.  Specifically, if these 2 conditions exist:&lt;br /&gt;&lt;br /&gt;1.  The business has very rigid procedural requirements that must be met before a new stored procedure can be put into production, and&lt;br /&gt;&lt;br /&gt;2.  You need to continually make data changes that are not possible with the regular tools and services that use the database.  For instance, manually deactivating or deleting records, adding complex domain data, etc.  You frequently have to make the same type of complex change over and over.&lt;br /&gt;&lt;br /&gt;Before starting to use temporary stored procedures, I or someone on my team would write up a separate script to do whatever task needed doing.  For instance, if I needed to add a set of domain data (including validation, lookups, and logging) we would script it out, save it to the source code control system, and send in a service request to get it run in production.  The next time a similar script was needed we would copy and paste the old script (assuming we could find it), change the applicable values, check it in and submit another service request.  &lt;br /&gt;&lt;br /&gt;However, this type of copy and paste programming is very error prone and time-consuming (see the wikipedia entry &lt;a href="http://en.wikipedia.org/wiki/Copy_and_paste_programming"&gt;here&lt;/a&gt;).  It's best to avoid it where possible.  What I've started doing instead is creating temporary stored procedures for these types of tasks.  I script them out and save them to an "OperationalScripts" folder.  Then, whenever I need to do a similar task, I will create a two-step service request.  &lt;br /&gt;&lt;br /&gt;Step 1 is to create the temporary stored procedure.  For instance, &lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;Create Procedure #ConfigurationAddDomainData&lt;br /&gt;(&lt;br /&gt;    @pProductID         int               &lt;br /&gt;    ,@pComponentID      int&lt;br /&gt;    ,@pLastUpdatedBy    varchar(32)   &lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;as&lt;br /&gt;-- long script with complex error handling, validation and logging&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Step 2 is a call to the temporary stored procedure.  &lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;exec #ConfigurationAddDomainData&lt;br /&gt;    @pProductID         = 15&lt;br /&gt;    ,@pComponentID      = 28&lt;br /&gt;    ,@pLastUpdatedBy    = 'Bug 1234'&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The next time I need to do the same task, of course, I just write a simple script to call the temporary stored procedure with the correct parameters, then run first the script that creates the temporary stored procedure, then the script that makes a call to that stored procedure.&lt;br /&gt;&lt;br /&gt;When time and resources allow, this temporary stored procedure can be incorporated into an administrative tool.  But meanwhile, nobody is wasting time and effort rewriting throwaway scripts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-8392133760651271792?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/8392133760651271792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2011/02/using-temporary-stored-procedures.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/8392133760651271792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/8392133760651271792'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2011/02/using-temporary-stored-procedures.html' title='Using Temporary Stored Procedures'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-9136155744942802231</id><published>2010-11-11T10:54:00.000-08:00</published><updated>2010-11-11T11:05:06.578-08:00</updated><title type='text'>Playing with SQL Azure; part 3</title><content type='html'>Well, I think I'm done with SQL Azure.  Not because I'm finding any kind of major problems with it, just because&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: small; line-height: 15px; "&gt;—&lt;/span&gt;there's not enough that's different between regular SQL Server and SQL Azure!  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The last thing I did here was create some additional tables and stored procedures in my SQL Azure database.  I inserted to these tables via BCP, that was also fairly straightforward once I figured out a few gotchas (naming for the user and server).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I thought there would be so much to learn, but honestly, for a database developer (as opposed to a DBA) it appears to be 99% the same as  SQL Server.  Now, if I were doing a full fledged pilot project, with stress tests, investigating privacy issues, size issues etc, it would be different.  But for just playing around for my own interest, I'm all done, and it's been fun!  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-9136155744942802231?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/9136155744942802231/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2010/11/playing-with-sql-azure-part-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/9136155744942802231'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/9136155744942802231'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2010/11/playing-with-sql-azure-part-3.html' title='Playing with SQL Azure; part 3'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-1487530299694434679</id><published>2010-11-04T14:41:00.001-07:00</published><updated>2010-11-04T14:52:59.928-07:00</updated><title type='text'>Playing with SQL Azure; part 2</title><content type='html'>&lt;span class="Apple-style-span" &gt;I'm working my way through the SQL Azure tutorial (the one I found most useful was &lt;a href="http://msdn.microsoft.com/en-us/wazplatformtrainingcourse_introtosqlazureforvs2010developers_topic3.aspx"&gt;here&lt;/a&gt;).  &lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;Ran into some surprises - you can't use the "Use DatabaseName" syntax to switch from one database to another (you have to open a connection in another database).  &lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;Also, it appears that you can't reference another database on the same server.  For instance, when running the following:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;p&gt;&lt;/p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;select * from TestSylvia..test1&lt;/pre&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;I got this error:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;p&gt;&lt;/p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;Msg 40515, Level 16, State 1, Line 1  &lt;/pre&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: arial; "&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;Reference to database and/or server name in 'TestSylvia..test1' is not supported in this version of SQL Server.&lt;/pre&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;From some searching online, it looks like that is being worked on.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;So, there's some thing that are not yet possible.  But what really struck me is the fact that overall, working on SQL Azure is so similar to working with an on-premises MS SQL Server database.  I look forward to exploring further.  And writing up a micro blog post on SQL Azure really motivates me to explore further.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-1487530299694434679?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/1487530299694434679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2010/11/playing-with-sql-azure-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/1487530299694434679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/1487530299694434679'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2010/11/playing-with-sql-azure-part-2.html' title='Playing with SQL Azure; part 2'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-7428734659155670538</id><published>2010-10-28T11:32:00.001-07:00</published><updated>2010-10-28T11:44:34.478-07:00</updated><title type='text'>Playing with SQL Azure; part 1</title><content type='html'>For my own amusement and edification I've decided to learn about cloud computing by playing with SQL Azure.  Within the last year, I've moved my personal information into the cloud (from Palm Pilot to a Nexus One Android phone with Google Docs, Remember the Milk, Google Contacts, etc) and am curious to see how that same switch could happen with databases.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Also, I just read the book &lt;a href="http://www.amazon.com/Big-Switch-Rewiring-Edison-Google/dp/0393062287"&gt;The Big Switch: Rewiring the World, from Edison to Google&lt;/a&gt;.  It's basically about how there's a very strong analogy between the move to cloud computing today and what happened about 100 years ago, when factories, instead of generating their own electricity, converted to buying their power from electrical plants.  Interesting stuff.  And I do believe that the movement to cloud computing is just as inevitable as the movement to buying power from dedicated electrical plants.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For my first step, I've set up a SQL Azure account (had to provide a credit card number, but they say they won't charge anything for three months).  I've set up a database, and will be experimenting with it (downloading tools to access it, etc).  My first task will probably be to go through the tutorials for SQL Azure.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-7428734659155670538?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/7428734659155670538/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2010/10/playing-with-sql-azure-part-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/7428734659155670538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/7428734659155670538'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2010/10/playing-with-sql-azure-part-1.html' title='Playing with SQL Azure; part 1'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-6344281918745286119</id><published>2010-10-01T19:34:00.000-07:00</published><updated>2010-10-04T08:24:56.831-07:00</updated><title type='text'>Recursive common table expression 'TableName' does not contain a top-level UNION ALL operator.</title><content type='html'>I've started using CTEs (Common Table Expressions) a lot.  I find they can make my code quite a bit easier to read.  But just now, working on a SQL statement with a CTE, it took me a while to figure out why I was getting this error message:  &lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;Msg 252 &lt;br /&gt;Recursive common table expression [TableName] does not contain a top-level UNION ALL operator.&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;It was a long and complex sql statement with multiple CTEs, which obscured the real problem.  The little I found online wasn't helping either.  I ended up going step by step, and stripping out everything from the SQL statement.  Finally I ended up with something similar to this, which returns the error:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;;With table1 AS&lt;br /&gt;(&lt;br /&gt;    Select field1 from table1 where field1 = 1&lt;br /&gt;)&lt;br /&gt;Select  * from table1&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;At this point it was obvious—you can't name your CTE with the same name as a table it's referencing.  So the answer was to rename my CTE to something like this:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;;With table1_filtered AS&lt;br /&gt;(&lt;br /&gt;    Select field1 from table1 where field1 = 1&lt;br /&gt;)&lt;br /&gt;Select  * from table1&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;There's other conditions that can cause this error to occur as well, but I didn't see this one described online anywhere.  If you have this problem as well, feel free to comment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-6344281918745286119?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/6344281918745286119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2010/10/recursive-common-table-expression.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/6344281918745286119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/6344281918745286119'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2010/10/recursive-common-table-expression.html' title='Recursive common table expression &apos;TableName&apos; does not contain a top-level UNION ALL operator.'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-3928151685542000882</id><published>2010-09-16T10:20:00.000-07:00</published><updated>2010-09-21T14:25:40.299-07:00</updated><title type='text'>Are you running out of space on your development SQL server?</title><content type='html'>&lt;div&gt;On a couple of our development SQL Server machines, we've run out of space fairly often. Lots of databases are being created frequently by multiple different developers, sometimes production backups are restored, or sample data is loaded.  Lots of databases are dropped, too.  But I believe there may be some bug in the drop database command, such that sometimes, it drops the databases, but the mdf and ndf files are left there. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is not something I've been able to reproduce, but I've run into it enough that I have a routine that goes like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;1.  Run out of space on the development server&lt;/div&gt;&lt;div&gt;2.  Check on the drive that's low on space for files over a certain size - usually about 5o gigs.   In our environment, these are almost always SQL Server database files.&lt;/div&gt;&lt;div&gt;3.  Use th&lt;span&gt;&lt;span&gt;e  sp_msforeachdb stored procedure to che&lt;/span&gt;&lt;/span&gt;ck which, if any, of these files is associated with an existing database.    If you have a lot of databases, this is LOTS easier than running sp_helpdb for each individual database&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: verdana, arial, helvetica, sans-serif; font-size: 11px; color: rgb(51, 51, 51); "&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;exec sp_msforeachdb&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;'&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;SELECT DatabaseName = ''?'', * FROM ?.dbo.sysfiles&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;where filename like ''%InsertFileNameHere%''&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;'&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For those that are NOT associated with a current database, you can delete them and clear up some space.  Be careful here, and do any double-checking you feel is necessary.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: verdana, arial, helvetica, sans-serif; font-size: 11px; color: rgb(51, 51, 51); white-space: pre; "&gt;  &lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-3928151685542000882?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/3928151685542000882/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2010/09/are-you-running-out-of-space-on-your.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/3928151685542000882'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/3928151685542000882'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2010/09/are-you-running-out-of-space-on-your.html' title='Are you running out of space on your development SQL server?'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-9026381177768759879</id><published>2009-12-31T10:42:00.000-08:00</published><updated>2009-12-31T11:14:09.318-08:00</updated><title type='text'>Easy Error Trapping When Using xp_cmdshell</title><content type='html'>Error handling can be tough when using xp_cmdshell.  Before I learned the trick that I go over below, I could usually figure out in my code if an error had occured when I ran a command via xp_cmdshell.  However, getting details about the error, or any output at all, was tricky and could involve parsing out files.&lt;br /&gt;&lt;br /&gt;Before you start, please be aware that xp_cmdshell will be executed under the same security context as the SQL Server service, and can be a security problem in some environments.&lt;br /&gt;&lt;br /&gt;This code uses the insert/execute syntax.  If you've never used this before, it's a good idea to learn it.  Basically, instead of inserting data into a table the normal way, you can insert the results of an execute statement - in this case the xp_cmdshell statement, like below:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;Insert into XPCmdShellOutput &lt;br /&gt;Execute master..xp_cmdshell 'bcp tempdb..Employee out c:\temp\Employee.txt -c'&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;So, below is an easy, straightforward way to use xp_cmdshell to bcp out a table, and also see the output from the command.  The same principles can be used for any other use of xp_cmdshell, and not only bcp.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;set nocount on&lt;br /&gt;use tempdb&lt;br /&gt;&lt;br /&gt;if object_id('tempdb..Employee') is not null drop table Employee&lt;br /&gt;if object_id('tempdb..XPCmdShellOutput') is not null drop table XPCmdShellOutput&lt;br /&gt;&lt;br /&gt;-- Create the table that we need to extract from&lt;br /&gt;create table Employee (EmployeeName varchar(20))&lt;br /&gt;insert into Employee values ('John')&lt;br /&gt;&lt;br /&gt;-- This table will be used to gather the output of xp_cmdshell&lt;br /&gt;create table XPCmdShellOutput (OutputLine varchar(1000))&lt;br /&gt;&lt;br /&gt;-- Show the output of xp_cmdshell when the directory does not exist&lt;br /&gt;Insert into XPCmdShellOutput &lt;br /&gt;Execute master..xp_cmdshell 'bcp tempdb..Employee out c:\DirectoryDoesNotExist\Employee.txt -c'&lt;br /&gt;select 'Error when directory does not exist' = OutputLine from XPCmdShellOutput&lt;br /&gt;delete from XPCmdShellOutput&lt;br /&gt;&lt;br /&gt;-- Show output of xp_cmdshell when the table to be exported does not exist&lt;br /&gt;Insert into XPCmdShellOutput &lt;br /&gt;Execute master..xp_cmdshell 'bcp tempdb..Employee1 out c:\temp\Employee.txt -c'&lt;br /&gt;select 'Error when table does not exist' = OutputLine from XPCmdShellOutput&lt;br /&gt;delete from XPCmdShellOutput&lt;br /&gt;&lt;br /&gt;-- Finally, successfully export the table!&lt;br /&gt;Insert into XPCmdShellOutput &lt;br /&gt;Execute master..xp_cmdshell 'bcp tempdb..Employee out c:\temp\Employee.txt -c'&lt;br /&gt;select 'Successfully export the table!' = OutputLine from XPCmdShellOutput&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;When you run the above code, this will be the output (note that you may need to modify the c:\temp directory in your environment, and you need create table permissions in the tempdb database):&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;&lt;br /&gt;Error when directory does not exist:&lt;br /&gt;&lt;br /&gt;OutputLine&lt;br /&gt;Password: &lt;br /&gt;SQLState = S1000, NativeError = 0&lt;br /&gt;Error = [Microsoft][ODBC SQL Server Driver]Unable to open BCP host data-file&lt;br /&gt;NULL&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Error when table does not exist:&lt;br /&gt;&lt;br /&gt;OutputLine&lt;br /&gt;Password: &lt;br /&gt;SQLState = S0002, NativeError = 208&lt;br /&gt;Error = [Microsoft][ODBC SQL Server Driver][SQL Server]Invalid object name 'tempdb..Employee1'.&lt;br /&gt;NULL&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Successfully export the table!&lt;br /&gt;&lt;br /&gt;OutputLine&lt;br /&gt;Password: &lt;br /&gt;NULL&lt;br /&gt;Starting copy...&lt;br /&gt;NULL&lt;br /&gt;1 rows copied.&lt;br /&gt;Network packet size (bytes): 4096&lt;br /&gt;Clock Time (ms.): total        1 Avg        1 (1000.00 rows per sec.)&lt;br /&gt;NULL&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;The output of the xp_cmdshell was inserted into the XPCmdShellOutput table every time we ran it, because we used the insert/execute syntax. Then, we select from XPCmdShellOutput to show what the output actually was.  The first section shows the error when the directory does not exist.  The second shows the error message when table name to be exported is misspelled.  And the third shows a successful export.  &lt;br /&gt;&lt;br /&gt;This is sample code, and simplified to make it easier to understand.  In working code, you would check the XPCmdShellOutput table for the string "Error".  If the error string exists, then obviously an error occurred, and the details would have been stored in the XPCmdShellOutput table.  Note that when the export is successful, you can also extract other information from the XPCmdShellOutput table - for instance, how many rows were copied out, and how long the export took.&lt;br /&gt;&lt;br /&gt;When using xp_cmdshell with bcp, keep in mind that it will NOT recognize any temporary tables that were created.  If you need to use temporary tables, they must be global temporary tables, prefixed with ## instead of #.&lt;br /&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-9026381177768759879?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/9026381177768759879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/12/easy-error-trapping-when-using.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/9026381177768759879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/9026381177768759879'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/12/easy-error-trapping-when-using.html' title='Easy Error Trapping When Using xp_cmdshell'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-8426499834716668512</id><published>2009-06-23T11:07:00.000-07:00</published><updated>2009-06-23T11:24:57.646-07:00</updated><title type='text'>Renaming a Column in a Temp Table in SQL Server 2005 - Yes, You Can!</title><content type='html'>For whatever weird reason, you may need to create a temp table in one step, and then rename one of the columns.  Perhaps you're creating the temp table in one stored procedure, and modifying it in another.  I'm not going to tell you that's a silly thing to do, or you should just create it with the right names in the first place - I saw those kind of responses online, and it's very unhelpful!  Sometimes you just need to do this type of thing.&lt;br /&gt;&lt;br /&gt;It's not a straightforward thing to do, though.  When you just run the below script that calls sp_rename:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;if object_id('tempdb..#Test123') is not null drop table #Test123&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;create table #Test123 (field1 int)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;exec sp_rename '#Test123.Field1', 'Field2', 'COLUMN'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...you get this error: "Either the parameter @objname is ambiguous or the claimed @objtype (COLUMN) is wrong."&lt;br /&gt;&lt;br /&gt;The key is to call sp_rename from the tempdb, like so:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;if object_id('tempdb..#Test123') is not null drop table #Test123&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;create table #Test123 (field1 int)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;exec tempdb..sp_rename '#Test123.Field1', 'Field2', 'COLUMN'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;select * from #Test123&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;And - success!  Note that you'll still have problems referencing the specific fieldname that was renamed.  I solved this by doing a select into another temp table.  Also, the procedure you're doing this in will probably have to do a recompile - that wasn't a problem for me, either.  Overall, it was a better solution than the alternatives.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-8426499834716668512?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/8426499834716668512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/06/renaming-column-in-temp-table-in-sql.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/8426499834716668512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/8426499834716668512'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/06/renaming-column-in-temp-table-in-sql.html' title='Renaming a Column in a Temp Table in SQL Server 2005 - Yes, You Can!'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-5502580225212080425</id><published>2009-05-21T13:06:00.001-07:00</published><updated>2010-03-02T10:24:46.794-08:00</updated><title type='text'>Easily delete database objects</title><content type='html'>Any developer working on larger, more complex systems, will eventually need to do some massive cleanup of tables, stored procedures, and other objects that are no longer used. Although you could just write a simple statement like this:&lt;br /&gt;&lt;br /&gt;Drop table TestTable&lt;br /&gt;&lt;br /&gt;...you should really have something more robust, that returns informative messagess and checks for errors. Something like this:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;Declare @Error int&lt;br /&gt;if exists (select * from sys.objects where name = 'TestTable' and type = 'u' and schema_id = schema_id('dbo') ) begin&lt;br /&gt;-- The table exists, prepare to delete it&lt;br /&gt;Drop table dbo.TestTable&lt;br /&gt;Select @Error = @@Error&lt;br /&gt;if @Error &lt;&gt; 0 begin&lt;br /&gt;RAISERROR ('Error dropping table dbo.TestTable' ,16 ,1)&lt;br /&gt;end&lt;br /&gt;print 'Successfully dropped Table TestTable.'&lt;br /&gt;end &lt;br /&gt;else begin&lt;br /&gt;print 'Table TestTable does not exist or has already been deleted.'&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;But do you want to constantly be rewriting that piece of code as you need to drop different objects? Absolutely not! That's why I wrote the stored procedure above called sp_DropDatabaseObject that will delete many different types of database objects (tables, procedures, views, functions, and indexes). It incorporates all the functionality above (error trapping, good error messages), in a reusable procedure. It's created in the master database, so that it can be called from any database. Note that at the end, I call sp_MS_marksystemobject to mark it as a system object - this allows it to have the context of the calling database even though it's located in the master database.&lt;br /&gt;&lt;br /&gt;Here are some examples of how to run sp_DropDatabaseObject&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;-- Drop table&lt;br /&gt;exec sp_DropDatabaseObject 'dbo', 'TestTable', 'u'&lt;br /&gt;-- Drop procedure&lt;br /&gt;exec sp_DropDatabaseObject 'dbo', 'TestProcedure', 'p'&lt;br /&gt;-- Drop index&lt;br /&gt;exec sp_DropDatabaseObject 'dbo', 'TestTable.index1', 'i'&lt;br /&gt;-- Drop View&lt;br /&gt;exec sp_DropDatabaseObject 'dbo', 'TestView', 'v'&lt;br /&gt;-- Drop function&lt;br /&gt;exec sp_DropDatabaseObject 'dbo', 'TestFunction', 'fn'&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;And below is the code for the stored procedure.  I haven't yet modified it to use SQL 2005 error trapping (try/catch), but that would be a definite improvement.&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;use master&lt;br /&gt;go&lt;br /&gt;&lt;br /&gt;Create procedure dbo.sp_DropDatabaseObject &lt;br /&gt; @pSchemaName varchar(100) -- the schema the object belongs to, when applicable&lt;br /&gt; ,@pObjectName sysname -- name of the object to drop, including schema (i.e. dbo.TableName)&lt;br /&gt; ,@pObjectType char(2) -- type of object to be dropped. &lt;br /&gt; -- Can be 'U', 'V', 'P', 'FN', 'I' (for table, view, procedure, function, and index)&lt;br /&gt;&lt;br /&gt;as&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;-- Declarations&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;declare -- Standard declares&lt;br /&gt; @FALSE tinyint -- Boolean false.&lt;br /&gt; ,@TRUE tinyint -- Boolean true.&lt;br /&gt; ,@ExitCode int -- Return value of this procedure.&lt;br /&gt; ,@rc int -- Return code from a called SP.&lt;br /&gt; ,@Error int -- Store error codes returned by statements and procedures (@@error).&lt;br /&gt; ,@RaiseMessage varchar(1000) -- Creates helpful message to be raised when running.&lt;br /&gt;&lt;br /&gt;declare -- sp specific declares&lt;br /&gt; @SingleQuote nchar(1)&lt;br /&gt; ,@SQL nvarchar(4000)&lt;br /&gt; ,@IndexTableName varchar(50)&lt;br /&gt; ,@IndexIndexName varchar(50)&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;-- Initializations&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;select -- Standard constants&lt;br /&gt; @FALSE = 0&lt;br /&gt; ,@TRUE = 1&lt;br /&gt; ,@ExitCode = 0&lt;br /&gt; ,@rc = 0&lt;br /&gt; ,@Error = 0&lt;br /&gt;&lt;br /&gt;Select&lt;br /&gt; @SingleQuote = char(39)&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;-- Validate that all objects have an appropriate ObjectType&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;if @pObjectType not in ('U', 'V', 'P', 'FN', 'I') begin&lt;br /&gt; select @RaiseMessage = 'Invalid ObjectType value: ' + @pObjectType&lt;br /&gt; goto ErrorHandler&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;-- Put together the SQL to drop the database object&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;if @pObjectType = 'U' begin&lt;br /&gt; if exists (select * from sys.objects where name = @pObjectName and type = @pObjectType and schema_id = schema_id(@pSchemaName) ) begin&lt;br /&gt; -- The table exists, prepare to delete it&lt;br /&gt; Select @SQL = 'Drop table ' + @pSchemaName + '.' + @pObjectName &lt;br /&gt; end &lt;br /&gt; else begin&lt;br /&gt; select @RaiseMessage = 'Table ' + @pObjectName + ' does not exist or has already been deleted'&lt;br /&gt; print @RaiseMessage&lt;br /&gt; goto ExitProc&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;if @pObjectType = 'V' begin&lt;br /&gt; if exists (select * from sys.objects where name = @pObjectName and type = @pObjectType and schema_id = schema_id(@pSchemaName) ) begin&lt;br /&gt; -- The view exists, prepare to delete it&lt;br /&gt; Select @SQL = 'Drop view ' + @pSchemaName + '.' + @pObjectName &lt;br /&gt; end &lt;br /&gt; else begin&lt;br /&gt; select @RaiseMessage = 'View ' + @pObjectName + ' does not exist or has already been deleted'&lt;br /&gt; print @RaiseMessage&lt;br /&gt; goto ExitProc&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;if @pObjectType = 'P' begin&lt;br /&gt; if exists (select * from sys.objects where name = @pObjectName and type = @pObjectType and schema_id = schema_id(@pSchemaName) ) begin&lt;br /&gt; -- The procedure exists, prepare to delete it&lt;br /&gt; Select @SQL = 'Drop procedure ' + @pSchemaName + '.' + @pObjectName &lt;br /&gt; end &lt;br /&gt; else begin&lt;br /&gt; select @RaiseMessage = 'Procedure ' + @pObjectName + ' does not exist or has already been deleted'&lt;br /&gt; print @RaiseMessage&lt;br /&gt; goto ExitProc&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;if @pObjectType = 'FN' begin&lt;br /&gt; if exists (select * from sys.objects where name = @pObjectName and type = @pObjectType and schema_id = schema_id(@pSchemaName) ) begin&lt;br /&gt; -- The function exists, prepare to delete it&lt;br /&gt; Select @SQL = 'Drop function ' + @pSchemaName + '.' + @pObjectName &lt;br /&gt; end &lt;br /&gt; else begin&lt;br /&gt; select @RaiseMessage = 'Function ' + @pObjectName + ' does not exist or has already been deleted'&lt;br /&gt; print @RaiseMessage&lt;br /&gt; goto ExitProc&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;if @pObjectType = 'I' begin&lt;br /&gt; -- Parse out the table/index names to be able to test for index existance easily&lt;br /&gt; Select @IndexTableName = substring(@pObjectName, 1, CHARINDEX('.', @pObjectName) - 1) &lt;br /&gt; Select @IndexIndexName = substring(@pObjectName, CHARINDEX('.', @pObjectName) + 1, 50 )&lt;br /&gt; If IndexProperty(OBJECT_ID(@IndexTableName),@IndexIndexName,'IndexID') IS not NULL begin&lt;br /&gt; -- Check first whether it's a primary key&lt;br /&gt; if exists &lt;br /&gt; (&lt;br /&gt; select * from sys.indexes where is_primary_key = @TRUE and object_name(object_id) = @IndexTableName and name = @IndexIndexName&lt;br /&gt; ) &lt;br /&gt; begin&lt;br /&gt; Select @SQL = 'Alter table ' + @pSchemaName + '.' + @IndexTableName + ' drop constraint ' + @IndexIndexName&lt;br /&gt; end&lt;br /&gt; else begin&lt;br /&gt; Select @SQL = 'Drop Index ' + @pSchemaName + '.' + @pObjectName &lt;br /&gt; end&lt;br /&gt; end&lt;br /&gt; else begin&lt;br /&gt; select @RaiseMessage = 'Index ' + @pObjectName + ' does not exist or has already been deleted'&lt;br /&gt; print @RaiseMessage&lt;br /&gt; goto ExitProc&lt;br /&gt; end &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;-- Drop the database object&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;if @SQL is not null begin&lt;br /&gt; Exec @RC = sp_executesql @sql&lt;br /&gt; select @Error = @@Error&lt;br /&gt; if @Error &lt;&gt; 0 or @RC &lt;&gt; 0 begin&lt;br /&gt; select @RaiseMessage = 'Error dropping object : ' + @pObjectName + ' using sql statement: ' + @SQL&lt;br /&gt; goto ErrorHandler &lt;br /&gt; end&lt;br /&gt; Select @RaiseMessage = 'Completed dropping object: ' + @pObjectName + ' using sql statement: ' + @SQL&lt;br /&gt; print @RaiseMessage&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;goto ExitProc&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;-- Error Handler&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;ErrorHandler:&lt;br /&gt;&lt;br /&gt; select @ExitCode = -100&lt;br /&gt;&lt;br /&gt; -- Print the Error Message now that will kill isql.&lt;br /&gt; RAISERROR (&lt;br /&gt; @RaiseMessage&lt;br /&gt; ,16 -- Severity.&lt;br /&gt; ,1 -- State.&lt;br /&gt; )&lt;br /&gt;&lt;br /&gt; goto ExitProc&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;-- Exit Procedure&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;ExitProc:&lt;br /&gt;&lt;br /&gt; return (@ExitCode)&lt;br /&gt;&lt;br /&gt;go&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-- Marks it as a system object. Otherwise, it may return object information from the master database instead of the calling database&lt;br /&gt;EXEC sys.sp_MS_marksystemobject sp_DropDatabaseObject&lt;br /&gt;GO&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-5502580225212080425?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/5502580225212080425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/05/easily-delete-database-objects.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/5502580225212080425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/5502580225212080425'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/05/easily-delete-database-objects.html' title='Easily delete database objects'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-7436839566951820214</id><published>2009-05-19T11:36:00.000-07:00</published><updated>2009-05-19T12:10:32.874-07:00</updated><title type='text'>9 Things to Do When You Inherit a Database</title><content type='html'>So—Bob’s left the company to move back east, and you’re the new lead database developer on the database. Or, the third-party company to which the maintenance has been outsourced is no longer working on it, so it’s yours now. One way or another, you need to take over a database system that you had no part in developing. It's not in good shape, and there’s not many resources for you to tap. &lt;br /&gt;&lt;br /&gt;What do you do? &lt;br /&gt;&lt;br /&gt;I’ve been faced with this situation a few times now, and have developed a list of some of the things that have helped me the most, both in getting productive, and in bringing the database system up to par.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Backups&lt;/strong&gt;&lt;br /&gt;Make sure that backups are happening. I’m assuming here that you’re the database developer, and not the database administrator. However, just as minimum check, make sure that backups are occurring regularly. Ideally you should successfully restore the backup somewhere else.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Research&lt;/strong&gt;&lt;br /&gt;Look at the database. Go through and get an idea of the table structure, what the largest tables are by size, what the most commonly used stored procedures are, if there are jobs, and what documentation there is. Read through some the stored procedures. You may find it useful to create a quick and dirty database diagram if there isn’t one, using the built in diagramming tool in SQL Server. This can also be a good visual aid when you talk to other people.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Talk to the former developers&lt;/strong&gt;&lt;br /&gt;This may not be an option, but try hard to have a least a few friendly interviews with the former developers. This is not the time to make comments like, “I can’t believe you guys did [insert bad development practice here]”. You don’t know the history– maybe it was that way when they got the system. You’ll want to get as much information as they can give you on current issues, items on this list, etc. Keep things friendly – and maybe try to get their cell number in case of questions. A good relationship with former developers can go a long way.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;A bug database&lt;/strong&gt;&lt;br /&gt;Is there a bug database – somewhere that bugs (and sometimes enhancement ideas) are tracked for this system? This is certainly one of the things that you want to set up, if it’s not there currently. I’ve always been lucky enough to work at companies where bug tracking was taken seriously, and there were systems already in place that I could just plug into. If there’s no bug database, time to do some research. I wouldn’t suggest reinventing the wheel here, since there’s a lot of good systems out there—just use what’s available.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Source code control&lt;/strong&gt;&lt;br /&gt;Is the code in some kind of source code control system, such as VSS or Perforce? If it is—is everything up to date? I’m going to hazard a guess that it’s either not in source code control, or it hasn’t been kept up to date. That’s been a big task for me when starting work on inherited systems. There’s a number of tools with which to tackle this. In the past I’ve used a custom written perl tool that used SQL DMO, but I won’t go into detail—that’s the topic of another article. If nothing else, you could use the built in tools that SQL Server provides to script out your database objects, and check them in. Once you have everything checked in, try running a database build from the checked in code, and compare it to production. Also—make sure you have a good system to keep all the code updated!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Talk to the users and/or business owners&lt;/strong&gt;&lt;br /&gt;Sit down and have some conversations with the users. This is a good opportunity to get to know their problems and concerns, the improvements they would most like to see, and where things are heading in the future. You want to make sure that this database is sticking around, that it’s not going to be replaced with a third party product or anything like that. If you’re going to put a lot of work into improving the system, you need to know that your efforts are going to pay off for the business. Also–you’ll probably be spending lots of time on issues that are important to a well-run database system (a bug database, source code control, etc), but that won’t give them any new features. Make sure they understand this. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Establish credibility with the users by fixing a few things or making some enhancements&lt;/strong&gt;&lt;br /&gt;Even though you’ll probably be needing to spend a lot of time on tasks like setting up source code control, bug tracking, etc, you don’t want to do this exclusively. From talks with users, hopefully you’ve identified enhancements or bug fixes that you could get out quickly. Do what you can here. This is a great way to establish credibility with them. Let them know, too, that once you have the systems in place, bug fixes and enhancements will be much easier to roll out.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Create a development environment&lt;/strong&gt;&lt;br /&gt;If you don’t have a development environment, but code still needs to be written, where are the developers going to write and test their code? I hate to tell you, but if they have access, they’ll write and test in the production environment. So you may have stored procedures called CampaignEmailExport_TEST hanging around (and never getting deleted). Or—oops—you may accidentally overwrite the production version with your new version, and then it runs and causes hundreds of thousands of emails to be sent where they weren’t supposed to. Not that I’ve ever heard of this happening. This kind of problem can go a long way towards convincing users that time and money needs to be spent on working on setting up a good foundation.&lt;br /&gt;For the development environment–you may be able to just get a backup from production, and set it up on another server. If it’s too large, you might need to be creative. Whatever you do, don’t develop or test in the production environment.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Drop obsolete objects&lt;/strong&gt;&lt;br /&gt;In a system that hasn’t been maintained very well, it’s likely that there are a lot of database objects out there that aren’t being used. They may have suffixes like ‘temp’ or ‘bak’ on them. It can be hard to identify all of these, and you may be tempted to just leave them. However, they can cause a number of problems:&lt;br /&gt;&lt;br /&gt;1. They make it difficult to figure out what the actual working codebase is. If you have a lot of duplicate, backup, “working” or “temp” objects, you don’t know what your codebase is like, and how complex it is.&lt;br /&gt;&lt;br /&gt;2. Supposed you’d like to drop a tables because it’s huge, and looks like it hasn’t been updated in a long time, but it turns out that they’re being used by stored procedure X. If it turns out that stored procedure X is never used, but you’re keeping it around in the database anyway, then you’ve just lost this opportunity to enhance your code because of an obsolete stored procedure. This kind of issue, multiplied by all the obsolete objects that are in the database, can cause development to be very slow, or even grind to a halt.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Finally...&lt;/strong&gt;&lt;br /&gt;There’s potentially months and months of work if you start from scratch on all of the above. It’ll require good judgment on what to prioritize, where to start, and how much time to spend on all the tasks that need doing. And perhaps you’re not in a position to set all the priorities. But it can be worthwhile and fun to streamline and tune-up a database that just needs a little work to become a well-oiled machine, requiring much less development time. &lt;br /&gt;&lt;br /&gt;Thanks for reading! I welcome feedback in the form of comments, and may post an update to this article with the best suggestions and comments.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-7436839566951820214?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/7436839566951820214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/05/9-things-to-do-when-you-inherit.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/7436839566951820214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/7436839566951820214'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/05/9-things-to-do-when-you-inherit.html' title='9 Things to Do When You Inherit a Database'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-1387285241389601680</id><published>2009-03-31T11:38:00.000-07:00</published><updated>2010-04-09T12:54:02.520-07:00</updated><title type='text'>Enhancing the readability of your code: Table aliasing in sql</title><content type='html'>Aliasing tables can be a topic that database developers feel very strongly about. I'm actually one of the developers that do use aliases, but there's a right way to do it, and there's a wrong way.&lt;br /&gt;First, the wrong way (using the pubs sample database):&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;select&lt;br /&gt;   c.Au_id&lt;br /&gt;   ,c.au_lname&lt;br /&gt;   ,c.au_fname&lt;br /&gt;   ,a.title&lt;br /&gt;   ,a.title_id&lt;br /&gt;   ,b.royaltyper&lt;br /&gt;from titles a&lt;br /&gt;   join titleauthor b&lt;br /&gt;      on a.title_id = b.title_id&lt;br /&gt;   join authors c&lt;br /&gt;      on c.au_id = b.au_id&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;Don't laugh. I've actually seen some sql done exactly like this. Reading and understanding it later on takes far longer than it should (now what did b stand for again?).&lt;br /&gt;&lt;br /&gt;Most people would agree that the aliasing in the above example is terrible, and most sql isn't written that way. However, I'd say about 90% of sql is written like the example below:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;select&lt;br /&gt;   au.Au_id&lt;br /&gt;   ,au.au_lname &lt;br /&gt;   ,au.au_fname&lt;br /&gt;   ,ti.title&lt;br /&gt;   ,ti.title_id&lt;br /&gt;   ,ta.royaltyper&lt;br /&gt;from titles ti&lt;br /&gt;   join titleauthor ta&lt;br /&gt;      on ti.title_id = ta.title_id&lt;br /&gt;   join authors au&lt;br /&gt;      on ta.au_id = au.au_id&lt;p&gt;&lt;/p&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;This is an improvement over using aliases that have no relation whatsoever with the tablename. But in this particular sql statement, there's no reason to alias your tables names - it would just save a few keystrokes, at the cost of making it much less readable. Here's what I would do.&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;select&lt;br /&gt;   authors.Au_id&lt;br /&gt;   ,au_lname &lt;br /&gt;   ,au_fname&lt;br /&gt;   ,title&lt;br /&gt;   ,titles.title_id&lt;br /&gt;   ,royaltyper&lt;br /&gt;from titles&lt;br /&gt;   join titleauthor&lt;br /&gt;      on titles.title_id = titleauthor.title_id&lt;br /&gt;   join authors&lt;br /&gt;      on titleauthor.au_id = authors.au_id&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;In this case - just a simple select statment - it really doesn't make any sense to use aliases. &lt;br /&gt;&lt;br /&gt;It's in more complex sql, with cross database joins, subqueries, and derived tables that you really want to use aliases in order to make your code more readable. For instance, the following from clause contains tables from 4 different databases:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;from BookingStg.dbo.TSData TSData&lt;br /&gt;join BookingStg.dbo.Booking Booking&lt;br /&gt;  on Booking.BookingID = TSData.BookingID&lt;br /&gt;join Domain.dbo.BookingWin BookingWin&lt;br /&gt;  on TSData.DateOfStayTimeID = BookingWin.DateOfStayTimeID&lt;br /&gt;left join WarehouseFactStore.dbo.Customer_Dim Customer_Dim&lt;br /&gt;  on Customer_Dim.TUID = Booking.TUID&lt;br /&gt;  and Customer_Dim.ProductID = TSData.ProductID&lt;br /&gt;left join TServerImp.dbo.TLRPostalCode TLRPostalCode&lt;br /&gt;  on TLRPostalCode.TLR = TSData.TLR&lt;br /&gt;  and TLRPostalCode.ProductID = TSData.ProductID&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;When using tables from multiple databases, you alias the fully qualitifed names (i.e. DatabaseName.SchemaName.TableName) with just the tablename. Like this:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;join BookingStg.dbo.Booking Booking&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;This cuts down of the length of table references, without obscuring them.&lt;br /&gt;&lt;br /&gt;Also, in a situation where you're creating a derived table, you'll need to pick a name for it. Make sure it reflects what the table is used for. For instance, here I'm creating a derived table in order to group transactions, so the alias name reflects that:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;from #TransactionAdFact&lt;br /&gt;left join&lt;br /&gt;  (&lt;br /&gt;  Select&lt;br /&gt;     TLR&lt;br /&gt;     ,MinAdID= min(AdID)&lt;br /&gt;  from  RetailOperations.dbo.OmTransaction OmTransaction&lt;br /&gt;  join #BaseData&lt;br /&gt;     on #BaseData.OmniProductMapping  = OmTransaction.OmProductID&lt;br /&gt;  where&lt;br /&gt;     TransDateKey between @StartDateKey and @EndDateKey&lt;br /&gt;     and AdID is not null&lt;br /&gt;  group by&lt;br /&gt;     TLR&lt;br /&gt;     ,OmTransaction.OmProductID&lt;br /&gt;  ) GroupedTransactions&lt;br /&gt;  on GroupedTransactions.TLR = #TransactionAdFact.TLR&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;Please, don't call the derived table something like Temp1. I see this all the time. It may save you the minute it would cost to think of a name that would be understandable to other people, but for the sake of the people who will be reading this code - take the minute, and make your code more readable.&lt;br /&gt;&lt;br /&gt;Chosing the right tables aliases in SQL Server can either make your queries far more readable...or you can save yourself some thought and a couple keystrokes. Your choice.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-1387285241389601680?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/1387285241389601680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/03/enhancing-readability-of-your-code.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/1387285241389601680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/1387285241389601680'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/03/enhancing-readability-of-your-code.html' title='Enhancing the readability of your code: Table aliasing in sql'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-4985843196474152520</id><published>2009-03-26T09:20:00.000-07:00</published><updated>2009-12-31T10:48:59.720-08:00</updated><title type='text'>Quick SQL - getting the size of all tables in a database</title><content type='html'>I use this little chunk of SQL code almost every day.  It gives you a relatively accurate report of the size of each table in the database, including indexes.  Very handy!&lt;br /&gt; &lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;select&lt;br /&gt;   TableName   = convert(varchar(100) ,sysobjects.name)    &lt;br /&gt;   ,TotalRows  = max(sysindexes.rows)    &lt;br /&gt;   ,MbData     = floor(sum(convert(real ,sysindexes.dpages)) * spt_values.low / 1048576)   &lt;br /&gt;   ,MbTotal    = floor(sum(convert(real ,sysindexes.used)) * spt_values.low / 1048576  )&lt;br /&gt;from sysobjects &lt;br /&gt;join sysindexes   &lt;br /&gt;   on sysobjects.id = sysindexes.id&lt;br /&gt;join master.dbo.spt_values spt_values &lt;br /&gt;   on spt_values.number = 1 &lt;br /&gt;   and spt_values.type = 'E' &lt;br /&gt;where&lt;br /&gt;   sysobjects.type = 'U'   &lt;br /&gt;   and indid in (0,1,255)&lt;br /&gt;group by    &lt;br /&gt;   sysobjects.name   &lt;br /&gt;   ,spt_values.low&lt;br /&gt;order by 4 desc&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-4985843196474152520?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/4985843196474152520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/03/quick-sql-getting-size-of-all-tables-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/4985843196474152520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/4985843196474152520'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/03/quick-sql-getting-size-of-all-tables-in.html' title='Quick SQL - getting the size of all tables in a database'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-3348246676145688323</id><published>2009-03-19T11:47:00.001-07:00</published><updated>2009-03-19T12:07:12.182-07:00</updated><title type='text'>All database outputs need business owners</title><content type='html'>This is more applicable to the data warehouse world, because in an OLTP world, it's much more obvious what the purpose of database objects are.&lt;br /&gt;&lt;br /&gt;However, in the data warehouse/reporting/business intelligence world, frequently reports and fact tables and data feeds will be created, and people will use them for a while, then not find them useful anymore and stop (or leave the company).  And they won't be used again, or people don't know about them.  &lt;br /&gt;&lt;br /&gt;Aside from the problem of having a bunch of unused reports, fact tables or data feeds cluttering up your system, you have the larger problem of people assuming that they ARE still used, and need to be accounted for.  As in, "If we modify table ABC, we also need to fix reports DEF and GHI because they depend on them".  Well, if it turns out that the reports aren't being actively used anymore, you've taken on a lot of extra wasted work.&lt;br /&gt;&lt;br /&gt;One neat idea is to put in is a usage tracking tool for all reports and data feeds.  So, from whatever UI that the reports are run, have it also log a record to a table called something like ReportUsageLog.   Then regularly monitor this table to see who's using what.  If there's any reports that aren't being used, figure out why and see if you can potentially decommission them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-3348246676145688323?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/3348246676145688323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/03/all-database-outputs-need-business.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/3348246676145688323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/3348246676145688323'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/03/all-database-outputs-need-business.html' title='All database outputs need business owners'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-395799851637662130</id><published>2009-03-05T13:00:00.000-08:00</published><updated>2009-03-05T13:27:36.330-08:00</updated><title type='text'>Having a great development environment</title><content type='html'>I'm a big fan of checklists, and one of the ones I like is the one by Joel Spolsky (Joel on Software) called &lt;a href="http://www.joelonsoftware.com/articles/fog0000000043.html"&gt; The Joel Test: 12 Steps to Better Code&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;He goes through a whole set of things that you need to be doing right. The one I want to focus on today is the "&lt;em&gt;Can you&lt;/em&gt; &lt;em&gt;make a build in one step&lt;/em&gt;", and how it translates to the database world.&lt;br /&gt;&lt;br /&gt;What Joel Spolsky focuses on mainly are software products. A build is not a big deal in his world. However, in the world of complex database systems, with all kinds of inputs and outputs from external vendors, things are different. In this world, doing a build, to me, means not just having the database objects set up correctly, but also &lt;em&gt;having data&lt;/em&gt;. I'm talking about a large set of data, probably extracted from the production environment - enough to develop and test everything that you might need to.&lt;br /&gt;&lt;br /&gt;This is a lot harder than it may seem, and most companies don't do too well at it. The problem is that almost everybody starts out getting database backups from production, and using those in their development environment. This works fine, until the database starts getting too large. Then, obviously, it's not so easy to get backups from production because the files are just so darn huge.&lt;br /&gt;&lt;br /&gt;What a lot of companies do is set up the development environment, with all the database objects, and leave the transferring of data up to the individual developer. That's a big mistake. It can take a very long time to specify exactly what data you need, transfer from a production environment, and then import into a development environment. Multiple this by every developer, for every project they're working on, and you're talking about a lot of wasted time.&lt;br /&gt;&lt;br /&gt;The better approach is to write up some code to get a reasonable subset of data. There's lots of ways to do this, and I may write more about it at some point, but it's absolutely critical to get data into your dev/test database systems.  The biggest difficulty is getting management to approve spending time on a project like this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-395799851637662130?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/395799851637662130/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/03/having-great-development-environment.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/395799851637662130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/395799851637662130'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/03/having-great-development-environment.html' title='Having a great development environment'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-5512034096411449352</id><published>2009-02-19T12:19:00.000-08:00</published><updated>2010-04-09T14:12:42.306-07:00</updated><title type='text'>I hate duplicate code; part I</title><content type='html'>Just last week I was working on some code modifications, which basically just added another category of sales to a pre-existing column. When I was researching how to do this, I saw that this field was used in numerous reports in a case statement. For instance: &lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;ClassId =&lt;br /&gt;   CASE&lt;br /&gt;      WHEN ec.CSRID = 19         THEN 34       &lt;br /&gt;      WHEN he.DomainTypeID = 4   THEN 39   &lt;br /&gt;      WHEN he.DomainTypeID = 128 THEN 37 &lt;br /&gt;      ELSE 30                          &lt;br /&gt;   END&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There's a lot wrong with this code, especially because it existed in a total of 5 stored procedures. Significantly, there were already bugs because it had been modified in some procedures and not others, even though theoretically it was all supposed to be the same.&lt;/p&gt;&lt;p&gt;This violates one of the basic "best practices" of coding, which is NO DUPLICATE CODE. There's a good write-up on why duplicate code is a bad idea in Wikipedia. Basically, it's harder to understand, and harder to maintain and fix bugs in. For this particular piece of duplicate code we've already seen that there were some bugs in there by the fact that some sections were missing some ClassId categories that had been added.&lt;/p&gt;&lt;p&gt;So, to avoid having this chunk of duplicate code in multiple stored procedures, we create the following function ClassIdGet: &lt;p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;Create function dbo.ClassIdGet ( @CSRID tinyint, @DomainTypeID int )&lt;br /&gt;returns tinyint  as&lt;br /&gt;begin&lt;br /&gt;   declare @ClassId tinyint&lt;br /&gt;   select @ClassId =&lt;br /&gt;      case&lt;br /&gt;         WHEN @CSRID        = 19      THEN 34   &lt;br /&gt;         WHEN @CSRID        = 54      THEN 123  &lt;br /&gt;         WHEN @DomainTypeID = 4       THEN 39   &lt;br /&gt;         WHEN @DomainTypeID = 128     THEN 37   &lt;br /&gt;         ELSE 30                              &lt;br /&gt;      end&lt;br /&gt;   return @ClassId&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, in my stored procedure, I call the function like this:&lt;p&gt;&lt;/p&gt;&lt;div class="mycode"&gt;&lt;pre&gt;ClassId = dbo.ClassIdGet (ec.CSRID, he.DomainTypeID)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a huge improvement. Now, whenever we add a new ClassId, we can just modify the function ClassIdGet.&lt;br /&gt;&lt;br /&gt;Is this the ideal way to take care of this issue? No - ideally I'd like to put the mapping of ClassId from CSRID and DomainTypeID into a domain table. Then adding a new ClassId would only involve adding a record to a table, instead of modifying a function. However, moving the code to a function is a world of improvement without a very dramatic change.&lt;/p&gt;&lt;p&gt;About the duplicate code above - why does this kind of poor coding practice show up again and again? I'm still thinking about that one. I think partly it's because for most companies working on internal applications, best practices in coding are not even on the radar. It's easy for people to get excited about esoteric coding methodologies, or about heavily promoted new data warehouse platforms. It's just not as easy to get excited about one of the key tenets of good programming - no duplicate code.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Also, it requires having a longer term view of things. It does take a little longer to do things the right way - in this instance, creating a both a stored procedure and a function to encapsulate the code, instead of just a stored procedure. It also takes a programmer who's experienced enough to know that copying and pasting code is usually not such a good idea. And it takes an understanding that the intitial development of the code is just a small part of the work generated over the code lifetime. &lt;/p&gt;&lt;p&gt;However, think of the time that would have been saved if it had been done right the first place - instead of needing a bunch of research and code modifications, whenever we needed a new ClassId, we would have been able to just modify one function.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;But without a culture of writing high quality code, most people tend to do what works immediately, rather than take a longer term view.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-5512034096411449352?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/5512034096411449352/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/02/i-hate-duplicate-code-part-i.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/5512034096411449352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/5512034096411449352'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/02/i-hate-duplicate-code-part-i.html' title='I hate duplicate code; part I'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-3170605212353874520</id><published>2009-02-12T10:52:00.000-08:00</published><updated>2009-02-12T11:21:14.628-08:00</updated><title type='text'>Using templates to improve your database code</title><content type='html'>If you're like me, when it's time to do some coding the first thing you do is look for some code to copy.  It's pretty sensible - why reinvent the wheel when there's something out there that may already do what you need?&lt;br /&gt;&lt;br /&gt;But instead of just copying whatever code that may already exist that seems to suits your needs, extend the concept of copying code, and make a set of templates.  For instance, you could have a set of stored procedure templates for the following purposes:&lt;br /&gt;&lt;br /&gt;- Loading data feeds, say from an external vendor&lt;br /&gt;- Pushing out data feeds&lt;br /&gt;- Processing exception records&lt;br /&gt;- Loading data warehouse fact tables&lt;br /&gt;- Reporting&lt;br /&gt;- Validating before inserting data&lt;br /&gt;&lt;br /&gt;These are oriented to data warehouse needs, but of course you could create a similar set of  stored procedure templates for an OLTP database.   When you find yourself needing a new type of functionality, you would create a new template.&lt;br /&gt;&lt;br /&gt;What are the benefits to creating templates like this, and making sure people use them?  Well, for one, you're leveraging the capabilities of the most experienced developers to help out the less experienced ones.  The more experienced developers would set up the templates, using the standards that have been set for error trapping, logging processes, etc.  Then the more junior developers could write up the code for a particular need, using the template, knowing that they're following the correct coding standards (or at least, making a good start at it).&lt;br /&gt;&lt;br /&gt;Some other benefits are that your code becomes more readable and maintainable when stored procedures follow templates.   And, of course, coding becomes much faster if you have a trusted set of templates that you can start out with.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-3170605212353874520?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/3170605212353874520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/02/using-templates-to-improve-your.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/3170605212353874520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/3170605212353874520'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/02/using-templates-to-improve-your.html' title='Using templates to improve your database code'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1345741839987072737.post-4213312510964030261</id><published>2009-02-05T09:59:00.001-08:00</published><updated>2009-02-05T10:39:29.808-08:00</updated><title type='text'>Introduction</title><content type='html'>&lt;blockquote&gt;&lt;/blockquote&gt;&lt;p&gt;I've been thinking about creating a database oriented blog for a while now. Gradually a stockpile of article ideas has been accumulating in my notes, and now is the time to starting expanding on some of them.&lt;br /&gt;&lt;br /&gt;A little about my background - I've been working on databases for 15 years. I started out working for Microsoft in tech support on DOS 7, as a contractor. I had no formal computer background whatsoever, but it turned out that I really enjoyed it, and was pretty good at it.&lt;br /&gt;&lt;br /&gt;I switched to tech support for Microsoft Access when it first came out, then started working more on ODBC and MS SQL Server issues. After a year or so of solving problems on the phone for people who were making about 5 times what I was, I decided that doing database consulting/contracting was the way to go. I got my MCDBA certification (I was one of the first!) and have been doing that ever since, focusing on MS SQL Server, but also with a heavy dose of OLAP, Perl, and various other tools.&lt;br /&gt;&lt;br /&gt;It's been a great career for me - flexible and lucrative, and often quite interesting. This blog is a way for me to give back to the database community, share some of the strategies that have been useful for me and the companies I've worked at, and also to bitch and moan about some of the problems that always seems to come up, that are also very hard to change.&lt;br /&gt;&lt;br /&gt;Unless it's something I'm really excited about, I'm going to stay away from SQL tips and tricks. That kind of information is plentiful, and just a Google search away. I'm planning on articles about topics such as:&lt;br /&gt;&lt;br /&gt;- Why is it so important to set up and maintain great development and test&lt;br /&gt;environments?&lt;br /&gt;- Databases and bug tracking software - best practices&lt;br /&gt;- Tracking database usage - why bother?&lt;br /&gt;&lt;br /&gt;I welcome comments and ideas!&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1345741839987072737-4213312510964030261?l=databasesarefun.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://databasesarefun.blogspot.com/feeds/4213312510964030261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://databasesarefun.blogspot.com/2009/02/introduction.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/4213312510964030261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1345741839987072737/posts/default/4213312510964030261'/><link rel='alternate' type='text/html' href='http://databasesarefun.blogspot.com/2009/02/introduction.html' title='Introduction'/><author><name>Sylvia Moestl Vasilik</name><uri>http://www.blogger.com/profile/13199313620723089797</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://2.bp.blogspot.com/-nHLpRljOx6A/TYIs2rz3sXI/AAAAAAAAABQ/mGK0m1EfHMA/s220/SylviaProfilePicture.jpg'/></author><thr:total>0</thr:total></entry></feed>
