<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>SQL</title>
        <link>http://nimblecoder.com/blog/category/12.aspx</link>
        <description>SQL</description>
        <language>en-US</language>
        <copyright>Ryan Van Slooten</copyright>
        <generator>Subtext Version 2.1.1.1</generator>
        <item>
            <title>When aspnet_regsql.exe won't connect</title>
            <link>http://nimblecoder.com/blog/archive/2008/04/24/when-aspnet_regsql.exe-wont-connect.aspx</link>
            <description>&lt;p&gt;I was building a quick test web site and I was using the aspnet_regsql tool to add membership to a SQLEXPRESS database. At first, I tried:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;aspnet_regsql -A all -C "Data Source=.\SQLEXPRESS;Integrated Security=True;User Instance=True" -d "C:\code\Test\APP_DATA\aspnetdb.mdf"&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;For some reason, the SqlConnection insisted that it try to create the database and disregarded the full path to the database (note the path in the exception).&lt;/p&gt;&lt;pre&gt;SQL Exception:
System.Data.SqlClient.SqlException: Directory lookup for the file "C:\Documents and Settings\user\Local Settings\Application Data\Microsoft\Microsoft SQL Server Data\SQLEXPRESS\C:\code\Asp.net\ServerControlTest\App_Data\Database.mdf" failed with the operating system error 123(The filename, directory name, or volume label syntax is incorrect.).
CREATE DATABASE failed. Some file names listed could not be created. Check related errors.
Creating the C:\code\Asp.net\ServerControlTest\App_Data\Database database...
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Web.Management.SqlServices.ExecuteFile(String file, String server, String database, String dbFileName, SqlConnection connection, Boolean sessionState, Boolean isInstall, SessionStateType sessionStatetype)
&lt;/pre&gt;
&lt;p&gt;Next I tried the simple commands (credit &lt;a href="http://www.4guysfromrolla.com/" target="_blank"&gt;Scott Mitchell&lt;/a&gt;) that worked great:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;sqlcmd -S localhost\SQLExpress -Q "EXEC sp_attach_db 'Foobar', N'pathToDBfile'"&lt;br /&gt;aspnet_regsql.exe -S localhost\SQLExpress -d Foobar -E -A all&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;References&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://scottonwriting.net/sowblog/posts/5480.aspx" target="_blank"&gt;Working with SQL Server 2005 Express Database&lt;/a&gt; 
&lt;/li&gt;&lt;li&gt;&lt;a href="http://weblogs.asp.net/lhunt/archive/2005/09/26/425966.aspx" target="_blank"&gt;Using ASPNET_RegSQL.exe with SQL Express databases in APP_DATA&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/67.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2008/04/24/when-aspnet_regsql.exe-wont-connect.aspx</guid>
            <pubDate>Thu, 24 Apr 2008 20:50:31 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/67.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2008/04/24/when-aspnet_regsql.exe-wont-connect.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/67.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/67.aspx</trackback:ping>
        </item>
        <item>
            <title>Unable to log into SQL Server after creating self-signed SSL certificate</title>
            <link>http://nimblecoder.com/blog/archive/2008/04/24/unable-to-log-into-sql-server-after-creating-self-signed-ssl.aspx</link>
            <description>&lt;p&gt;In my &lt;a href="http://www.nimblecoder.com/blog/archive/2008/04/23/sharepoint-fba-limitations-and-options.aspx" target="_blank"&gt;SharePoint experiments&lt;/a&gt; with form-based authentication (FBA), I have been &lt;a title="Setting up SSL with a SelfSSL certificate" href="http://www.visualwin.com/SelfSSL/" target="_blank"&gt;installing self-signed SSL&lt;/a&gt; certificates since I am developing in a virtual machine without a certificate authority. Last night, I shut down my virtual machine instead of the usual Suspend operation. This morning, I started the virtual machine but SharePoint wasn't working and said: Cannot connect to the configuration database.&lt;/p&gt; &lt;p&gt;I then tried to open SQL Server Management Console and tried to connect, only to receive the following message:&lt;/p&gt; &lt;p&gt;&lt;a href="http://nimblecoder.com/blog/images/nimblecoder_com/blog/WindowsLiveWriter/UnabletologintoSQLServeraftercreatingsel_A21D/SQL-Error-233-SSL.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="187" alt="A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.) (Microsoft SQL Server, Error: 233)" src="http://nimblecoder.com/blog/images/nimblecoder_com/blog/WindowsLiveWriter/UnabletologintoSQLServeraftercreatingsel_A21D/SQL-Error-233-SSL_thumb.png" width="644" border="0" /&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;I didn't freak out, but I did check the SQL Server Service which was running, and then the SQL Server Log files which looked normal. There was one line which I didn't recognize:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;The certificate was successfully loaded for encryption.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;This is actually a normal line in the log file, but it was then that I realized that the self-signed certificates I created were interfering with the login process. I searched around for Internet resources, but finally found what I was looking for in the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms186362.aspx" target="_blank"&gt;Certificate MMC snap-in configuration&lt;/a&gt;. Since I had created multiple SSL certificates, I deleted the unused certificates and checked the encryption and certificate settings in the SQL Server Configuration Manager. After a quick reboot, I was back in business.&lt;/p&gt; &lt;p&gt;References:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;a href="http://msdn2.microsoft.com/en-us/library/ms186362.aspx" target="_blank"&gt;Configuring Certificate for User by SSL&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://support.microsoft.com/kb/316898" target="_blank"&gt;How to enable SSL encryption for an instance of SQL Server&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://blogs.msdn.com/sql_protocols/archive/2005/12/22/506607.aspx" target="_blank"&gt;SQL Protocols: Troubleshoot Connectivity Issues in SQL Server 2005, Part III&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/66.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2008/04/24/unable-to-log-into-sql-server-after-creating-self-signed-ssl.aspx</guid>
            <pubDate>Thu, 24 Apr 2008 16:29:18 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/66.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2008/04/24/unable-to-log-into-sql-server-after-creating-self-signed-ssl.aspx#feedback</comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/66.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/66.aspx</trackback:ping>
        </item>
        <item>
            <title>Overcoming SQL GROUP BY challenges with ROW_NUMBER</title>
            <link>http://nimblecoder.com/blog/archive/2008/04/14/overcoming-sql-group-by-challenges-with-row_number.aspx</link>
            <description>&lt;p&gt;In SQL, aggregate functions will return the group by values or the aggregate function results, but it is difficult (or at least harder than it should be) to return the primary key or ROWID. In contrast, most programming languages will return the instance (or a pointer/reference to the instance) when searching for items.&lt;/p&gt; &lt;h4&gt;Background&lt;/h4&gt; &lt;p&gt;A project manager allocated me several weeks ago on emergency basis to help out with another project that was having difficulties with a SQL Historian system. I ended up developing a nice set of SQL tables, functions, and stored procedures to transfer data from a remote linked server into the Historian. It was actually rather fun to work on as it had several challenging aspects. Of course, after several late nights of development, I don't know how much fun like that I could take!&lt;/p&gt; &lt;p&gt;The key to the data transfer was obtaining the latest data from the remote database and inserting it into the Historian. The remote database was an Oracle database and the Historian was a SQL Server database. My first reaction was to do a simple query with a TOP specification but Oracle doesn't have a TOP function so I started to search for ways to achieve the same result. I came across the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms186734.aspx" target="_blank"&gt;ROW_NUMBER()&lt;/a&gt; function and it did the trick although it required a sub-query such as:&lt;/p&gt;&lt;pre class="sql" name="code"&gt;SELECT  *
FROM    (
    SELECT
        SAMPLE_DAY
    ,   ASSET
    ,   TANK_LEVEL_MM
    ,   TANK_PRES_PSIG
    ,   TANK_VOL_M3
    ,   FEED_FLOW_M3HR
    ,   ROW_NUMBER() OVER (PARTITION BY ASSET ORDER BY SAMPLE_DAY DESC) RN
    FROM    REMOTE_DATA_VIEW_1
    ORDER BY ASSET, SAMPLE_DAY DESC
)
WHERE   RN = 1
&lt;/pre&gt;
&lt;p&gt;It turns out that this query is more powerful than the TOP query as it essentially performs a GROUP BY aggregate (ASSET and order by SAMPLE_DAY descending) without some of the limitations of SQL aggregate functions.&lt;/p&gt;
&lt;h4&gt;Exploring Aggregate Queries&lt;/h4&gt;
&lt;p&gt;In a traditional SQL aggregate query, the columns returned by the query have to be in the GROUP BY clause or in an aggregate function. This means that it is difficult to return the primary key for a table because you usually are grouping by a name, location, or other non-unique field. Here is an example:&lt;/p&gt;&lt;pre class="sql" name="code"&gt;-- DDL: This table stores the tag names, source, and values. This
-- table is just for demonstrative purposes and does not represent
-- a normalized structure.
CREATE TABLE TAG_VALUE
(
    ValueID             int             IDENTITY    PRIMARY KEY
,   DateTime            datetime        NOT NULL
,   TagName             varchar(50)     NOT NULL
,   Value               numeric             NULL
,   Ignored             bit             NOT NULL    DEFAULT 0
,   Processed           bit             NOT NULL    DEFAULT 0
)
GO

-- Get the most recent entry grouped by tag name
SELECT  TagName
,       MAX(DateTime) AS DateTime
FROM    TAG_VALUE
GROUP BY TagName
&lt;/pre&gt;
&lt;p&gt;If you try to put the primary key as a return column, you will get an error. Unfortunately this means you end up having an ugly subquery and potentially expensive (hopefully you have indices on the matching columns as well), such as:&lt;/p&gt;&lt;pre class="sql" name="code"&gt;SELECT  ValueID
FROM    TAG_VALUE   AS V1
    JOIN (

        SELECT  TagName
        ,       MAX(DateTime) AS DateTime
        FROM    TAG_VALUE
        GROUP BY TagName

    )               AS V2
    ON (    (V1.TagName  = V2.TagName)
        AND (V1.DateTime = V2.DateTime))
&lt;/pre&gt;
&lt;h4&gt;Using ROW_NUMBER&lt;/h4&gt;
&lt;p&gt;The ROW_NUMBER function has the powerful feature of specifying the PARTITION BY which provides some of the same functionality that GROUP BY would perform but with less hassle. For example, the following query analyzes the TAG_VALUE table and looks for duplicate TagName rows. It then marks the "old" values as "Ignored" and leaves the newest entry alone.&lt;/p&gt;&lt;pre class="sql" name="code"&gt;UPDATE  TAG_VALUE
SET     Ignored = 1
FROM    TAG_VALUE AS V1
    JOIN (
        SELECT  ValueID
        ,       ROW_NUMBER() OVER (PARTITION BY TagName ORDER BY DateTime DESC) AS RN
        FROM    TAG_VALUE
        WHERE   TagName IN (
            SELECT DISTINCT TagName
            FROM        TAG_VALUE
            GROUP BY    TagName
            HAVING      COUNT(TagName) &amp;gt; 1
            )
    ) AS V2
    ON (V1.ValueID = V2.ValueID)
WHERE   RN &amp;gt; 1
&lt;/pre&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;So, in conclusion, ROW_NUMBER provides an easier method of returning the actual row so you can perform updates, deletes, or return the primary key.&lt;/p&gt;
&lt;hr /&gt;

&lt;h4&gt;Sample SQL Code&lt;/h4&gt;&lt;pre class="sql" name="code"&gt;/* ====
NOTE: The table structure resembles a database design that I have seen used.
I did not design the table structure and it is intentionally flat. Originally
this example used a remote query to allow SQL Server to query ORACLE, but for
the sample it is all in SQL Server. With very minor changes it works in Oracle
as well.
==== */

-- DDL: This table simulates a remote linked server
CREATE TABLE REMOTE_DATA_VIEW_1
(
    SAMPLE_DAY          datetime
,   ASSET               varchar(50)
,   TANK_LEVEL_MM       numeric
,   TANK_PRES_PSIG      numeric
,   TANK_VOL_M3         numeric
,   FEED_FLOW_M3HR      numeric
)
GO

-- Sample data
INSERT REMOTE_DATA_VIEW_1 VALUES ('2008-02-04', 'ASSET_01', 1200.0, 18.5, 100.0, 150.0)
INSERT REMOTE_DATA_VIEW_1 VALUES ('2008-02-01', 'ASSET_01', 1100.0, 17.5,  90.0, 145.0)
INSERT REMOTE_DATA_VIEW_1 VALUES ('2008-01-25', 'ASSET_01', 1000.0, 16.5,  80.0, 155.0)
INSERT REMOTE_DATA_VIEW_1 VALUES ('2008-02-03', 'ASSET_02',  800.0, 19.5, 110.0, 160.0)
INSERT REMOTE_DATA_VIEW_1 VALUES ('2008-02-02', 'ASSET_02',  750.0, 17.0,  95.0, 165.0)
INSERT REMOTE_DATA_VIEW_1 VALUES ('2008-02-04', 'ASSET_03', 1500.0, 18.0, 100.0, 170.0)
INSERT REMOTE_DATA_VIEW_1 VALUES ('2008-01-31', 'ASSET_04', 1350.0, 19.0,  90.0, 135.0)
GO


-- Return the raw data
SELECT
    SAMPLE_DAY
,   ASSET
,   TANK_LEVEL_MM
,   TANK_PRES_PSIG
,   TANK_VOL_M3
,   FEED_FLOW_M3HR
,   ROW_NUMBER() OVER (PARTITION BY ASSET ORDER BY SAMPLE_DAY DESC) RN
FROM    REMOTE_DATA_VIEW_1
ORDER BY ASSET, SAMPLE_DAY DESC

/* ====
SAMPLE_DAY              ASSET    TANK_LEVEL_MM TANK_PRES_PSIG TANK_VOL_M3 FEED_FLOW_M3HR RN
----------------------- -------- ------------- -------------- ----------- -------------- --
2008-02-04 00:00:00.000 ASSET_01 1200          19             100         150            1
2008-02-01 00:00:00.000 ASSET_01 1100          18             90          145            2
2008-01-25 00:00:00.000 ASSET_01 1000          17             80          155            3
2008-02-03 00:00:00.000 ASSET_02 800           20             110         160            1
2008-02-02 00:00:00.000 ASSET_02 750           17             95          165            2
2008-02-04 00:00:00.000 ASSET_03 1500          18             100         170            1
2008-01-31 00:00:00.000 ASSET_04 1350          19             90          135            1
==== */


-- Use a simple embedded query
SELECT  *
FROM    (
    SELECT
        SAMPLE_DAY
    ,   ASSET
    ,   TANK_LEVEL_MM
    ,   TANK_PRES_PSIG
    ,   TANK_VOL_M3
    ,   FEED_FLOW_M3HR
    ,   ROW_NUMBER() OVER (PARTITION BY ASSET ORDER BY SAMPLE_DAY DESC) AS RN
    FROM
        REMOTE_DATA_VIEW_1
    )   DATA
WHERE   RN = 1

/* ====
SAMPLE_DAY              ASSET    TANK_LEVEL_MM TANK_PRES_PSIG TANK_VOL_M3 FEED_FLOW_M3HR RN
----------------------- -------- ------------- -------------- ----------- -------------- --
2008-02-04 00:00:00.000 ASSET_01 1200          19             100         150            1
2008-02-03 00:00:00.000 ASSET_02 800           20             110         160            1
2008-02-04 00:00:00.000 ASSET_03 1500          18             100         170            1
2008-01-31 00:00:00.000 ASSET_04 1350          19             90          135            1
==== */


-- This is an uglier version of the query above but provided for comparison.
-- Return only the most recent entry using SAMPLE_DAY and ASSET as keys. On a production
-- table, there should be a real primary key or ROWID that you would use instead.
SELECT
    DATA.SAMPLE_DAY
,   DATA.ASSET
,   DATA.TANK_LEVEL_MM
,   DATA.TANK_PRES_PSIG
,   DATA.TANK_VOL_M3
,   DATA.FEED_FLOW_M3HR
FROM    (

        SELECT
            SAMPLE_DAY
        ,   ASSET
        ,   ROW_NUMBER() OVER (PARTITION BY ASSET ORDER BY SAMPLE_DAY DESC) AS RN
        FROM
            REMOTE_DATA_VIEW_1

    )   AS  REMOTE_QUERY
    JOIN    REMOTE_DATA_VIEW_1  AS DATA
        ON (    (REMOTE_QUERY.SAMPLE_DAY  = DATA.SAMPLE_DAY)
            AND (REMOTE_QUERY.ASSET       = DATA.ASSET) )

WHERE   REMOTE_QUERY.RN = 1
ORDER BY DATA.ASSET, DATA.SAMPLE_DAY DESC

/* ====
SAMPLE_DAY              ASSET    TANK_LEVEL_MM TANK_PRES_PSIG TANK_VOL_M3 FEED_FLOW_M3HR
----------------------- -------- ------------- -------------- ----------- --------------
2008-02-04 00:00:00.000 ASSET_01 1200          19             100         150
2008-02-03 00:00:00.000 ASSET_02 800           20             110         160
2008-02-04 00:00:00.000 ASSET_03 1500          18             100         170
2008-01-31 00:00:00.000 ASSET_04 1350          19             90          135
==== */
GO

&lt;/pre&gt;
&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:2d6788fa-f0bb-4042-a2bc-6172a8115516" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati tags: &lt;a href="http://technorati.com/tags/SQL" rel="tag"&gt;SQL&lt;/a&gt;, &lt;a href="http://technorati.com/tags/SQL%20Server" rel="tag"&gt;SQL Server&lt;/a&gt;, &lt;a href="http://technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;&lt;/div&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/62.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2008/04/14/overcoming-sql-group-by-challenges-with-row_number.aspx</guid>
            <pubDate>Mon, 14 Apr 2008 19:35:46 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/62.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2008/04/14/overcoming-sql-group-by-challenges-with-row_number.aspx#feedback</comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/62.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/62.aspx</trackback:ping>
        </item>
        <item>
            <title>Required Fields with Linked SQL Servers?</title>
            <link>http://nimblecoder.com/blog/archive/2007/10/12/required-fields-with-linked-sql-servers.aspx</link>
            <description>&lt;p&gt;I was setting up a linked server from my web database to my local database to ease the backup procedure. Currently I have a script which I run to copy all of the database from the web database to my local database. I thought I could simply use &lt;a href="http://msdn2.microsoft.com/en-us/library/ms190479.aspx" target="_blank"&gt;sp_addlinkedserver&lt;/a&gt; to conect the SQL Servers. My initial attempts to connect the servers was rebuffed:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;-- Try 1 (failed)&lt;/span&gt;
&lt;span class="kwrd"&gt;EXEC&lt;/span&gt; sp_addlinkedserver 
     @server        = &lt;span class="str"&gt;'subtext_web'&lt;/span&gt;
    ,@srvproduct    = &lt;span class="str"&gt;'SQL Server'&lt;/span&gt;
    ,@datasrc       = &lt;span class="str"&gt;'some.sql-web-host.com'&lt;/span&gt;
    ,@&lt;span class="kwrd"&gt;catalog&lt;/span&gt;       = &lt;span class="str"&gt;'some-db'&lt;/span&gt;

&lt;span class="rem"&gt;-- Results in the following error message:&lt;/span&gt;
&lt;span class="rem"&gt;--   Msg 15426, Level 16, State 1, Procedure sp_addlinkedserver, Line 20&lt;/span&gt;
&lt;span class="rem"&gt;--   You must specify a provider name with this set of properties.&lt;/span&gt;
&lt;span class="kwrd"&gt;GO&lt;/span&gt;


&lt;span class="rem"&gt;-- Try 2 (failed)&lt;/span&gt;
&lt;span class="kwrd"&gt;EXEC&lt;/span&gt; sp_addlinkedserver 
     @server        = &lt;span class="str"&gt;'subtext_web'&lt;/span&gt;
    ,@srvproduct    = &lt;span class="str"&gt;'SQL Server'&lt;/span&gt;
    ,@provider      = &lt;span class="str"&gt;'SQLNCLI'&lt;/span&gt;
    ,@datasrc       = &lt;span class="str"&gt;'some.sql-web-host.com'&lt;/span&gt;
    ,@&lt;span class="kwrd"&gt;catalog&lt;/span&gt;       = &lt;span class="str"&gt;'some-db'&lt;/span&gt;

&lt;span class="rem"&gt;-- Results in the following error message:&lt;/span&gt;
&lt;span class="rem"&gt;--   Msg 15428, Level 16, State 1, Procedure sp_addlinkedserver, Line 37&lt;/span&gt;
&lt;span class="rem"&gt;--   You cannot specify a provider or any properties for product 'SQL Server'.&lt;/span&gt;
&lt;span class="kwrd"&gt;GO&lt;/span&gt;


&lt;span class="rem"&gt;-- Try 3 (succeeded)&lt;/span&gt;
&lt;span class="kwrd"&gt;EXEC&lt;/span&gt; sp_addlinkedserver 
     @server        = &lt;span class="str"&gt;'subtext_web'&lt;/span&gt;
    ,@srvproduct    = &lt;span class="str"&gt;''&lt;/span&gt;
    ,@provider      = &lt;span class="str"&gt;'SQLNCLI'&lt;/span&gt;
    ,@datasrc       = &lt;span class="str"&gt;'some.sql-web-host.com'&lt;/span&gt;
    ,@&lt;span class="kwrd"&gt;catalog&lt;/span&gt;       = &lt;span class="str"&gt;'some-db'&lt;/span&gt;

&lt;span class="rem"&gt;-- Results in the following message:&lt;/span&gt;
&lt;span class="rem"&gt;--   Command(s) completed successfully.&lt;/span&gt;
&lt;span class="kwrd"&gt;GO&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I read the documentation on sp_addlinkedserver and there was a footnote on the use of @srvproduct = 'SQL Server':&lt;/p&gt;
&lt;blockquote&gt;This way of setting up a linked server forces the name of the linked server to be the same as the network name of the remote instance of SQL Server. Use data_source to specify the server.&lt;/blockquote&gt;
&lt;p&gt;Since I was successful in Try 3, it wasn't a big deal but the error messages in Try 1 and Try 2 seemed to contradict each other since the error message in Try 1 specifically required a provider.&lt;/p&gt;
&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:ccadea5a-505a-4f0d-acbc-87c4d0b85bac" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati tags: &lt;a href="http://technorati.com/tags/SQL%20Server" rel="tag"&gt;SQL Server&lt;/a&gt;&lt;/div&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/41.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2007/10/12/required-fields-with-linked-sql-servers.aspx</guid>
            <pubDate>Fri, 12 Oct 2007 22:01:13 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/41.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2007/10/12/required-fields-with-linked-sql-servers.aspx#feedback</comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/41.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/41.aspx</trackback:ping>
        </item>
        <item>
            <title>Percentage Normalization in SQL</title>
            <link>http://nimblecoder.com/blog/archive/2007/09/17/percentage-normalization-in-sql.aspx</link>
            <description>&lt;p&gt;I had a little fun with SQL Server last week trying to normalize a gas composition function that was returning a molar percentage of the gas. During testing we found several cases where the sum of the components did not add up to 100.0%. Of course due to floating point inaccuracies it is quite possible that rounding errors could also contribute to the issue. The values we were seeing were a couple of percentage points different and so we decided to normalize the data to ensure that it added up to 100.0%. At the same time, I rounded the components (to two decimal places in molar %) since there are contractual documents specifying the precision that will be used during final calculation.&lt;/p&gt; &lt;p&gt;One interesting point is that if you simply round the components after the summation, the total percentage may not be 100% either. Here is an example using purely theoretical values:&lt;/p&gt; &lt;table style="width: auto; font-family: lucida console,courier new,fixed" cellspacing="0" cellpadding="2" border="1"&gt; &lt;colgroup&gt; &lt;col /&gt; &lt;col align="right" /&gt; &lt;col align="right" /&gt;&lt;/colgroup&gt; &lt;tbody&gt; &lt;tr&gt; &lt;th&gt;Component&lt;/th&gt; &lt;th&gt;Actual&lt;/th&gt; &lt;th&gt;Rounded&lt;/th&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;C1&lt;/td&gt; &lt;td&gt;10.955556&lt;/td&gt; &lt;td&gt;10.96&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;C2&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;C3&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;iC4&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;nC4&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;iC5&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;nC5&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;C6+&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;N2&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;CO2&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;O2&lt;/td&gt; &lt;td&gt;8.904444&lt;/td&gt; &lt;td&gt;8.90&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Total&lt;/td&gt; &lt;td&gt;100.000000&lt;/td&gt; &lt;td&gt;99.96&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;To account for this, I rounded all of the values except the final (chosen) component and then applied the remainder to that component. So the final normalization and rounding might look like this:&lt;/p&gt; &lt;table style="width: auto; font-family: lucida console,courier new,fixed" cellspacing="0" cellpadding="2" border="1"&gt; &lt;colgroup&gt; &lt;col /&gt; &lt;col align="right" /&gt; &lt;col align="right" /&gt;&lt;/colgroup&gt; &lt;tbody&gt; &lt;tr&gt; &lt;th&gt;Component&lt;/th&gt; &lt;th&gt;Actual&lt;/th&gt; &lt;th&gt;Normalized&lt;/th&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;C1&lt;/td&gt; &lt;td&gt;94.719559&lt;/td&gt; &lt;td&gt;95.00&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;C2&lt;/td&gt; &lt;td&gt;3.579182&lt;/td&gt; &lt;td&gt;3.59&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;C3&lt;/td&gt; &lt;td&gt;0.923634&lt;/td&gt; &lt;td&gt;0.93&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;iC4&lt;/td&gt; &lt;td&gt;0.343786&lt;/td&gt; &lt;td&gt;0.34&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;nC4&lt;/td&gt; &lt;td&gt;0.050411&lt;/td&gt; &lt;td&gt;0.05&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;iC5&lt;/td&gt; &lt;td&gt;0.050411&lt;/td&gt; &lt;td&gt;0.05&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;nC5&lt;/td&gt; &lt;td&gt;0.000000&lt;/td&gt; &lt;td&gt;0.00&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;C6+&lt;/td&gt; &lt;td&gt;0.000000&lt;/td&gt; &lt;td&gt;0.00&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;N2&lt;/td&gt; &lt;td&gt;0.040948&lt;/td&gt; &lt;td&gt;0.04&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;CO2&lt;/td&gt; &lt;td&gt;0.000000&lt;/td&gt; &lt;td&gt;0.00&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;O2&lt;/td&gt; &lt;td&gt;0.000000&lt;/td&gt; &lt;td&gt;0.00&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Total&lt;/td&gt; &lt;td&gt;99.707930&lt;/td&gt; &lt;td&gt;100.00&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;IF&lt;/span&gt; (ABS(100.0 - @sumComponents) &amp;lt;= @threshold)
&lt;span class="kwrd"&gt;BEGIN&lt;/span&gt;
    &lt;span class="kwrd"&gt;SET&lt;/span&gt; @diff = 100.0 - @sumComponents
    &lt;span class="kwrd"&gt;SET&lt;/span&gt; @factor = 1.0 + (@diff / 100.0)
&lt;span class="kwrd"&gt;END&lt;/span&gt;
&lt;span class="kwrd"&gt;ELSE&lt;/span&gt;
&lt;span class="kwrd"&gt;BEGIN&lt;/span&gt;
    &lt;span class="rem"&gt;-- No correction factor&lt;/span&gt;
    &lt;span class="kwrd"&gt;SET&lt;/span&gt; @factor = 1.0
&lt;span class="kwrd"&gt;END&lt;/span&gt;

&lt;span class="rem"&gt;-- Round the components to 2 decimal places for molar percent&lt;/span&gt;
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt;  @C2  = ROUND(@C2  * @factor, 2)
       ,@C3  = ROUND(@C3  * @factor, 2)
       ,@iC4 = ROUND(@iC4 * @factor, 2)
       ,@nC4 = ROUND(@nC4 * @factor, 2)
       ,@iC5 = ROUND(@iC5 * @factor, 2)
       ,@nC5 = ROUND(@nC5 * @factor, 2)
       ,@C6  = ROUND(@C6  * @factor, 2)
       ,@N2  = ROUND(@N2  * @factor, 2)
       ,@CO2 = ROUND(@CO2 * @factor, 2)
       ,@O2  = ROUND(@O2  * @factor, 2)

&lt;span class="rem"&gt;-- Apply error/remainder to methane&lt;/span&gt;
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @diff = ROUND(@C2 + @C3 + @iC4 + @nC4 + @iC5 + @nC5 + @C6 + @N2 + @CO2 + @O2, 2)
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @C1   = ROUND(100.0 - @diff, 2)

&lt;span class="rem"&gt;-- Verify total&lt;/span&gt;
&lt;span class="kwrd"&gt;SELECT&lt;/span&gt;  @sumComponents = (@C1 + @C2 + @C3 + @iC4 + @nC4 + @iC5 + @nC5 + @C6 + @N2 + @CO2 + @O2)

&lt;/pre&gt;
&lt;p&gt;Technorati tags: &lt;a href="http://technorati.com/tag/SQL" rel="tag"&gt;SQL&lt;/a&gt;&lt;/p&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/39.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2007/09/17/percentage-normalization-in-sql.aspx</guid>
            <pubDate>Mon, 17 Sep 2007 16:40:30 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/39.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2007/09/17/percentage-normalization-in-sql.aspx#feedback</comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/39.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/39.aspx</trackback:ping>
        </item>
        <item>
            <title>Blog Maintenance Is A Pain</title>
            <link>http://nimblecoder.com/blog/archive/2007/08/01/blog-maintenance-is-a-pain.aspx</link>
            <description>&lt;p&gt;I must be getting tired... Once again I almost damaged my blog trying to get a pristine local copy. This time I decided that it would be easiest to export the blog as BlogML and reload it into the local web application. However I didn't notice that when I first exported the blog, it generated an error (I think due to the "Embed Attachments" option) in the export so I regenerated the export without embedded attachments. When I went to import the BlogML, somehow I didn't notice that it was connected to my real blog and not my local blog (like I said it is getting late!) and imported a duplicate of the information I had already created. At least this time I was able to cover my tracks without incident. Here is the SQL I used to clean up the database:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;-- I determined the last valid entry was ID = 34&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;-- so I removed entries after 34 and reseeded the&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;-- identity column.&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;DELETE&lt;/span&gt;    subtext_Links&lt;br /&gt;&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;    PostID &lt;span class="kwrd"&gt;IN&lt;/span&gt; (&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt;    ID&lt;br /&gt;    &lt;span class="kwrd"&gt;FROM&lt;/span&gt;    subtext_Content&lt;br /&gt;    &lt;span class="kwrd"&gt;WHERE&lt;/span&gt;    ID &amp;gt; 34&lt;br /&gt;)&lt;br /&gt;&lt;span class="kwrd"&gt;GO&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;DELETE&lt;/span&gt;    subtext_Feedback&lt;br /&gt;&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;    EntryID &lt;span class="kwrd"&gt;IN&lt;/span&gt; (&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt;    ID&lt;br /&gt;    &lt;span class="kwrd"&gt;FROM&lt;/span&gt;    subtext_Content&lt;br /&gt;    &lt;span class="kwrd"&gt;WHERE&lt;/span&gt;    ID &amp;gt; 34&lt;br /&gt;)&lt;br /&gt;&lt;span class="kwrd"&gt;GO&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;DELETE&lt;/span&gt;    subtext_Content&lt;br /&gt;&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;    ID &amp;gt; 34&lt;br /&gt;&lt;span class="kwrd"&gt;GO&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;DBCC&lt;/span&gt; CHECKIDENT (&lt;span class="str"&gt;'dbo.subtext_Content'&lt;/span&gt;, RESEED, 34)&lt;br /&gt;&lt;span class="kwrd"&gt;GO&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;I had to recheck the documentation on &lt;a href="http://msdn2.microsoft.com/en-us/library/ms176057.aspx"&gt;CHECKIDENT&lt;/a&gt; just to make sure that I should use the maximum value (34) and not the next value (35). According to the documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Current identity value is set to the &lt;em&gt;new_reseed_value&lt;/em&gt;. If no rows have been inserted to the table since it was created, the first row inserted after you run DBCC CHECKIDENT uses &lt;em&gt;new_reseed_value&lt;/em&gt; as the identity. Otherwise, the next row inserted uses &lt;em&gt;new_reseed_value&lt;/em&gt; + the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms189795.aspx"&gt;current increment&lt;/a&gt; value.&lt;/p&gt;
&lt;/blockquote&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/35.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2007/08/01/blog-maintenance-is-a-pain.aspx</guid>
            <pubDate>Wed, 01 Aug 2007 05:34:38 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/35.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2007/08/01/blog-maintenance-is-a-pain.aspx#feedback</comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/35.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/35.aspx</trackback:ping>
        </item>
        <item>
            <title>When It Rains It Pours</title>
            <link>http://nimblecoder.com/blog/archive/2007/07/31/when-it-rains-it-pours.aspx</link>
            <description>&lt;p&gt;&lt;a href="http://nimblecoder.com/blog/images/nimblecoder_com/blog/WindowsLiveWriter/WhenItRainsItPours_65DF/Subtext_Eport_01%5B5%5D.png" atomicselection="true"&gt;&lt;img height="512" src="http://nimblecoder.com/blog/images/nimblecoder_com/blog/WindowsLiveWriter/WhenItRainsItPours_65DF/Subtext_Eport_01_thumb%5B3%5D.png" width="538" align="right" /&gt;&lt;/a&gt;Aside from the fact Houston has been getting a &lt;a href="http://weatherblog.abc13.com/2007/07/rain-and-record.html"&gt;significant amount of rain&lt;/a&gt; this year, it seems that work and personal projects are also experiencing their share of challenges. I wanted to adjust the skin of my blog the day before yesterday and I wanted to try it out offline on a local installation. I also wanted to make a full backup of the database which served multiple purposes including being able to test changes locally. I had made a copy of the data previously using the SQL Server 2005 Export Data DTS mechanism, but it did not contain the views, stored procedures, or relationships. &lt;/p&gt; &lt;p&gt;So I embarked on a task to make a complete local copy of my database since it is stored online through web hosting. Since my current project is keeping my quite busy, I had to start the process late at night and, in hind sight, perhaps that was not wise. Try as I might, I could not convince the Export mechanism to reorder the table transfer sequence to match the required dependencies on the tables. I tried multiple times but I could not find a way to specify the order of the data transfer. Since some of the table required identity insert, I also tried to extract the actual SQL used to perform the transfer (it was late at night and I didn't want to spend the time to do it by hand) but that was unsuccessful as well.&lt;/p&gt; &lt;p&gt;&lt;a href="http://nimblecoder.com/blog/images/nimblecoder_com/blog/WindowsLiveWriter/WhenItRainsItPours_65DF/Subtext_Eport_02%5B17%5D.png" atomicselection="true"&gt;&lt;img height="301" src="http://nimblecoder.com/blog/images/nimblecoder_com/blog/WindowsLiveWriter/WhenItRainsItPours_65DF/Subtext_Eport_02_thumb%5B11%5D.png" width="454" align="left" /&gt;&lt;/a&gt; Unfortunately, during one of my failed attempts to export the data I accidentally deleted the very data I was trying to export. It was late at night and I was not a happy camper. To top it off, after this I couldn't get to sleep either because I was so bothered by deleting the data.&lt;/p&gt; &lt;p&gt; Fortunately, I was able to recover the data and all was not lost however it did result in downtime of my blog for a day before I could restore the data. It does seem that I will be writing the data transfer SQL by hand since the SQL Server Export tool is doing such a poor job.&lt;/p&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/34.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2007/07/31/when-it-rains-it-pours.aspx</guid>
            <pubDate>Tue, 31 Jul 2007 19:50:58 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/34.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2007/07/31/when-it-rains-it-pours.aspx#feedback</comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/34.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/34.aspx</trackback:ping>
        </item>
        <item>
            <title>Bilinear Interpolation in SQL Server</title>
            <link>http://nimblecoder.com/blog/archive/2007/06/19/bilinear-interpolation-in-sql-server.aspx</link>
            <description>&lt;p&gt;The past several days I have been wrapped in relatively complex calculations in SQL Server. The process involves gas composition data from a time-series historian that is weighted by flow-rate and averaged. Then the composition is used for several lookup tables for volume correction based on the gas mixture and temperature. The lookup tables are similar to the following structure:&lt;/p&gt;
&lt;table cellspacing="0" cellpadding="0" border="1" style="font-family: courier new,fixed;"&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th&gt;Molecular&lt;br /&gt;
            Mass of&lt;br /&gt;
            Mixture &lt;/th&gt;
            &lt;th&gt;-150°C &lt;/th&gt;
            &lt;th&gt;-154°C &lt;/th&gt;
            &lt;th&gt;-158°C &lt;/th&gt;
            &lt;th&gt;-160°C &lt;/th&gt;
            &lt;th&gt;-162°C &lt;/th&gt;
            &lt;th&gt;-166°C &lt;/th&gt;
            &lt;th&gt;-170°C &lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;th&gt;16.0 &lt;/th&gt;
            &lt;td align="right"&gt;-0.000012 &lt;/td&gt;
            &lt;td align="right"&gt;-0.000010 &lt;/td&gt;
            &lt;td align="right"&gt;-0.000009 &lt;/td&gt;
            &lt;td align="right"&gt;-0.000009 &lt;/td&gt;
            &lt;td align="right"&gt;-0.000008 &lt;/td&gt;
            &lt;td align="right"&gt;-0.000007 &lt;/td&gt;
            &lt;td align="right"&gt;-0.000007 &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;th&gt;16.5 &lt;/th&gt;
            &lt;td align="right"&gt;0.000135 &lt;/td&gt;
            &lt;td align="right"&gt;0.000118 &lt;/td&gt;
            &lt;td align="right"&gt;0.000106 &lt;/td&gt;
            &lt;td align="right"&gt;0.000100 &lt;/td&gt;
            &lt;td align="right"&gt;0.000094 &lt;/td&gt;
            &lt;td align="right"&gt;0.000086 &lt;/td&gt;
            &lt;td align="right"&gt;0.000078 &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;th&gt;17.0 &lt;/th&gt;
            &lt;td align="right"&gt;0.000282 &lt;/td&gt;
            &lt;td align="right"&gt;0.000245 &lt;/td&gt;
            &lt;td align="right"&gt;0.000221 &lt;/td&gt;
            &lt;td align="right"&gt;0.000209 &lt;/td&gt;
            &lt;td align="right"&gt;0.000197 &lt;/td&gt;
            &lt;td align="right"&gt;0.000179 &lt;/td&gt;
            &lt;td align="right"&gt;0.000163 &lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The algorithm tries to find the closest &lt;em&gt;x&lt;/em&gt; and &lt;em&gt;y&lt;/em&gt; values to the supplied parameters. If any of the parameters match the lookup table, then standard linear interpolation can be used or if both parameters match then no computation is necessary. The &lt;a href="http://en.wikipedia.org/wiki/Bilinear_interpolation"&gt;bilinear interpolation&lt;/a&gt; is used for the &lt;em&gt;x&lt;/em&gt; and &lt;em&gt;y&lt;/em&gt; parameters and then between the intermediate &lt;em&gt;x&lt;/em&gt; and &lt;em&gt;y&lt;/em&gt; interpolated values.&lt;/p&gt;
&lt;p&gt;The SQL lookup table is defined as follows and I created a quick script to generate INSERT statements from the Excel spreadsheet source:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;CREATE&lt;/span&gt; &lt;span class="kwrd"&gt;TABLE&lt;/span&gt; [dbo].[K1_Lookup](&lt;br /&gt;     X          &lt;span class="kwrd"&gt;float&lt;/span&gt;           &lt;span class="kwrd"&gt;NOT&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;        &lt;span class="rem"&gt;-- Molecular mass of mixture (Xi * Mi)&lt;/span&gt;&lt;br /&gt;    ,Y          &lt;span class="kwrd"&gt;float&lt;/span&gt;           &lt;span class="kwrd"&gt;NOT&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;        &lt;span class="rem"&gt;-- Temperature, degC&lt;/span&gt;&lt;br /&gt;    ,Z          &lt;span class="kwrd"&gt;float&lt;/span&gt;           &lt;span class="kwrd"&gt;NOT&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;        &lt;span class="rem"&gt;-- Volume correction factor, m^3/kmol&lt;/span&gt;&lt;br /&gt;)&lt;/pre&gt;
&lt;p&gt;The algorithm to find the lookup values is crude in SQL. I used MAX where the value is less than or equal to the coordinate. That is: find the maximum coordinate value that is less than or equal to the desired coordinate. Please pardon the use of GOTO; I prefer not to use it but it was used specifically for error handling and validation. Since the &lt;/p&gt;
&lt;pre class="csharpcode"&gt;    &lt;span class="rem"&gt;-- The lookup table is available in: @DataValues&lt;/span&gt;&lt;br /&gt;    &lt;span class="rem"&gt;-- Get the X variable position&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;DECLARE&lt;/span&gt;  @X0 &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;            ,@X1 &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @X0 = &lt;span class="kwrd"&gt;MAX&lt;/span&gt;(X) &lt;span class="kwrd"&gt;FROM&lt;/span&gt; @DataValues &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; X &amp;lt;= @XValue&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @X1 = &lt;span class="kwrd"&gt;MIN&lt;/span&gt;(X) &lt;span class="kwrd"&gt;FROM&lt;/span&gt; @DataValues &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; X &amp;gt;= @XValue&lt;br /&gt;    &lt;span class="kwrd"&gt;IF&lt;/span&gt; (@X0 &lt;span class="kwrd"&gt;IS&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;) &lt;span class="kwrd"&gt;OR&lt;/span&gt; (@X1 &lt;span class="kwrd"&gt;IS&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;) &lt;span class="kwrd"&gt;RETURN&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;-- Get the Y variable position    &lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;DECLARE&lt;/span&gt;  @Y0 &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;            ,@Y1 &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Y0 = &lt;span class="kwrd"&gt;MAX&lt;/span&gt;(Y) &lt;span class="kwrd"&gt;FROM&lt;/span&gt; @DataValues &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; Y &amp;lt;= @YValue&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Y1 = &lt;span class="kwrd"&gt;MIN&lt;/span&gt;(Y) &lt;span class="kwrd"&gt;FROM&lt;/span&gt; @DataValues &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; Y &amp;gt;= @YValue&lt;br /&gt;    &lt;span class="kwrd"&gt;IF&lt;/span&gt; (@Y0 &lt;span class="kwrd"&gt;IS&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;) &lt;span class="kwrd"&gt;OR&lt;/span&gt; (@Y1 &lt;span class="kwrd"&gt;IS&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;) &lt;span class="kwrd"&gt;RETURN&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;-- Get the Z variable position    &lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;DECLARE&lt;/span&gt;  @Z00 &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;            ,@Z01 &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;            ,@Z10 &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;            ,@Z11 &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Z00 = Z &lt;span class="kwrd"&gt;FROM&lt;/span&gt; @DataValues &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; Y = @Y0 &lt;span class="kwrd"&gt;AND&lt;/span&gt; X = @X0&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Z01 = Z &lt;span class="kwrd"&gt;FROM&lt;/span&gt; @DataValues &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; Y = @Y0 &lt;span class="kwrd"&gt;AND&lt;/span&gt; X = @X1&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Z10 = Z &lt;span class="kwrd"&gt;FROM&lt;/span&gt; @DataValues &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; Y = @Y1 &lt;span class="kwrd"&gt;AND&lt;/span&gt; X = @X0&lt;br /&gt;    &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Z11 = Z &lt;span class="kwrd"&gt;FROM&lt;/span&gt; @DataValues &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; Y = @Y1 &lt;span class="kwrd"&gt;AND&lt;/span&gt; X = @X1&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;IF&lt;/span&gt; (@X0 = @X1) &lt;span class="kwrd"&gt;AND&lt;/span&gt; (@Y0 = @Y1)&lt;br /&gt;    &lt;span class="kwrd"&gt;BEGIN&lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;-- Exact match for X and Y look-up values&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Result = @Z00&lt;br /&gt;    &lt;span class="kwrd"&gt;END&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;ELSE&lt;/span&gt; &lt;span class="kwrd"&gt;IF&lt;/span&gt; (@X0 = @X1)&lt;br /&gt;    &lt;span class="kwrd"&gt;BEGIN&lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;-- Exact match for X look-up value; find Y value&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Result = @Z10 + (@Z11-@Z10)/(@Y1-@Y0)*(@YValue-@Y0)&lt;br /&gt;    &lt;span class="kwrd"&gt;END&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;ELSE&lt;/span&gt; &lt;span class="kwrd"&gt;IF&lt;/span&gt; (@Y0 = @Y1)&lt;br /&gt;    &lt;span class="kwrd"&gt;BEGIN&lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;-- Exact match for Y look-up value; find X value&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Result = @Z00 + (@Z01-@Z00)/(@X1-@X0)*(@XValue-@X0)&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;END&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;ELSE&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;BEGIN&lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;-- Full Bilinear interpolation; determine Z position&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;DECLARE&lt;/span&gt;  @FA &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;                ,@FB &lt;span class="kwrd"&gt;FLOAT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @FA = @Z00 + (@Z01-@Z00)/(@X1-@X0)*(@XValue-@X0)&lt;br /&gt;        &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @FB = @Z10 + (@Z11-@Z10)/(@X1-@X0)*(@XValue-@X0)&lt;br /&gt;        &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @Result = @FA + (@FB-@FA)/(@Y1-@Y0)*(@YValue-@Y0)&lt;br /&gt;    &lt;span class="kwrd"&gt;END&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;RETURN&lt;/span&gt; @Result&lt;/pre&gt;
&lt;p&gt;This is one area where I am keenly interested to compare the implementation and performance in a SQL based function or stored procedure compared to a CLR-based solution. Hopefully I will get some time to create a CLR-based solution and to compare the two solutions soon.&lt;/p&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/30.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2007/06/19/bilinear-interpolation-in-sql-server.aspx</guid>
            <pubDate>Tue, 19 Jun 2007 17:17:05 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/30.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2007/06/19/bilinear-interpolation-in-sql-server.aspx#feedback</comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/30.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/30.aspx</trackback:ping>
        </item>
        <item>
            <title>Changing table owners in SQL Server</title>
            <link>http://nimblecoder.com/blog/archive/2007/06/03/Changing-table-owners-in-SQL-Server.aspx</link>
            <description>&lt;p&gt;I recently installed some tables via SQL Query Analyzer under a user id that was not 'dbo'. I needed to change the owner on the tables and wrote this simple query to generate the execute statements to change the owner. Run the query to generate the next query -- so you can review the list prior to running it.&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt;  &lt;span class="str"&gt;'exec sp_changeobjectowner '&lt;/span&gt;&lt;span class="str"&gt;''&lt;/span&gt; + name + &lt;span class="str"&gt;''&lt;/span&gt;&lt;span class="str"&gt;', '&lt;/span&gt;&lt;span class="str"&gt;'dbo'&lt;/span&gt;&lt;span class="str"&gt;''&lt;/span&gt;
&lt;span class="kwrd"&gt;FROM&lt;/span&gt;    sysobjects
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;   type = &lt;span class="str"&gt;'U'&lt;/span&gt;
    &lt;span class="kwrd"&gt;AND&lt;/span&gt; uid != 1
&lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; name&lt;/pre&gt;
&lt;p&gt;The result looks something like:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;exec&lt;/span&gt; sp_changeobjectowner &lt;span class="str"&gt;'asset'&lt;/span&gt;, &lt;span class="str"&gt;'dbo'&lt;/span&gt;
&lt;span class="kwrd"&gt;exec&lt;/span&gt; sp_changeobjectowner &lt;span class="str"&gt;'project'&lt;/span&gt;, &lt;span class="str"&gt;'dbo'&lt;/span&gt;
&lt;span class="kwrd"&gt;exec&lt;/span&gt; sp_changeobjectowner &lt;span class="str"&gt;'salesorder'&lt;/span&gt;, &lt;span class="str"&gt;'dbo'&lt;/span&gt;
&lt;/pre&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/26.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2007/06/03/Changing-table-owners-in-SQL-Server.aspx</guid>
            <pubDate>Sun, 03 Jun 2007 19:34:54 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/26.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2007/06/03/Changing-table-owners-in-SQL-Server.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/26.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/26.aspx</trackback:ping>
        </item>
        <item>
            <title>Altering SQL Server</title>
            <link>http://nimblecoder.com/blog/archive/2007/04/04/Altering-SQL-Server.aspx</link>
            <description>&lt;p&gt;I was working on a project last week where I needed to update the columns and data types in tables in SQL Server. It is pretty quick to do this manually such as:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;ALTER&lt;/span&gt; &lt;span class="kwrd"&gt;TABLE&lt;/span&gt; orders
&lt;span class="kwrd"&gt;    ALTER&lt;/span&gt; &lt;span class="kwrd"&gt;COLUMN&lt;/span&gt; orderid &lt;span class="kwrd"&gt;varchar(36)&lt;/span&gt; &lt;span class="kwrd"&gt;NOT&lt;/span&gt; NULL&lt;/pre&gt;
&lt;p&gt;I prefer this compared to the code SQL Server generates in the change script which typically renames the existing table, creates a new table, copies the data from the previous table to the new table, and then restores indices and keys. While that is appropriate for dramatic changes, it is overkill for something of this nature.&lt;/p&gt;
&lt;p&gt;Since I love using tools and scripting, I decided to make a small Python program which would let me just specify which tables and columns to modify. This simplifies maintenance as I only have to manage the tables and columns rather than reading the entire SQL script looking for missing columns or keys.&lt;/p&gt;
&lt;p&gt;While I was developing the Python script, I wanted to add enough SQL to avoid obvious errors such as trying to add duplicate primary keys or trying to alter columns that are already NOT NULL. I used MSDN and looked at some of the generated SQL to get the following templates:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;IF&lt;/span&gt; &lt;span class="kwrd"&gt;EXISTS&lt;/span&gt; (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; * &lt;span class="kwrd"&gt;FROM&lt;/span&gt; dbo.sysobjects &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; id = OBJECT_ID(&lt;span class="str"&gt;'orders'&lt;/span&gt;) &lt;span class="kwrd"&gt;AND&lt;/span&gt; COLUMNPROPERTY(id, &lt;span class="str"&gt;'orderid'&lt;/span&gt;, &lt;span class="str"&gt;'AllowsNull'&lt;/span&gt;) = 1)
&lt;span class="kwrd"&gt;BEGIN&lt;/span&gt;
    &lt;span class="kwrd"&gt;PRINT&lt;/span&gt; &lt;span class="str"&gt;'Altering column: orders.orderid'&lt;/span&gt;
    &lt;span class="kwrd"&gt;ALTER&lt;/span&gt; &lt;span class="kwrd"&gt;TABLE&lt;/span&gt; orders
        &lt;span class="kwrd"&gt;ALTER&lt;/span&gt; &lt;span class="kwrd"&gt;COLUMN&lt;/span&gt; orderid &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(36) &lt;span class="kwrd"&gt;NOT&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;
&lt;span class="kwrd"&gt;END&lt;/span&gt;
&lt;span class="kwrd"&gt;GO&lt;/span&gt;

&lt;span class="kwrd"&gt;IF&lt;/span&gt; &lt;span class="kwrd"&gt;EXISTS&lt;/span&gt; (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; * &lt;span class="kwrd"&gt;FROM&lt;/span&gt; dbo.sysobjects &lt;span class="kwrd"&gt;WHERE&lt;/span&gt; id = OBJECT_ID(&lt;span class="str"&gt;'orders'&lt;/span&gt;) &lt;span class="kwrd"&gt;AND&lt;/span&gt; OBJECTPROPERTY(id, &lt;span class="str"&gt;'TableHasPrimaryKey'&lt;/span&gt;) = 0)
&lt;span class="kwrd"&gt;BEGIN&lt;/span&gt;
    &lt;span class="kwrd"&gt;PRINT&lt;/span&gt; &lt;span class="str"&gt;'Adding primary key: orders.orderid'&lt;/span&gt;
    &lt;span class="kwrd"&gt;ALTER&lt;/span&gt; &lt;span class="kwrd"&gt;TABLE&lt;/span&gt; orders
        &lt;span class="kwrd"&gt;ADD&lt;/span&gt; &lt;span class="kwrd"&gt;CONSTRAINT&lt;/span&gt; PK_orders
            &lt;span class="kwrd"&gt;PRIMARY&lt;/span&gt; &lt;span class="kwrd"&gt;KEY&lt;/span&gt; (orderid)
&lt;span class="kwrd"&gt;END&lt;/span&gt;
&lt;span class="kwrd"&gt;GO&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;So the Python script used the string templates to write the output SQL and it looked like:&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span class="str"&gt;"""Generate a T-SQL script to set the appropriate NOT NULL
columns and PRIMARY KEY indices for the database.

TODO: Create indices for specific columns as well
"""&lt;/span&gt;

&lt;span class="kwrd"&gt;import&lt;/span&gt; string

&lt;span class="rem"&gt;# database properties&lt;/span&gt;

tables = {
    &lt;span class="str"&gt;"orders"&lt;/span&gt;:           { &lt;span class="str"&gt;'notnull'&lt;/span&gt;: [(&lt;span class="str"&gt;'orderid'&lt;/span&gt;, &lt;span class="str"&gt;'varchar(36)'&lt;/span&gt;)],
                          &lt;span class="str"&gt;'pk'&lt;/span&gt;     : [&lt;span class="str"&gt;'orderid'&lt;/span&gt;] },

    &lt;span class="str"&gt;"accounts"&lt;/span&gt;:         { &lt;span class="str"&gt;'notnull'&lt;/span&gt;: [(&lt;span class="str"&gt;'accountid'&lt;/span&gt;, &lt;span class="str"&gt;'varchar(50)'&lt;/span&gt;),
                                      (&lt;span class="str"&gt;'name'&lt;/span&gt;, &lt;span class="str"&gt;'varchar(100)'&lt;/span&gt;)],
                          &lt;span class="str"&gt;'pk'&lt;/span&gt;     : [&lt;span class="str"&gt;'accountid'&lt;/span&gt;] },

    &lt;span class="rem"&gt;# More tables and columns...&lt;/span&gt;
    }

&lt;span class="rem"&gt;# Functions&lt;/span&gt;

def generatenotnull(table, column, datatype):
    print alternotnull % { &lt;span class="str"&gt;'table'&lt;/span&gt;:table, &lt;span class="str"&gt;'column'&lt;/span&gt;:column, &lt;span class="str"&gt;'datatype'&lt;/span&gt;:datatype }

def generatepk(table, columns):
    print alterpk % { &lt;span class="str"&gt;'table'&lt;/span&gt;:table, &lt;span class="str"&gt;'columns'&lt;/span&gt;:&lt;span class="kwrd"&gt;string&lt;/span&gt;.join(columns, &lt;span class="str"&gt;'"'&lt;/span&gt;) }

# SQL statements and formats

alternotnull = &lt;span class="str"&gt;"""IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID('%(table)s') AND COLUMNPROPERTY(id, '%(column)s', 'AllowsNull') = 1)
BEGIN
    PRINT 'Altering nullable column: %(table)s.%(column)s'
    ALTER TABLE %(table)s
        ALTER COLUMN %(column)s %(datatype)s NOT NULL
END
GO"""&lt;/span&gt;

alterpk = &lt;span class="str"&gt;"""IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID('%(table)s') AND OBJECTPROPERTY(id, 'TableHasPrimaryKey') = 0)
BEGIN
    PRINT 'Adding primary key: %(table)s.%(columns)s'
    ALTER TABLE %(table)s
        ADD CONSTRAINT PK_%(table)s
            PRIMARY KEY (%(columns)s)
END
GO"""&lt;/span&gt;


&lt;span class="rem"&gt;# Main entry&lt;/span&gt;
&lt;span class="kwrd"&gt;if&lt;/span&gt; __name__ == &lt;span class="str"&gt;"__main__"&lt;/span&gt;:
    &lt;span class="kwrd"&gt;for&lt;/span&gt; table &lt;span class="kwrd"&gt;in&lt;/span&gt; tables:
        print &lt;span class="str"&gt;"/* ==== Processing %s ==== */"&lt;/span&gt; % table
        tabledict = tables[table]
        &lt;span class="kwrd"&gt;for&lt;/span&gt; column &lt;span class="kwrd"&gt;in&lt;/span&gt; tabledict[&lt;span class="str"&gt;'notnull'&lt;/span&gt;]:
            generatenotnull(table, column[0], column[1])
        &lt;span class="rem"&gt;# TODO: Allow for compound primary keys&lt;/span&gt;
        generatepk(table, tabledict[&lt;span class="str"&gt;'pk'&lt;/span&gt;])
        print &lt;span class="str"&gt;""&lt;/span&gt;
&lt;/pre&gt;&lt;img src="http://nimblecoder.com/blog/aggbug/17.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ryan Van Slooten</dc:creator>
            <guid>http://nimblecoder.com/blog/archive/2007/04/04/Altering-SQL-Server.aspx</guid>
            <pubDate>Wed, 04 Apr 2007 21:17:53 GMT</pubDate>
            <wfw:comment>http://nimblecoder.com/blog/comments/17.aspx</wfw:comment>
            <comments>http://nimblecoder.com/blog/archive/2007/04/04/Altering-SQL-Server.aspx#feedback</comments>
            <wfw:commentRss>http://nimblecoder.com/blog/comments/commentRss/17.aspx</wfw:commentRss>
            <trackback:ping>http://nimblecoder.com/blog/services/trackbacks/17.aspx</trackback:ping>
        </item>
    </channel>
</rss>