Spellings Consulting

When you need IT done right.

MS SQL And Sub-Selects

Today I discovered what I would consider a very serious flaw in MS SQL. Unfortunately, this discovery caused quite a bit of damage to my data, and although I could/should have been more careful, I place most of the blame on poorly designed software. After some simple tests, it appears that MySQL does not suffer from the same issue.

Our exceptional DBA’s (and others) suggest that a good practice to prevent UPDATE errors is to execute a SELECT clause that uses the same WHERE clause before your run your UPDATE command. Had I done this, it would have prevented my mistake, which is why I say I should have been more careful.

Anyway, the bottom line is that it appears that MS SQL’s implementation of Sub-Selects in both SELECT and UPDATE commands do not function as expected. It turns out that if you use a completely invalid SELECT statement as a Sub-Select in an UPDATE or SELECT statement, MS SQL will return records when it should not, in some circumstances.

Here is the SQL statement that I ran that caused all of my grief:
UPDATE FoldersAccounts
SET iAccountID = 56789
WHERE iFoldersAccountsID in (
             SELECT iFoldersAccountsID
             FROM Folders
            WHERE iFolderID in
            (1159,1160,1161,1162,1193,1200,1197,1196,1195,1186,1191,1189))

Now, what is so outrageous is that the sub-select is completely invalid because there is no column named “iFoldersAccountsID” in the Folders table. If you try to run this statement by itself:
SELECT iFoldersAccountsID
FROM Folders
WHERE iFolderID in
(1159,1160,1161,1162,1193,1200,1197,1196,1195,1186,1191,1189))

The above query, when run by itself in MS SQL Server Mangagement Studio, returns the following error:
Msg 207, Level 16, State 1, Line 1
Invalid column name ‘iFoldersAccountsID’.

As I mentioned earlier, this is the proper response for this query because there is no iFoldersAccountsID column in the Folders table.

What is so crazy is that when you run the same SELECT statement as a sub-select, no error is returned an every single record in the FoldersAccounts table is returned. In this case, that’s 1204 records. That also means that when this same SELECT statement is used in the sub-select of an UPDATE command, every single record in the table is updated, which is obviously terrible.

Even more strange is that when you make the exact same Sub-select but swap out the table and use a table name in the FROM clause that has neither the column in the SELECT clause or the WHERE clause, then still no error is returned and a seemingly ambiguous number or records are returned. Thus, the following query returns 12 records, which appears to be one for each iFolderID:
SELECT iFoldersAccountsID
FROM PriceCodes
WHERE iFolderID in
(1159,1160,1161,1162,1193,1200,1197,1196,1195,1186,1191,1189))

Obviously, the PriceCodes table has neither an iFoldersAccountsID nor an iFolderID, yet the above query returns no errors.

Interestingly, if you use a nonexistant table name, that will throw an error. At least they got that right. :-)

Since I am clearly biased as to the “correctness” of this behavior, since it caused me so much trouble, I decided to see how MySQL handles the exact same scenario. So I created the exact same table structure in a MySQL database and tried the following query:
UPDATE FoldersAccounts
SET iAccountID = 56789
WHERE iFoldersAccountsID in (
             SELECT iFoldersAccountsID
             FROM Folders
            WHERE iFolderID in
            (1159,1160,1161,1162,1193,1200,1197,1196,1195,1186,1191,1189))

The above query in MySQL does not return an error, but it does return an “empty result” (selects zero records):

IMO, this is the better behavior, but ideally this should return an error.  This seems like something that should be a no-brainer, but maybe I am missing something.  If so, fill me in.

One thing that I did think about here is how this behavior in MSSQL could potentially be used in an SQL Injection attack to completely screw up an entire table.  This is a great reason to use Stored Procedures in web applications for any data access in web applications. 

sample image gallery

sample 4 sample title 3 sample title 2 sample title  

HTTP Request Script

I recently thought that I would be nice to write a VB Script that could run as a scheduled task that would attempt to login to a data-driven web site and notify me via email if there was an unsuccessful login.  I figured that this would be a good way to pro-actively be notified if a client’s website had gone down (ie: if you can’t login, something has happened to the site).

The script sends an http request with form post data in the http header and waits for the HTTP server response.  It then looks for a specific string that will only appear in the response after a successful login, such as the username or a specific greeting that appears on the landing page.  If the expected string is not found, then the Vb Script sends an email (optional) and also writes the HTTP Server response to a local file with a unique name so that a user can inspect the responses for debugging and forensic purposes.

Download the script here and use it freely as long as you do not resell it, and you don’t blame me for unexpected results.

Subdirectory Sizes Script

Ever wonder where all your hard drive space is going?  At work,we had a 1 terabyte hard drive that was quickly filling up and wanted to know where all the space was going.  Some of the directories were so large that right-clicking on them would essentially crash windows (make the explorer window nonresponseive), so another solution was needed.

 So I wrote a VB Script to do the job.  Simply place this VB Script in any directory and double-click it.  It will find the size of every subdirectory therein and create a log file listing the sizes of every subdirectory.  The script’s log file is written in HTML and displayed in Internet Explorer.  This was done to create a log flie that is easier to read.

If you’d like to use this script, feel free to download it here.  If you’d like features added to the script, please leave a comment.

Zip Script

I was recently asked to zip up tens of thousands of files in a few hundred subdirectories.  The targeted files were those whose Last Modified date was older than one year ago.  Instead of manually zipping all of these files, I started looking for a way to automate this process.  I discovered that WinZip offers a Command Line Add-on, which would be accessible via script.  The Add-on does require the pruchase of WinZip 10 Pro, which I later discovered actually has similar features built-in already.

The Script
The script I’ve written is a VB Script that also utilizes the WinZip Command Line add-on mentioned above.  I have tested the script on: Windows 2000, Windows XP Pro, and Windows 2003 Server Standard Edition 64-Bit. 

After I wrote the script to use in a specific situation, I realized that it would be useful to alter it to be used in a more generic way.  So I added some features that allow the script to be called from the command line or a batch file in a manner where the user can add arguments when they call the script to control (1) the target directory (2) the Archive Date threshold (3) the file types to be zipped.

How it Works
The script goes through all the subdirectories in a directory and compares the Last Modified Date of each file to a date specified by the user.  If the Last Modified Date is older than the specified date, then the file is added to a zip file that is named the same name as the subdirectory.  If there is no existing zip file, then one is created.  After the files are added to the zip, they are removed from the subdirectory.

You can run the script as a scheduled task, but if you do, so be sure to alter the Default values for all of the variables that control how this script selects the target directory and files.  This is clearly commented in the code, so it should be easy to find.

The script also keeps a very detailed log file about what happens every time that it runs.  It can also be configured to send an email when it complete or fails.

If you would like a copy of this script, feel free to use it, but please give creidt to the author.  You can download it here.