Using SQL to Retrieve SQL

At work, someone made a request that required me to look through potentially hundreds of views in dozens of databases on our SQL Server. I certainly didn't want to examine each one at a time. How could I speed up this process with code?

Well, you can find all of your views by querying the sysobjects table, and you can retrieve the SQL behind the views by querying the syscomments table. Something like this works well:

SELECT RTrim(sysobjects.name) AS ViewName,
       RTrim(syscomments.text) AS ViewSQL
FROM   sysobjects JOIN syscomments
ON     syscomments.id=sysobjects.id
WHERE  sysobjects.xtype='V' AND sysobjects.category=0

This will retrieve the SQL code and names of all the views in the current database. This simple query is the heart of the solution. But I would like to retrieve this information for all of the databases.

Well, it's easy enough to get a list of all the databases. The sysdatabases table in the master database has that list. You can query that table, perhaps filtering out some of the system or sample databases included with SQL Server:

SELECT name FROM master.dbo.sysdatabases
WHERE name NOT IN ('tempdb','master','msdb','pubs','model')
ORDER BY name

Now just combine this information. To accomplish this, we'll build a stored procedure that will create a temporary table, loop through the databases and query each one for its views, insert the view information into the temporary table, and return the temporary table.

Something like this will do the trick:

CREATE PROC dbo.selectViews   AS
BEGIN

-- Vars
DECLARE @dbname sysname

-- Temp Table
CREATE TABLE #Results
(  
  DatabaseName varchar(200),
  ViewName varchar(200),
  ViewText nvarchar(4000)
)

-- Loop Thru the Databases.
DECLARE dbnames_cursor CURSOR
FOR
  SELECT name FROM master.dbo.sysdatabases
  WHERE name NOT IN ('tempdb','master','msdb','pubs','model')
  ORDER BY name
OPEN dbnames_cursor
FETCH NEXT FROM dbnames_cursor INTO @dbname
WHILE (@@FETCH_STATUS <> -1)
BEGIN
  IF (@@FETCH_STATUS <> -2)
  BEGIN   
    -- Grab the Views of this Database and Put them in the Temp Table.
    SET @dbname = RTRIM(@dbname)
    INSERT INTO #Results
    EXECUTE
    (
      'SELECT '''+@dbName+''' as DatabaseName, ' +
      'RTrim(' + @dbname + '.dbo.sysobjects.name) as ViewName, ' +
      'RTrim(' + @dbname + '.dbo.syscomments.text) as ViewText ' +
      'FROM ' + @dbName + '.dbo.sysobjects join ' + @dbName + '.dbo.syscomments ' +
      'ON ' + @dbName + '.dbo.syscomments.id=' + @dbName + '.dbo.sysobjects.id ' +
      'WHERE ' + @dbname + '.dbo.sysobjects.xtype=''V'' and ' + @dbname + '.dbo.sysobjects.category=0 '
    )
  END
  FETCH NEXT FROM dbnames_cursor INTO @dbname
END
CLOSE dbnames_cursor
DEALLOCATE dbnames_cursor
SELECT * FROM #Results order by DatabaseName, ViewName
DROP TABLE #Results

END
--

Now just execute the stored procedure and review its output.

exec selectViews

This portion of the solution just retrieves the data. After writing this, I developed a really short and simple ColdFusion application that would output the database name, view name, and SQL to a table, and used some simple JavaScript to make it easier to search and filter the views. The client-side methods used to view and work with the data are obviously up to you.

Microsoft Access 2007 and ColdFusion 8

With Microsoft Office 2007, Microsoft strikes again and implements a new format for their Access database standard. I do not recommend using Microsoft Access databases for production applications. In fact, I believe you will pay, sooner or later, for adopting such a practice. Nevertheless, it is useful for testing or development at times, and more importantly, you may be in a situation like me, where there are databases that you do not "own" or control but your ColdFusion apps must use them.

That said, please note that the drivers that come with CF8 do not support Access 2007. This is mentioned in the ColdFusion 8 Release Notes under #69495. The solution is nicely described right in the release notes. Download and install the Data Connectivity Components for Office 2007, set up a Windows ODBC connection, and use ColdFusion's ODBC socket driver to connect to the Windows ODBC connection. A bit irritating that all of this has to be done, but like I said at the outset, these irritants are the price you pay, among other things, when using Access databases.

Another point to note! You thought you were done, but no, Microsoft has more joy in store for some of us.  The Office 2007 drivers do not support Windows 2000. So if your server is still sitting on a Windows 2000 installation, the final word is either (a) No Access 2007 for you, or (b) Time to upgrade to Windows 2003 SP1 or later.

Why That Batch For Loop Isn’t Working

Time for another fun foray into Windows batch scripts. Perhaps you've used the FOR /F command to loop through the contents of a file (for instance, perhaps some data that was redirected to a text file from a command). Grab a line, act on its values, and output some text and commands.

Let's set this up. First, we have a data file named SomeAccounts.txt:

Josh
Mary
Suzy
Amanda
Trisha
Ben

Then, we have ProcessAccounts.bat, which we want to just loop through the accounts in the text file, tell us what they are, and tell us the first letter of the account name (just to have something to do):

set file=SomeAccounts.txt
FOR /F %%i IN (%file%) DO (
set username=%%i
echo My account, %username%, starts with %username:~0,1%.
)

Except when you do this, you encounter a problem: All of the values from the FOR loop are the same! It's as if the for loop ran the proper number of times, but it just ran on the last record over and over again! See below:

My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.

What's actually happening is the FOR loop is indeed running over every line, and setting the variables as instructed, but the results of those variables being altered isn't echoed until the FOR loop is complete, so the last value of the variable is what displays. This wouldn't be a problem if you were just using your FOR parameter, in this case %%i, but any variables you set while in the FOR loop, like username, experience this "wait until you're out of the loop" phenomenon.

The fix is simple enough, if you know about it! But I've found the solution to be a bit elusive, which is the whole point of sharing it now.

The key is the setlocal EnableDelayedExpansion command. As explained at ss64.com, making this statement before your FOR loop will enable you to display variables as their value at the moment you're referencing them, or their "intermediate values" while in the middle of the FOR loop. In addition to calling the setlocal command, you then have to reference your variables with the exclamation point (!) rather than percent (%) to indicate that you want to use the intermediate value.

Your script will then look like this:

setlocal EnableDelayedExpansion
set file=SomeAccounts.txt
FOR /F %%i IN (%file%) DO (
set username=%%i
echo My account, !username!, starts with !username:~0,1!.
)

It will now happily act as desired, outputting these results:

My account, Josh, starts with J.
My account, Mary, starts with M.
My account, Suzy, starts with S.
My account, Amanda, starts with A.
My account, Trisha, starts with T.
My account, Ben, starts with B.

Upgrading Subversion Requires a Bindings Update for Trac!

My Subversion/Trac server was at Trac v0.9.6 and Subversion v1.3.x because those were the latest stable releases when I set up the server. I decided it would be relatively quick and painless to at least get the latest version of Subversion (v1.4.5) installed since I didn't see anything on the web about Trac v0.9.6 being incompatible with newer Subversion builds.

Using the Windows binary installer, I had no problem installing Subversion v1.4.5 on the server. I tested everything and Subversion still worked, it showed the new version when accessing via web access, and Trac still worked fine.

Alas: Don't forget that an upgraded version of Subversion will not upgrade your repository. It will upgrade a working copy of a checked-out repository, but it will not upgrade the repository itself.

That said, I was unaware of one more step that you must take to upgrade Subversion on a Subversion/Trac setup: You must also upgrade the Python bindings to Subversion.

This became apparent the next time I created a new repository, which was not a v1.4.x repository, and when I tried to build a Trac environment to point to it, Trac got upset because of the classic "Expected version '3' of repository; found version '5'" error. To fix this, you must set up new bindings to the new version of Subversion, as explained in the TracSubversion page.

Now, I obviously love Subversion and I love Trac, but honestly, straight-forward documentation that is easy to understand for someone who doesn't want to get in the thick of it isn't really the strong suit for these communities, at least when it comes to installation and deployment on the server. What exactly it means--and how to do it--when they say, "Update the Subversion bindings" is not easy to understand. However, the solution is simple. All that is needed is to download the appropriate "svn-python" Windows installer that matches your version of Subversion and Python (in my case, 1.4.5 and py2.3) and run it on the server.

In my case, I had to stop Apache for the installation to succeed. Upon restarting Apache, everything worked great.

  Theme Brought to you by Directory Journal and Elegant Directory.