Category SQL

Upcoming Speaking Engagements

Just a quick reminder that I’ll be speaking at the Richmond Code Camp this upcoming weekend. I’ll be giving two talks… here they are:

Indexes and Other Free Performance Boosts

Time & Location

October 4, 2009 at Richmond Code Camp.

Abstract

The database is often viewed as a major performance bottleneck. There are a number of quick, easy, painless techniques that can increase the performance of an application not just by a small amount, but by orders of magnitude. These techniques includes simple indexing techniques, T-SQL techniques, and general database application design patterns that give great gains in performance. In this session, you will learn how to look at a database to identify these problem areas and how to resolve common issues that you will encounter.

From Tables to Objects: Making Your Database Work With You

Time & Location

October 4, 2009 at Richmond Code Camp.

Abstract

A database is far more than a persistent object store for your application; it is capable of data validation en masse, aggregations, and creating different projections of data. By working with your database, rather than against it, it is possible to leverage all of the capabilities of a relational database to provide rich, high performance interaction with your application through an ORM. This presentation will discuss the finer points of building a full-featured data access layer using an ORM and the features of a relational database.

Goals

  1. Teach attendees about database features that make development easier – including views, sparsely populated tables, and user-defined functions and types.
  2. Empower the audience to solve object relational impedance mismatch using data modeling techniques and database features.
  3. Demonstrate how to integrate the database and object oriented software using a custom designed data access layer.

Links for the Week – 2009.09.25

SQL Server

There’s a three part series (one two three) on migrating databases. I’m sure many people have been through this before. I know I have. About a week before these three posts were published.

Ever wonder when you should use a filtered index and when you should use an indexed view? Yeah, I have too. The distinction hasn’t been all that clear (to me). Andrew Fryer clears that up a bit in this blog post.

Development

I like the idea of continuous integration. I like testing. I like automating everything until I don’t even have to think about it anymore. It can be done even with the database. Jeffrey Palermo and Eric Hexter demonstrated just how to get CI working with your database project using nothing but pure T-SQL.

Stuff & Things

These first two are for some RSS reader goodness. I haven’t gotten around to trying out Fever for my RSS feeds yet. The idea of paying $40 for an RSS reader is a bit steep. But, the idea of having my feeds add value to one another is intriguing. If you’re feeling a bit cheap, there’s always Helvetireader. This is a set of browser scripts and CSS that add a touch of class to your google reader.

The Personal MBA is a set of reading materials meant to teach everything that you would get in an MBA program. While I doubt that it’s the same as getting an MBA, I do like the idea of a reading list that’s designed to help me, a nerd, get better at something I suck at: business.

Soundtrack for a City Augmented reality – through our phones or sunglasses or our retinal implants – is only beginning. It’s new. Soon sight will be augmented by sound. I love this idea.

SQL Server Performance Tuning Webcast

Looking for a quick overview of SQL Server performance tuning? Well, look no further! Way back at devLink, David Giard was kind enough to interview me about my thoughts on troubleshooting SQL Server performance. Check it out.

A little history on this, David talked to me many months ago at the Stir Trek convention, but the audio was particularly bad so you couldn’t hear me ramble about ORMs and how stored procedures are the greatest thing since butter. Trust me, you’re better off for it.

Anyway… a huge thanks to David Giard for giving me the chance to talk a little about troubleshooting SQL Server performance.

Check Your Indexes

We all know that we’re supposed to check for missing indexes. But what about unused or underused indexes. How often do you check for those? My guess is that a lot of people don’t check for them. I know I didn’t… until Friday.

It’s database migration season here in central Ohio, and in order to move databases to new servers, you need to physically copy files. To make that operation fast you want to copy as little data as possible. Here’s the thing: we often only think about the amount of space that an index is going to take up before we add it to a table, but how often do you think about how much space indexes are taking up after you’ve added the index to the table?

Over the course of 3 minutes, I identified 20 gigabytes of unused indexes on just one database. That’s a staggering amount of space being taken up by indexes with no purpose. In addition to taking up precious storage space, these indexes also required maintenance – defragmentation, statistics updating – and also utilized disk IO whenever the source table was modified.

What about underutilized indexes? I don’t know… yet. I plan on examining the index utilization further to make sure that we’re using the most effective indexing strategies and I will probably be dumping indexes that are only used rarely. This is where things get tricky: when should I drop an index? How little usage is the same as no usage? Can I merge this index with another index? How much space is the unused index taking up?

I’m curious: Do other DBAs have a usage threshold they consider before they drop an index?

Links for the Week 2009.09.20

SQL Server

Distributed Queries – Remote Login Permissions and Execution Plans You can sometimes end up with SQL Server not generating good execution plans for remote queries. I just assumed that this was a problem with remote queries. Turns out that I was wrong and all you really need to do is give some specific permissions to the remote login. You learn something new every day.

John Paul Cook : Script to create all foreign keys Let’s make some foreign keys! How many times have you had to drop and recreate all of your foreign keys? I’ve had to do it, especially for complicated ETL processes where I really don’t feel like following the rules and I’d prefer to just throw data at a schema and validate it later. John Paul Cook has a great way to generate all of your keys as a drop and create. BOOM! DONE!

Development

Microsoft StreamInsight : Updated Sample Package StreamInsight is part of the new complex event processing framework that will be unleashed on the world in SQL Server 2008 R2. This mainly includes internationalization fixes and an updated help file.

Running Rake tasks in E-Text Editor e-text editor is a lot like TextMate, but for Windows. It’s a must if you’re doing any kind of Ruby development and don’t want NetBeans or Eclipse eating 95% of your system memory. I’ve run into problems using it to run my rake tasks in the past. My solution was to keep a terminal window open. Apparently there’s a better way.

Git Resources Git is what traditional source control wants to be when it grows up. I’m still learning and I have a long way to go on being 100% comfortable with git, however here’s a list of resources to help you along your route to using some sweet sweet version control.

Stuff & Things

Retro games are cool. I love retro gaming. In part because I don’t have a 3d accelerated graphics card in my macbook and my options are for retro games or flash games. The best part is that these aren’t abandonware games, these are legit games that have legally been released to the community. Go download some retro games. (The site is kind of slow because lifehacker linked to them, but be patient, it’s so worth it.)

i love typography has a link dump. I heart fonts. I heart link dumps. I super heart font link dumps.

Mistakes can be good. Matt at 37signals talks about the importance of revealing your mistakes and being honest about them and ties it in with an anecdote about Julia Child’s frank approach to her own cooking. I remember an episode of her show from when I was young where she tenderized meat with a 2×4. 20 years later I needed to tenderize meat, but I had no meat tenderizing hammer. But I did remember her advice about covering the meat and using anything available. So I covered the meat in saran wrap and then covered a book in saran wrap and I had a meat tenderizer. I’ve also used a shoe. True story.

Links for the Week of 2009.09.12

SQL Server

Optimizing Queries That Access Correlated datetime Columns DATE_CORRELATION_OPTIMIZATION is some good stuff. If you have an InvoiceDate column that happens to be close to the OrderDate column in a related table, and this setting is on, the compiler will generate what could be a much better optimization plan.

Difference between an index and a primary key Denny Cherry does a great job of explaining this one. I frequently misuse primary key and clustered index as the same in conversation (yes, I do have A LOT of conversations about databases, even outside of work). I need to be better about that. This reminds me why.

Development

Vertical Centering in CSS This question comes up every once in a while. I always forget how to do it. Hopefully this will help someone else get their vertical centering sorted out.

The Unspoken Truth About Managing Geeks I’m not a manager but I play one on TV. There some good info in here. Especially if you only play a manager on TV.

Using Accessibility Modifiers on Auto Properties Auto properties rule. Justin Etheredge demonstrates how to keep that level of awesome rolling forward.

Stuff & Things

Simplified Virtual PC Creation with Differencing Disks I don’t know what to say. The idea is so damn simple I don’t know why I didn’t think of it before. Thanks to Jonathan Kehayias for this article. This is going to save me so much time and trouble.

20 Tips for Writing for the Web

Flowing Data | Data Visualization and Statistics It’s a blog that’s devoted to different ways of presenting information. Gotta love that. Flat tabular display of data is good, but there’s often a better way. A much better way.

Make Fried Pie in Five Minutes and Kickstart Your Day Fried. Pie.

Monkey on a motorcycle

Think about the different areas of your life Love the quote.

Automating DBA Tasks

A member of my user group recently asked me about the best practices for monitoring and automating database maintenance.

Automate Everything

If I do something once, I automate it. I learned this valuable lesson at last year’s PASS Summit during a panel discussion. Any time I find an error situation, I try to determine which conditions might have caused that situation and I create a repeatable script that I can put on every server to check for a problem and email me.

Which Parts of Everything Should I Automate?

First off, you should check out this excellent article by Jonathan Kehayias about How to Automate Common DBA Tasks. It covers almost everything that we do on a day to day basis to make sure we have early warnings about any problems. I understand that there a products out there that might cover a lot of this functionality. But, if you’re stuck waiting for a purchase order, these scripts can hold you off for a while.

There are some WMI scripts in Jonathan’s article. I’m not using those yet. Why? I’d like to be using PowerShell instead. With the push to pick up PowerShell for the entire admin/IT pro space, I would prefer to migrate things to PowerShell than to add new features into VBScript and WMI applications.

Magical Backups

The final part of this cavalcade of T-SQL is my backup solution. This is no substitute for a commercial backup system, but it’s a start.

The BackupControl Table

First up we have BackupControl table:

CREATE TABLE [dbo].[BackupControl](
  
[id] [int] IDENTITY(1,1) NOT NULL,
  
[DatabaseName] [sysname] NOT NULL,
  
[BackupRoot] [nvarchar](255) NULL,
  
[BackupInterval] [tinyint] NULL,
 
CONSTRAINT [PK_BackupControl] PRIMARY KEY CLUSTERED 
(
  
[id] ASC
)WITH (PAD_INDEX  = OFFSTATISTICS_NORECOMPUTE  = OFFIGNORE_DUP_KEY = OFFALLOW_ROW_LOCKS  = ON
ALLOW_PAGE_LOCKS  = ONON [PRIMARY]
ON [PRIMARY] ;

This is a pretty simple table. There’s an arbitrary primary key so that we don’t keep referencing a lovely heap table. In addition, we have the name of the database, the root path where we want to do our backups, and then the interval for backups, in minutes.

The Jobs

I’ve scheduled a job to run nightly at a specific time, we’ll say 1:00 AM. This job takes nightly backups, removes backup history, and then deletes old backup files.

Backing up Databases

This is a simple step, but I’ve gone and made it complicated by using a lot of code. The reason for using a lot of code is that sometimes it’s necessary to backup databases A, B, and C to the G: drive and then backup databases D, R, and Q to the P: drive. You’ll also notice in this script that I use a maintenance database which has been cunningly named Maintenance. That’s so that I can hold all of my routine scripts in a single database, rather than having to copy them to every database on the server.

DECLARE @current_time AS NVARCHAR(50) ;
DECLARE @backup_root AS NVARCHAR(50) ;
DECLARE @sql AS NVARCHAR(MAX) ;
DECLARE @newline AS NVARCHAR(2) ;
DECLARE @s AS NVARCHAR(1) ;
DECLARE @now AS DATETIME ;

SET @current_time '' ;
SET @backup_root 'H:\BACKUPS\OLTP\DEFAULT'
SET @sql '' ;
SET @newline NCHAR(13) + NCHAR(10) ;
SET @s '''' ;
SET @now GETDATE() ;

SET @current_time CAST(DATEPART(yyyy@nowAS NVARCHAR(4)) + N'.'
  
RIGHT(N'00' CAST(DATEPART(mm@nowAS NVARCHAR(2)), 2) + N'.'
  
RIGHT(N'00' CAST(DATEPART(dd@nowAS NVARCHAR(2)), 2) + N'.'
  
RIGHT(N'00' CAST(DATEPART(hh@nowAS NVARCHAR(2)), 2) + N'.'
  
RIGHT(N'00' CAST(DATEPART(mi@nowAS NVARCHAR(2)), 2) ;
              
SELECT  @sql @sql N'BACKUP DATABASE ' DatabaseName N' TO DISK = ' @s
        
COALESCE(BackupRoot@backup_root DatabaseName N'\')
        + 
DatabaseName N'.' @current_time N'.bak' @s
        
N' WITH FORMAT ;' @newline
FROM    Maintenance.dbo.BackupControl ;

EXEC sp_executesql @sql ;

After this runs, I run a stored procedure to clean up the backup history – more info about why this is important over here, on Brent Ozar’s blog. The code is in a step that is set to run in msdb:

DECLARE @oldest_date AS DATETIME;
SET @oldest_date DATEADD(mm, -1GETDATE());

EXEC sp_delete_backuphistory @oldest_date;

And, finally, I run a little chunk of PowerShell to delete only backup files:

Get-Item "H:\BACKUPS\OLTP\DEFAULT" | Get-ChildItem | Get-ChildItem |? {$_.CreationTime -lt (Get-Date).AddDays(-2)} | Del

What About My Transaction Logs?

I thought you might ask that… Okay, you probably didn’t because you know I’m using rhetorical devices.

Just like our lovely script to take daily backups, we have a script to take regular transaction log backups. The cool part is that we’re going to tell it to run on a schedule and we don’t have to worry about whether or not the timings sync up perfectly. How can we do that?

At the start of the script, we grab the current time into a local variable. Then we go ahead and pull a list of all of the databases from our BackupControl table that meet the following conditions:

  • The BackupInterval isn’t NULL
  • The modulus of the BackupInterval and the current time is 0 (if you want to run the backup every 7 mintues, it will get run at 12:07, 12:14, 12:21, etc)
  • The database recovery mode is not set to simple.

The code to do this is remarkably similar to the full backup script above:

DECLARE @current_time AS NVARCHAR(50) ;
DECLARE @newline AS NVARCHAR(2NCHAR(13) + NCHAR(10) ;
DECLARE @now AS DATETIME GETDATE() ;
DECLARE @sql AS NVARCHAR(MAX'' ;
DECLARE @s AS NVARCHAR(1'''';

SET @current_time CAST(DATEPART(yyyy@nowAS NVARCHAR(4)) + N'.'
  
RIGHT(N'00' CAST(DATEPART(mm@nowAS NVARCHAR(2)), 2) + N'.'
  
RIGHT(N'00' CAST(DATEPART(dd@nowAS NVARCHAR(2)), 2) ;

SELECT  @sql +N'BACKUP LOG ' DatabaseName N' TO DISK = ' @s BackupRoot
        
DatabaseName N'.' @current_time N'.log.bak' @s @newline
FROM    dbo.BackupControl AS bc
WHERE   BackupInterval IS NOT NULL
        AND (
DATEPART(mi@now) % BackupInterval0
        
AND DATABASEPROPERTYEX(DatabaseName'Recovery') <> 'SIMPLE' ;

EXEC sp_executesql @sql ;

What Else Should I Automate?

Automate everything. If you’ve done it once, automate it. If you don’t want to deal with a problem, figure out how to detect it before it’s a problem and automate that. Through the Dynamic Management Views and built-in functions, it’s possible to collect a wealth of information about SQL Server that you can use to detect your pain points. Turn those pain points around and you’ve got a solid monitoring solution.

Free and Cheap Stuff!

That’s right, free and/or cheap stuff. Right now you’re probably saying to yourself “ZOMG! He’s got free stuff!”

Free Stuff!

So, what do I know about that’s free?

Data Cleansing with SSIS – Eric Veerman is going to be talking all about data cleansing. You know, getting all of the crud and nastiness out of your incoming data. You know, the data where people have extra spaces in their name because your CRM system treats that as a different person and sales people can steal commissions if it’s a “new” customer.

SQL Lunch: Historical DMV Information Thomas LeBlanc is giving this presentation. Here’s what he has to say about it: DMV are great to find information about performance without running a trace. But there information is lost once the SQL Server service is restarted. After finding some useful scripts online (Thanks SQL Server community!!!), I decided to schedule a job to run the night before to store Index Usage, Least Used Indexes, SP Usage, Missing Index and Expensive CPU Usage. We will review the scripts and historical data captured.

CBusPASS – the Columbus, OH PASS Chapter – is meeting tomorrow, September 10th, at 6:30PM. Joe Webb will be giving his presentation Locking and Blocking Made Simple: A good working knowledge of how SQL Server makes use of locking and transaction isolation levels can go a long way toward improving an application’s performance. In this session, we will explore SQL Server’s locking methodology and discover techniques for enhancing query response times.

Some of the 24 Hours of PASS Sessions are now online for viewing. Check them out and get your learn on!

Cheap Stuff

Remember how I posted about getting to PASS on the cheap? Or how about that interview I did with Thomas LaRock about getting to PASS on the cheap?

Well, thanks to some some great volunteers we now have a forum for the Summit! The point of the forum is to help you save money bu sharing a room with another attendee, a ride, or to coordinate social get togethers. We really want you to come to the PASS Summit and have fun. I really do. Really.

text TEXT teXt?

The Problem

A reader ran into a problem on an old post about Automating T-SQL Testing. Thankfully, rather than decide that I’m some useless crank on the internet, this reader took the time to write in to me and tell me about the problem. But, he went one step further: he documented step by step his problem!

The Process

I fired up SSMS and verified his problem (using the thoughtfully provided steps to duplicate). Sure enough, my code didn’t work. Which is odd because I’ve used this very same code to test stored procedures.

At this point, I calmly stood up, walked away from my computer, opened the window, and swore loudly. I then closed the window, walked back to the computer, and sat down. After numerous permutations, I finally searched google in exasperation only to find out it was a capitalization issue (read all about it over here).

Where was this coming from?

The Actual Problem

The GeSHi JavaScript syntax highlighting library was capitalizing the word ‘text’ because it is a datatype in SQL Server. Unfortunately, the text() XML method is lowercase. So, in this isolated incident, my syntax highlighting was causing me problems. The solution was to remove syntax highlighting from that portion of my post and inform the reader of the problem.

What to do?

Well, you need to test things. In this case, though, it was something that I couldn’t effectively test. The developers of the GeSHi plug-in should probably be testing these things (and I’ll be tracking down their info to send them a bug report). The key here is, I guess, to be very very careful. Really I just wanted to share this bug so others don’t get caught by it.

Scripting Objects with Magic

You know what, there’s no magic here. However, it’s kinda magic to me since I didn’t write it and reading other people’s code is something that is almost like magic to me. And, I have to admit that I did not write this code. One of my co-workers, Brent Morrow, came up with this solution. Sadly, Brent does not have a blog. Luckily, I do. So now I will share with you Brent’s solution for keeping track of objects to move from one platform to another.

What’s the goal here? Well, let’s say we don’t have VSTS: Database Developer edition. And let’s further posit that we don’t have access to any of the commercially available schema comparison tools. Apart from pen and paper, how shall we keep track of the changes that need to go into production?

Well, we can use SQL Server to keep track of these changes.

The first thing we need is a table to track this:

CREATE TABLE [Debug].[DBObjects]
    (
      [LocalID] [int] IDENTITY(1, 1)
                      NOT NULL,
      [Schema] [nvarchar](128) NOT NULL,
      [Name] [nvarchar](128) NOT NULL,
      [ObjectType] [varchar](30) NOT NULL,
      [RequestedBy] [varchar](30) NULL,
      [RequestedDate] [datetime] NULL,
      [ProductionReady] [bit] NULL,
      [ProductionDate] [smalldatetime] NULL,
      [InProduction] [bit] NULL,
      [Comments] [varchar](2048) NULL,
      CONSTRAINT [PK_DBObjects] PRIMARY KEY CLUSTERED ( [LocalID] ASC )
        WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
               IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
               ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]
    )
ON  [PRIMARY] ;

So, here are the rules for the table:

  • Schema – this is the object schema. It’s required.
  • Name – the name of the object to be scripted. Also required.
  • ObjectType – the type of object that we’re scripting. This needs to be one of the following values: STORED PROCEDURE, VIEW, FUNCTION. This is required. Spelling is important.
  • RequestedBy – this shows up in the script, but it’s not required.
  • RequestedDate – this also shows up in the script, but it’s not required.
  • ProductionReady – this is required, it needs to be set to 1.
  • ProductionDate – this is optional, it is just the date that something goes/went into production.
  • InProduction – One the object has been moved into production, set this to true (1). If you don’t do this, and ProductionReady is set to true (1), then the object will be scripted the next time that everything is run.
  • Comments – optional comments, these show up in the script.

So, what is the next step? Well, you need a way to script all of this. Basically, the following script will execute against the DBOjects table and will generate a script for all of the objects in the table with ProductionReady set to true and InProduct set to false. Once the script has been run against the new environment, set InProduction to true (or remove the row) and the object won’t be scripted in subsequent runs. If you have more changes, add a new line with a new comment and new request date. Good times, eh?

So, a big thanks to Brent Morrow for sharing this code with the world. If you have questions, comments, concerns, improvements, or just want to share kudos, put them down in the comments or drop me an email and I’ll make sure that Brent knows about it.

---------------------------------------------------------------
-- DEFINITIONS
---------------------------------------------------------------

CREATE TABLE #EntryHold ( [Text] VARCHAR(4000) )

CREATE TABLE #Entry ( [Text] VARCHAR(4000) )

DECLARE @LocalID INT
DECLARE @Schema VARCHAR(50)
DECLARE @Name VARCHAR(100)
DECLARE @ObjectType VARCHAR(100)
DECLARE @ProductionReady BIT
DECLARE @InProduction BIT
DECLARE @RequestedBy VARCHAR(50)
DECLARE @RequestedDate DATETIME
DECLARE @Comments VARCHAR(2000)
DECLARE @Counter TINYINT
DECLARE @Supported BIT

DECLARE [EntryCursor] SCROLL CURSOR
    FOR SELECT  [LocalID],
                [Schema],
                [Name],
                [ObjectType],
                [ProductionReady],
                [InProduction],
                [RequestedBy],
                [RequestedDate],
                [Comments]
        FROM    [Debug].[DBObjects]
        ORDER BY [LocalID]

---------------------------------------------------------------
-- HEADER
---------------------------------------------------------------
OPEN [EntryCursor]

-- STORED PROCEDURES NOT MOVED
INSERT  INTO #Entry
VALUES  (
          '--- OBJECTS NOT BEING MOVED ---'
        )

SET @Counter = 0
WHILE @Counter < 3
    BEGIN
        FETCH FIRST FROM [EntryCursor] INTO @LocalID, @Schema, @Name,
            @ObjectType, @ProductionReady, @InProduction, @RequestedBy,
            @RequestedDate, @Comments

        WHILE @@FETCH_STATUS = 0
            BEGIN
                SET @Supported = CASE WHEN @ObjectType IN ( 'VIEW',
                                                            'STORED PROCEDURE',
                                                            'FUNCTION' )
                                      THEN 1
                                      ELSE 0
                                 END

                IF ( ( @ObjectType = 'VIEW'
                       AND @Counter = 0
                     )
                     OR ( @ObjectType = 'STORED PROCEDURE'
                          AND @Counter = 1
                        )
                     OR ( @ObjectType NOT IN ( 'STORED PROCEDURE', 'VIEW' )
                          AND @Counter = 2
                        )
                   )
                    AND ( @ProductionReady IS NULL
                          OR @ProductionReady = 0
                        )
                    AND ( @InProduction IS NULL
                          OR @InProduction = 0
                        )
                    BEGIN
                        INSERT  INTO #Entry
                        VALUES  (
                                  '--REQUESTOR: ' + @RequestedBy + SPACE(12 - LEN(@RequestedBy))
                                  + 'DATE: ' + CONVERT(VARCHAR, @RequestedDate, 101)
                                  + ' | ' + @ObjectType + ': ' + @Schema + '.'
                                  + @Name
                                )
                        IF @Supported = 1
                            INSERT  INTO #Entry
                            VALUES  (
                                      '--        REASON NOT MOVED:  Not production ready!'
                                    )
                        ELSE
                            INSERT  INTO #Entry
                            VALUES  (
                                      '--        REASON NOT MOVED:  Not Supported!'
                                    )

                        IF ( @Comments IS NOT NULL )
                            INSERT  INTO #Entry
                            VALUES  (
                                      '--        COMMENTS:  ' + @Comments
                                    )
                    END
                FETCH [EntryCursor] INTO @LocalID, @Schema, @Name, @ObjectType,
                    @ProductionReady, @InProduction, @RequestedBy,
                    @RequestedDate, @Comments
            END
        SET @Counter = @Counter + 1
    END
INSERT  INTO #Entry
VALUES  (
          '---------------------------------------------------------------------------------------------------'
        )

INSERT  INTO #Entry
VALUES  (
          '--- OBJECTS READY TO MOVE ---'
        )

SET @Counter = 0
WHILE @Counter < 3
    BEGIN
        FETCH FIRST FROM [EntryCursor] INTO @LocalID, @Schema, @Name,
            @ObjectType, @ProductionReady, @InProduction, @RequestedBy,
            @RequestedDate, @Comments
        WHILE @@FETCH_STATUS = 0
            BEGIN
                IF ( ( @ObjectType = 'VIEW'
                       AND @Counter = 0
                     )
                     OR ( @ObjectType = 'STORED PROCEDURE'
                          AND @Counter = 1
                        )
                     OR ( @ObjectType NOT IN ( 'STORED PROCEDURE', 'VIEW' )
                          AND @Counter = 2
                        )
                   )
                    AND ( @ProductionReady = 1 )
                    AND ( @InProduction IS NULL
                          OR @InProduction = 0
                        )
                    BEGIN
                        INSERT  INTO #Entry
                        VALUES  (
                                  '--REQUESTOR: ' + @RequestedBy + SPACE(12 - LEN(@RequestedBy))
                                  + 'DATE: ' + CONVERT(VARCHAR, @RequestedDate, 101)
                                  + ' | ' + @ObjectType + ': ' + @Schema + '.'
                                  + @Name
                                )
                        IF ( @Comments IS NOT NULL )
                            INSERT  INTO #Entry
                            VALUES  (
                                      '--        COMMENTS:  ' + @Comments
                                    )
                    END
                FETCH [EntryCursor] INTO @LocalID, @Schema, @Name, @ObjectType,
                    @ProductionReady, @InProduction, @RequestedBy,
                    @RequestedDate, @Comments
            END
        SET @Counter = @Counter + 1
    END

INSERT  INTO #Entry
VALUES  (
          '---------------------------------------------------------------------------------------------------'
        )
INSERT  INTO #Entry
VALUES  (
          '--- OBJECT SCRIPTS ---'
        )

---------------------------------------------------------------
-- SCRIPTS THAT ARE PRODUCTION READY
---------------------------------------------------------------
SET @Counter = 0
WHILE @Counter  5
                            BEGIN
                                INSERT  INTO #Entry
                                        SELECT  *
                                        FROM    #EntryHold
                            END
                        ELSE
                            BEGIN
                                INSERT  INTO #Entry
                                VALUES  (
                                          '--' + @ObjectType + ' [' + @Schema
                                          + '].[' + @Name + '] was not found.'
                                        )
                                INSERT  INTO #Entry
                                VALUES  (
                                          '---------------------------------------------------------------------------------------------------'
                                        )
                            END
                        DELETE  FROM #EntryHold
                    END

                FETCH [EntryCursor] INTO @LocalID, @Schema, @Name, @ObjectType,
                    @ProductionReady, @InProduction, @RequestedBy,
                    @RequestedDate, @Comments
            END
        SET @Counter = @Counter + 1
    END

---------------------------------------------------------------
-- SCRIPTS THAT ARE NOT PRODUCTION READY
---------------------------------------------------------------
CREATE TABLE #CommentEntry ( [Text] VARCHAR(4000) )

SET @Counter = 0
WHILE @Counter < 3
    BEGIN
        FETCH FIRST FROM [EntryCursor] INTO @LocalID, @Schema, @Name,
            @ObjectType, @ProductionReady, @InProduction, @RequestedBy,
            @RequestedDate, @Comments

        WHILE @@FETCH_STATUS = 0
            BEGIN
                IF ( ( @ObjectType = 'VIEW'
                       AND @Counter = 0
                     )
                     OR ( @ObjectType = 'STORED PROCEDURE'
                          AND @Counter = 1
                        )
                     OR ( @ObjectType NOT IN ( 'STORED PROCEDURE', 'VIEW' )
                          AND @Counter = 2
                        )
                   )
                    AND ( @ProductionReady IS NULL
                          OR @ProductionReady = 0
                        )
                    AND ( @InProduction IS NULL
                          OR @InProduction = 0
                        )
                    BEGIN
                        INSERT  INTO #CommentEntry
                                EXECUTE
                                       ( 'sp_helptext @objname = ''' + @Schema
                                         + '.' + @Name + ''''
                                       )
                        INSERT  INTO #CommentEntry
                        VALUES  ( 'GO' )
                        INSERT  INTO #CommentEntry
                        VALUES  (
                                  '---------------------------------------------------------------------------------------------------'
                                )
                    END

                FETCH [EntryCursor] INTO @LocalID, @Schema, @Name, @ObjectType,
                    @ProductionReady, @InProduction, @RequestedBy,
                    @RequestedDate, @Comments
            END
        SET @Counter = @Counter + 1
    END

INSERT  INTO #Entry
        SELECT  '--' + [Text]
        FROM    #CommentEntry

DROP TABLE #CommentEntry

CLOSE [EntryCursor]
DEALLOCATE [EntryCursor]

DROP TABLE #EntryHold

SELECT  *
FROM    #Entry
DROP TABLE #ENTRY

This site is protected with Urban Giraffe's plugin 'HTML Purified' and Edward Z. Yang's Powered by HTML Purifier. 401 items have been purified.