Review SQL Server 2008 Administration in Action

SQL Server 2008 Administration in Action by Rod College
I got a copy of this book back in September of 2009 because I wanted to know more about the hardware configuration of production database servers.  Specifically, I wanted to know about best practices and recommendations in the areas of storage, memory and CPU.  While at the store, I read chapter 2 “Storage and System Sizing” which covers from what SATA means to areas such as RAID, SAN, solid state drives, and best practice considerations. I purchased this book just for the content in chapter 2 and had zero expectations for the rest.

After reading the remainder of the book, I commend Rod College and the team behind this volume for their fantastic job. The material is accurate, current and invaluable to DBAs at any level. You’ll get a wealth of information from new features found in MS SQL 2008 to concepts you should already know but you are not familiar with in MS SQL 2008.

Review SQL Antipatterns

SQL Antipatterns - Avoiding the Pitfalls of Database Programming
Bill Karwin style and choice of arranging the material within the chapters made it an easy read. It includes good and accurate examples the reader can test in almost any database system. Overall, the book will save the inexperience from many failures and embarrassment.

I purchased this book hoping to find something new. Unfortunately, I have been in the trenches long enough to live through and see most of these
patterns. This book will be a great addition and a must read for application developers, new database professionals including DBA, developers and architects. I say “new” because it takes time to see all these patterns.

Review SQL Server 2008 Administration with Windows Powershell

Microsoft SQL Server 2008 Administration with Windows PowerShell I recommend this book to all database administrators who still have not jumped in and executed their first PowerShell command.  I know of several DBAs who for one excuse reason or another, keep postponing their exposure to the next generation command line shell.  This volume makes a good first PowerShell book for DBAs.  It covers the basics of the language with enough examples to get you going and good coverage of what is possible when combined with MS SQL 2008.

The first ten chapters go over specifics of the language, programming syntax, and how to interact with different areas of the operating system.  The following eleven chapters demonstrate with code samples, how integrated are MS SQL 2008 and PowerShell.  I have found that many of the examples in this book also apply to MS SQL 2005.  My favorite chapter is 15 “Building SQL Server Inventory” because the authors provide a working solution to a common problems DBA have when managing many database servers.

To those DBAs who still have not jumped in, do yourself a favor and read this book.

Monitoring MS SQL Jobs with PowerShell

PROBLEM: You manage many MS SQL servers and need an easy way to review jobs that have failed.

SOLUTION: I will show you one way to accomplish this task with results displaying on the screen or to a file. To make your life easier, I recommend you download & install PowerGUI from Quest Software. This application comes with a script editor that will enhance your PowerShell development experience.

Go ahead and open a new session of PowerShell. You can type or copy and paste the code from our initial three lines of code. We will start by loading the SMO assembly & creating an instance of the server object. On the third and final line of code, we get a collection of all jobs by accessing the SQL Server agent on the server. We then pipe the resulting object and look for all jobs which are enabled that have failed.

[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")|out-null
$srv=New-Object "Microsoft.SqlServer.Management.Smo.Server" "TUIRA"
$srv.jobserver.jobs|where-object {$_.IsEnabled -eq $TRUE -and `
    $_.LastRunOutcome -eq "Failed"
}

You will notice the result not being in a desirable format, especially when you have many jobs. We will use the information from one of the jobs to pick the specific properties we want in our report and display them in a table format. Lets pick the Parent, Category, Description, LastRunDate, LastRunOutcome, & Name properties for our next result.
On your PS session screen, hit the up key and you should get the last line of commands we executed. Hit the left arrow key to modify the command as follow

$srv.jobserver.jobs|where-object {$_.isenabled -eq $TRUE}|Format-Table `
    Parent, Category, Name, LastRunDate, LastRunOutcome, Description `
    -Autosize

Now we have a result we can save to a file or print. But before doing that, we’ll expand this script to retrieve the jobs from multiples servers. Lets go ahead and open a Notepad session and enter the name of a few MS SQL servers you manage, one name per line, and save it with the file name serverlist.txt. I saved mine in C: for simplicity. Now, we just need to enclose our previously executed commands in a loop.

[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")|out-null
foreach($svr in get-content C:serverlist_prd.txt) {
    $srv=New-Object "Microsoft.SqlServer.Management.Smo.Server" "$svr"
    $srv.jobserver.jobs|where-object {$_.isenabled -eq $TRUE}| `
        Format-Table Parent, Category, Name, LastRunDate, `
        LastRunOutcome, Description -Autosize
}

I find this looping syntax very helpful whenever I need to execute code against multiple servers. The result from this modified version of the original code will leave a blank space between each server or iteration of the code. We’ll solve that with a variable in the next and final version of the script which includes the code to save the report to a file.

CLEAR
Set-Content -Path "C:TempMonitorJobs_LowerEnvironments.txt" -Value " "
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")|out-null

foreach($svr in get-content C:tempserverlist.txt) {
    $srv=New-Object "Microsoft.SqlServer.Management.Smo.Server" "$svr"
    $a += $srv.jobserver.jobs
}

$a|where-object {$_.isenabled -eq $TRUE -and $_.LastRunOutcome `
    -eq "Failed"}|Sort-Object -Descending LastRunDate| `
    Format-Table Parent, Category, Name, LastRunDate, `
    LastRunOutcome, Description -Autosize|Out-File `
    -FilePath C:TempMonitorJobs.txt -Append -Encoding ASCII


How to Script Database Tables with PowerShell

PROBLEM: I need an easy way to script out all the tables from a database in MS SQL 2000 and above.

SOLUTION: Like anything in programming, there are a thousand ways to skin a cat. Here, I’ll show you the simplest way I found to meet this request.

Go ahead and open a new session of PowerShell. You can type or copy and paste in your PS session window all the code in this solution. We will start by loading the SMO assembly.

[system.reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO")|out-null

Next, we’ll create an instance of SMO server object, database object, and scripter object

$srv = new-object "Microsoft.SqlServer.Management.SMO.Server" "."
$db = new-object("Microsoft.SqlServer.Management.SMO.Database")
$scr = new-object("Microsoft.SqlServer.Management.SMO.Scripter")

We then assign the instance of MS SQL Server we want the scripter to use. We wrap it up by calling the Script() method with an SQL SMO collection of objects to be script out –in this case, all tables.

$scr.Server = $srv
$scr.Script($srv.Databases["AdventureWorks"].tables)

That’s it! The simplest way to script out all tables from a database with PowerShell. In a future article, I’ll expand from this solution by writing a script that will take parameters, use SMO Scripter Options, filter tables to be script out, and then some.

T-SQL Programming

T-SQLProgrammingI purchased my copy back in 1999 and it is one of my favorite SQL books. I find the information very accurate and relevant to the MS SQL versions (6.5 & 7.0) printed on the cover of the book. I think it was the best SQL book for many years. Since 1999, many books for newer versions of database servers have been published.

For some reason, many people who wrote bad reviews about this book were looking for MS SQL 2000 information -the cover states the versions covered in the book.

There are many newer books that cover MS SQL 2000. One of my favorites is SQL Server 2000 Programming from Robert Vieira.

SQL Server 2000 Programming

Professional SQL Server 2000 Programming by Robert VieiraBy far, this is the best overall MS SQL 2000 server book written until today. Most topics are covered from basic to advance level. This book is a must have for anyone who is looking to get a well rounded knowledge of what’s possible with MS SQL 2000.

The chapters I liked the most are:
Ch9 SQL Server Storage and Index Structures
Ch10 Views
Ch12 Stored Procedures
Ch15 Triggers
Ch16 Advanced Queries
Ch26 Full-Text Search
Ch29 Performance Tuning
All Appendixes

Noticed that I said “best overall” & “Most topics”; this book will not have an answer for very advance topics in areas like DTS, performance tuning, XML, & Analysis Services. There are great books out there if what you are looking for is not in Professional SQL Server 2000 Programming. The “Professional SQL Server 2000 DTS” is the best I have read when it comes to Data Transformation Services. For performance tuning and XML you may find books written by Ken Henderson to be among the best. For Analysis Services I have not found one book I will recommend yet.

Buena Idea Mal Implementada

Una de las razones por la cual decidí escribir este artículo es porque alguien me sugirió utilizar el procedimiento de sistema SP_DEPENDS para encontrar las tablas que se usan en un SP (procedimiento almacenado). Al iniciar mis pruebas con el SP_DEPENDS utilizando la base de datos AdventureWorks todo parecía andar muy bien, pero cuando lo utilicé en una base de datos que ha estado funcionando por varios años, descubrí algunas fallas en él.

La idea principal del procedimiento SP_DEPENDS y la tabla de sistema SYSDEPENDS es la de tener un mecanismo que permita guardar información de la dependencia entre los objetos de la base de datos. Por ejemplo, si tenemos TABLA A, TABLA B y un SP C que haga referencia a ambas tablas mediante una consulta SELECT, podríamos ejecutar el SP_DEPENDS pasando el nombre de cualquiera de los tres objetos y veríamos que A esta relacionado a C, B esta relacionado a C y que C esta relacionado a A y B. Ahora bien, si ejecutamos el comando DROP TABLE A y luego recreamos la TABLA A, lo lógico seria que si ejecutamos el SP_DEPENDS nuevamente deberíamos obtener el mismo resultado que vimos anteriormente –pero no es así.

La falla esta en que cuando ejecutamos el comando DROP , todos los registros relacionados con el objeto que estamos eliminando se borran del SYSDEPENDS y aunque volvamos a crear el mismo objeto, las relaciones no se crean nuevamente. Volvamos al ejemplo anterior para explicarlo más detalladamente. Si se ejecuta un DROP TABLE A, entonces todos los registros en la tabla SYSDEPENDS de la TABLA A se borran y la dependencia A > C se desaparece. Al ejecutar el SP_DEPENDS con el objeto C como parámetro podremos observar que la unica relacion que existe es con la TABLA B aunque se aya creado la TABLA A nuevamente.

Así que bien, manténganse alejados del uso del SP_DEPENDS y SYSDEPENDS por ahora. Yo estaré haciendo pruebas con el servidor SQL 2005 a ver si Microsoft se tomó el tiempo y arregló este problema.

Puedes descargar el código aquí y no te olvides de añadir tus comentarios.

RECURSOS: –The Guru’s Guide to Transact-SQL
Transact-SQL Programming

The Object Within

Finding tables referenced in a stored procedure

Recently, I was asked for an easy way to list the tables used in a stored procedure. I looked in my list of previously used scripts and found one that did not fully satisfy the requirements of the task. So I decided to come up with a solution that will accurately list all tables referenced in a stored procedure. The solution is pretty straight forward and I’ll describe some of the 12 steps in the code that helped me find all referenced tables within it.

Overview of the problem
Sometimes it is necessary to know which tables are referenced or used in a stored procedure. If the stored procedure you’re working with, is one of those mega procedures of thousands of lines of code, it will be very tedious to go through line by line to find all tables referenced within the procedure.

While developing this solution, I encountered some issues rooted in the fact that stored procedures may be written in many different ways, especially the areas that make reference to tables. For example, take a look at the code in List 1 and see three ways you may write the same query.

There were so many variables that made the development of this solution very interesting. The sample code in List 1 reveals only the SELECT statement with three possible ways I would find reference to tables in stored procedures. Take into consideration that we need to account for the UPDATE, INSERT, and DELETE statements and all the different ways we may find them in the body of stored procedures.

SELECT a.Col1
       b.Col2
  FROM Table1 a,
       Table2 b
 WHERE a.ID = b.ID
SELECT a.Col1,
       b.Col2
  FROM Table1 a, Table2 b
 WHERE a.ID = b.ID
SELECT a.Col1,
       b.Col2
  FROM Table1 a
 INNER JOIN Table2 b ON a.ID = b.ID

First, I created two temporary tables. Table one (dbo.#Tables) will hold the name of tables and views from the database and table two (dbo.#Procedure) will hold the text from the stored procedure. Later in the script, I’ll verify the list of possible table names and view names found in the stored procedure (dbo.#Procedure) against the values in the first table (dbo.#Tables)to validate possible matches with the real object names.

Table one is populated with data from the SYSOBJECTS table WHERE type IN(‘U’,’V’). Notice in List 2 how I convert the data extracted from sysname data type to varchar to avoid any data comparison problems later in the code. I also made sure to TRIM the values from the SYSOBJECTS table.

INSERT dbo.#Tables(TableName)
SELECT CONVERT(VARCHAR(35), LTRIM(RTRIM(Name)))
  FROM dbo.SYSOBJECTS
 WHERE Type IN ('U','V')

You may need to check the maximum object name length in your SYSOBECTS table and update the table definition for dbo.#Tables and the CONVERT statement in List 2 in case the value of 35 is not long enough.

I used the SP_HELPTEXT extended procedure to populate table two. This process will insert one record per line of code returned by the extended procedure.

The following steps will modify the values stored in table two. I replaced any tabs, line feeds, and carriage returns with two spaces which will enable me to use the CHARINDEX() function later on in the solution. I also removed any rows with comments, temporary objects and the CREATE PROCEDURE statement since none of these rows should have the table names I’m looking for. I then looked for the first reference to a real table and removed all rows previous to this row.

The code in List 3 shows how I marked any rows with possible table names by setting the "PossibleTable" column to 1 and then removed all rows with NULL value in the same column.

SELECT @PossibleTable = Texto
  FROM dbo.Procedure
 WHERE Texto LIKE '%,%' AND
       Texto NOT LIKE '%(%';

IF @PossibleTable IS NOT NULL
   BEGIN
   INSERT dbo.#Procedure (PossibleTable, Texto)
   SELECT 1 AS PossibleTable,
          TableNames
     FROM dbo.fnParseCommaDelimitedList (@PossibleTable);
END

The UDF (User Defined Function) in List 4 is used on those rows that may have multiple table names separated by commas. The UDF takes one single parameter of up to 255 characters long and returns a table. It basically inserts one row in the return table for each value separated by a comma from the input parameter.

CREATE FUNCTION dbo.fnParseCommaDelimitedList (
       @myString VARCHAR (255))

RETURNS

@TableNames TABLE (
            TableNames VARCHAR (255))

AS

BEGIN

DECLARE @myPosition AS INT, 
        @myLen AS INT, 
        @myStart AS INT;

SELECT @myPosition = -1,
       @myString = @myString + ',',
       @myLen = LEN(@myString),
       @myStart = 1;

WHILE @myPosition < @myLen
      BEGIN

      INSERT @TableNames
      SELECT SUBSTRING(@myString, @myStart, CHARINDEX(',', @myString, 
                                 @myPosition + 1) - @myStart);

      SELECT @myPosition = CHARINDEX(',', @myString, @myPosition + 1);

      SELECT @myStart = @myPosition;
END
RETURN;
END

Conclusion
I have tested this solution against many stored procedures written in many different ways and I have found one condition that will cause some problems. It is on how you write your stored procedures and the width of your lines of code. You’ll need to increase the length of the "Texto" column in the dbo.#Procedure table If there are lines of code in your procedure longer than 255 characters. Make sure you have the necessary user rights to create the objects for this solution.

Download the code 01042006.sql and feel free to post your comments about this solution.

RESOURCES: BOL (MS SQL Books Online)

Top 25 Most Expensive Stored Procedures

USE: These queries will display the top 25 most expensive stored procedures that are still in the cache.

SET NOCOUNT ON

--[ REVIEW MOST EXPENSIVE TOP 25 CACHED STORED PROCEDURES ]
--[ LOGICAL READS RELATE TO MEMORY PRESSURE               ]
SELECT TOP(25) A.name [SP Name]
     , B.total_logical_reads [TotalLogicalReads]
     , B.total_logical_reads / B.execution_count [AvgerageLogicalReads]
     , B.execution_count
     , ISNULL(B.execution_count / DATEDIFF(SECOND, B.cached_time, GETDATE()), 0) [Calls/Second]
     , B.total_elapsed_time
     , B.total_elapsed_time / B.execution_count [AverageElapsedTime]
     , B.cached_time
  FROM sys.procedures A
 INNER JOIN sys.dm_exec_procedure_stats B ON A.[object_id] = B.[object_id]
 WHERE B.database_id = DB_ID()
 ORDER BY B.total_logical_reads DESC;
 
 
 
--[ REVIEW MOST EXPENSIVE TOP 25 CACHED STORED PROCEDURES ]
--[ PHYSICAL READS RELATE TO IO PRESSURE                  ]
SELECT TOP(25) A.name [SP Name]
     , B.total_physical_reads [TotalphysicalReads]
     , B.total_physical_reads / B.execution_count [AvgeragePhysicalReads]
     , B.execution_count
     , ISNULL(B.execution_count / DATEDIFF(SECOND, B.cached_time, GETDATE()), 0) [Calls/Second]
     , B.total_elapsed_time
     , B.total_elapsed_time / B.execution_count [AvgerageElapsedTime]
     , B.cached_time
  FROM sys.procedures A
 INNER JOIN sys.dm_exec_procedure_stats B ON A.[object_id] = B.[object_id]
 WHERE B.database_id = DB_ID()
 ORDER BY B.total_physical_reads DESC;