Twitter

Follow SQLQuill on Twitter

Subscribe

MN Pass

A script to create missing default bindings

Today five people showed up in my cube at 5:15 saying that the new development databases are missing defaults.  I did some checking and the default objects were there, the columns with default constraints were there, but the columns with bindings to the default objects were missing the bindings.  I created a script to run on the source database.  The output of this script will be a script to run on the destination database to get things in sync.

Hopefully you won’t need it, with new tools like VS2010 and Redgate’s SQL Compare, but here it is if you do:

Select
     'EXECUTE sp_bindefault ''dbo.' + SO.Name + ''', N''dbo.' + OBJECT_NAME(SC.object_id) + '.' + SC.name + ''''
from sys.columns SC
Join sys.objects SO on SO.object_id = SC.default_object_id and type = 'D' and parent_object_id = 0
Order by SO.Name

TSQL Tip O’ the Week – 8/30/11 – Almost a Reboot

This weeks tip:

Anytime anyone runs any command on a SQL server, it changes the conditions of that SQL server.  Execution plans get created, different objects are stored in memory, etc.  I don’t like to use time as a performance tuning metric (I’m a big fan of logical reads), but most people do – especially executives, so when measuring execution time, it’s important to compare apples to apples.

Below is a script, which I call “Almost a Reboot” as it’s almost like rebooting server, that I run on a server anytime I’m tesing performance. I got the initial script from Kimberly Tripp (Blog/Twitter) at DevConnections in about 2004 – I’ve tweaked it when SQL2005 came out and it’s probably due for a few changes for SQL2008, so don’t be shy to offer suggestions.

My steps for performance tuning any SQL code are:

  • Run this script
  • Run the original code
  • Record the results
  • Make my change
  • Run this script again
  • Run the changed code
  • Record/Compare the results

--Almost a reboot--
DECLARE @DBName Varchar(255)
Set @DBName = <<SET DBNAME HERE>> --Fill this in to the database you want to flush

CHECKPOINT --writes dirty pages to disk
DBCC FREEPROCCACHE --clears entire plan cache
DBCC DROPCLEANBUFFERS --clears all clean data cache pages

DECLARE @intDBID INTEGER
SET @intDBID = (SELECT dbid FROM master.dbo.sysdatabases WHERE name = @DBName)

--Recompile the stored procedures
DBCC FLUSHPROCINDB (@intDBID) --clears all clean plan cache for specified database

Again, let me know if you have any good tips to update this.

Here are some related posts from the intraweb:

Previous Tips O’ the Week

Random Error Generator

I believe I first heard this idea at PASS in Rob Farley’s (blog/twitter) session, “The Incredible Shrinking Execution Plan”.  If you want to play a cruel trick on someone, insert this somewhere in a script.  I’ve actually used this in testing my deploys to see at what point things blow up.  Here’s the script to produce a random error.

DECLARE @ErrorMessage NVARCHAR(4000),
     @ErrorSeverity INT,
     @ErrorState INT;

Set @ErrorSeverity = ROUND((RAND() * (18)),0) –severity 0 –18
Set @ErrorState = ROUND((RAND() * (255)),0)

Raiserror('Operation not allowed',
     @ErrorSeverity,
     @ErrorState) with log

T-SQL Tuesday #016 – The Power of the CHECKSUM_AGG

This month’s T-SQL Tuesday, “Aggregate Functions” is hosted by Jes Schultz Borland (Blog/Twitter) aka grrlgeek.

The Task

One of my duties as a development DBA is to verify our databases are set up the same in each of our environments.  One of the tasks of this duty is to verify our static data/look-up tables are all the same in each environment.  I’ve got a good method for writing the insert/update statements – see this post – but I still need to verify the values are the same in our static data tables.  My tool of choice is the CHECKSUM_AGG function.

How do I do this?

The easiest way I’ve found to do this is to take the BINARY_CHECKSUM of each row in a table that has static data in it (i.e. no datetimes, timestamps, etc).  Then I take the CHECKSUM_AGG of the each row’s binary_checksum – thus giving me a Checksum for the entire table.  I do this on the same table in two different environments and I compare the value.  They will be equal if the values in the tables are the equal.  For a more in depth explanation of this, see this post.

How this grew…

This method described above is easy to do for one table manually, but I’m responsible for approximately 10 to 50 static data tables in each of our 15 databases – in each of our five environments per project, so I needed to automate it.  The good news is that we’ve got good naming standards for our tables, so all the static data tables end in “Type”.  So I created a script that would calculate a CHECKSUM_AGG of all the BINARY_CHECKSUMs of each of the tables ending in “type” in a database, I could export that to excel, run the same script on a different server and compare the results.  This worked for a bit, but I was doing the same thing on each database, so I added a bit of logic to include specified databases to my original script.  Now, I run this script on each  server and I compare the results.

The script:

A couple of highlights, the vSQLCall is included in the final output, so if there is a difference in values, you can easily run the single table script and compare the values of each row.  (I had to do this one time as an update statement that was missing a period and the values were different.)  Also, forgive the combination of temp tables and table value parameters – I like a temp table for the final results, so I can work with it and not drop it.

Create table #TestTable
(ID int NOT NULL IDENTITY (1, 1),
ServerName varchar(255),
DBName varchar(255),
TableName varchar(255),
vSQLCall nvarchar(2550),
ChecksumVal varchar(255))
--Add default for blank column
ALTER TABLE #TestTable ADD  CONSTRAINT [DF_testtable]  DEFAULT (' ') FOR [vSQLCall]

-- A work table to track the databases and the server name
Declare @TestTableDBs Table
(ID int NOT NULL IDENTITY (1, 1),
ServerName varchar(255),
DBName varchar(255))

Declare @icount int,
@TopNum int,
@sql1 nvarchar(2000)

--Initialize variables
Set @icount = 1

--Fill up the databases to do...Change DB list here if scope changes.
Insert @TestTableDBs
Select @@ServerName, Name from master.sys.databases
where name in (<<DBNAME1 goes here>>, <<DBNAME2 goes here>>, <<DBNAME3 goes here>>, ....)
Select @TopNum = Max(ID) from @TestTableDBs

--Fill in main table with all the tables needed to check
--Note any table name with '%type%' in the name
While @icount <= @TopNum
Begin
Set @sql1 = 'Insert #TestTable (ServerName, DBName, TableName) '
Select @sql1 = @sql1  + 'Select @@ServerName, Table_catalog, Table_Name from '+ DBName
+ '.information_schema.tables where table_type = ''BASE TABLE'' and table_name like ''%Type%'''
from @TestTableDBs where id = @icount
Exec (@sql1)
Set @icount = @icount + 1

End

---Now figure out the colums to of the tables and the SQL call to find the checksum
Declare @TableID int,
@CurDBName varchar(255),
@CurTableName varchar(255),
@Start int,
@loopStart int,
@end int,
@maxEnd int,
@vColumnName varchar(2000),
@sql nvarchar(2000)

Create table #TestTableColumns
(ID int NOT NULL IDENTITY (1, 1),
ServerName varchar(255),
DBName varchar(255),
TableName varchar(255),
columnNames varchar (2000))

--Initialize the variables
Select @Start = 1
Set @TableID = 1
Set @loopStart = 1

Select @maxEnd = Max(ID) from #TestTable

--Loop for each table to get the columns
While @Start <= @maxEnd
Begin

Select @CurDBName = DBName,
@CurTableName = TableName
from #TestTable where ID = @TableID

Set @vColumnName = ''
Select @sql =
'Insert #TestTableColumns
(ServerName,
DBName,
TableName,
columnNames)
Select @@serverName,
Table_Catalog,
Table_Name,
Column_Name
from ' + @CurDBName + '.information_schema.columns
Join #TestTable on DBName = Table_Catalog and TableName = Table_Name
where data_type <> ''timestamp''
and #TestTable.ID = ' + STR(@TableID)
Exec (@sql)

Select @end = Max(ID) from #TestTableColumns

--Loop through to get the column name for each table
While @loopStart <= @end
Begin
Select @vColumnName = @vColumnName + columnNames + ', ' from #TestTableColumns where id = @loopStart
Set @loopStart = @loopStart + 1
End --End of loop for all columns for each table

--Update #TestTable with the SQL call to get the checksum
update #TestTable
Set vSQLCall = 'Select CHECKSUM_AGG(BINARY_CHECKSUM(' + Left(@vColumnName, Len(@vColumnName) - 1)
+ ')) from ' + @CurDBName + '.dbo.' + @CurTableName
where ID = @TableID

Set @TableID = @TableID + 1
Set @Start = @Start + 1
End --End of loop for all tables
--No longer needed
DROP TABLE #TestTableColumns

---Finally execute the SQL in the column
Declare @Finalcount int,
@TopNum1 int,
@SQL2 nvarchar(2550)
Set @Finalcount = 1

Select @TopNum1 = Max(ID) from #TestTable

While @Finalcount <= @TopNum1
begin
Select @SQL2 = 'Update #TestTable Set CheckSumVal = (' + vSQLCall + ') where ID = ' + STR(@Finalcount) from #TestTable where ID = @Finalcount
EXEC (@SQL2)
Set @Finalcount = @Finalcount + 1
End

--Output the results
Select * from #TestTable

--Clean up
Drop table #TestTable

The Output

The Future

I’ve got plans to have Powershell run this on each server I specify, then combine and compare the results in a file and save all the supporting data.  Thus when I need the documentation, it’ll take about 3 minutes (2.5 of which will be getting coffee), instead of the 1 hour it’s currently listed in the schedule to take.

Add a Non-Nullable Column to a Table with a Non-Auto Generated Default Name

I’ve been a stickler for default names since reading Jason Strate’s (blog/twitter) post on cleaning up auto-generated names.  Today I had to create a non-nullable column with a default – can I do it in one step?  Yep – here’s an example:

ALTER TABLE <<TABLENAME>> ADD <<COLUMNNAME>> <<TYPE>> NOT NULL CONSTRAINT <<DEFAULTNAME>> DEFAULT (<<Value>>)

Here’s the example in action:

Create table Testtable
(id int)

Insert Testtable Values (1),(2),(3),(4),(5)

ALTER TABLE Testtable ADD DefaultTest BIT NOT NULL CONSTRAINT DF_Testtable_DefaultTest DEFAULT (0)

Select * from Testtable

--/*Now to drop this column, do it in this order */
Alter table Testtable alter column DefaultTest  bit null
Alter table Testtable Drop constraint DF_Testtable_DefaultTest
Alter table Testtable DROP column DefaultTest

Create Insert/Update Scripts for Static Data Tables

Here’s a script that will create an insert/update statement for a list of values.  This is a major time saver for me – I have to create scripts like this for people fairly often. It is designed for a simple look up table with an ID column and a some sort of description column such as this….

Create table TestAndysAwesomeInsertGenerator
	(TestID int,
	TestDesc varchar(50),
	TSTimestamp timestamp)

Now let’s say you needed to generate insert and/or update statements for this list of values like this:

  1. This is line1
  2. This is line2
  3. This is line3
  4. etc…

You could enter the data into a table, then script out the insert statements and worry about the update statements later – or create row update/insert and do a lot of copying and pasting and editing, etc.  Well here’s a shortcut I came up with…use this script that creates a script.

Set nocount on
--/*Declare the variables.*/
Declare @ScriptOutput varchar(Max),
	@tablename varchar(255),
	@Col1 varchar(255),
	@Col2 varchar(255),
	@Counter1 int,
	@Counter2 int,
	@DescValue varchar(255)

Declare @ValuesTable table ([ID] int IDENTITY(1,1), [Description] varchar(255))
Declare @FinalScript table ([Script] varchar(max))

----------------------------------------------------------------------------------------------
--/*  This is the section that needs to be changed each time for each table and the values. */
----------------------------------------------------------------------------------------------
Set @tablename = 'TestAndysAwesomeInsertGenerator'

Insert @ValuesTable
Values('This is line1'),
	('This is line2'),
	('This is line3'),
	('etc...')

--------------------------------------------------------------------------------------------
--/*  That's it - everything else should be automatic - if it's a simple table.   */
--------------------------------------------------------------------------------------------
Select @Counter1 = COUNT(*) from @ValuesTable
Set @Counter2 = 1

--/* if the table has more columns, hard code the column names in here. */
Select @Col1 = SC.name from sys.columns SC
Join sys.types ST on ST.user_type_id = SC.user_type_id
where SC.object_id = OBJECT_ID(@tablename)
and ST.name like '%int%'

Select @Col2 = SC.name from sys.columns SC
Join sys.types ST on ST.user_type_id = SC.user_type_id
where SC.object_id = OBJECT_ID(@tablename)
and ST.name like '%char%'

--Manipulate the string...
Set @ScriptOutput =
'IF NOT EXISTS (Select 1 from <<TableName>> where <<Col1>> = <<IDVal>>)
	Begin
		Insert <<TableName>> (<<Col1>>, <<Col2>>) Values (<<IDVal>>, <<DescVal>>)
	End
Else
	Begin
		IF NOT EXISTS (Select 1 from <<TableName>> where <<Col1>> = <<IDVal>> and <<Col2>> = <<DescVal>>)
		Update <<TableName>> Set <<Col2>> = <<DescVal>> where <<Col1>> = <<IDVal>>
	End
	'
Select @ScriptOutput = Replace(Replace(Replace(@ScriptOutput, '<<TableName>>', @tablename), '<<Col1>>', @Col1), '<<Col2>>', @Col2)
Insert @FinalScript Select '/* Begin Codes for ' + @tablename + '*/'
While @Counter2 <= @Counter1
Begin
	Select @DescValue = '''' + [Description] + '''' from @ValuesTable where [ID] = @Counter2
	Insert @FinalScript
		Select Replace(Replace(@ScriptOutput, '<<IDVal>>', @Counter2), '<<DescVal>>', @DescValue)
	Select @Counter2 = @Counter2 + 1
End

Insert @FinalScript Select '/* End Codes for ' + @tablename + '*/'
Select * from @FinalScript

Here is the output of this script:

/* Begin Codes for TestAndysAwesomeInsertGenerator*/
IF NOT EXISTS (Select 1 from TestAndysAwesomeInsertGenerator where TestID = 1)
	Begin
		Insert TestAndysAwesomeInsertGenerator (TestID, TestDesc) Values (1, 'This is line1')
	End
Else
	Begin
		IF NOT EXISTS (Select 1 from TestAndysAwesomeInsertGenerator where TestID = 1 and TestDesc = 'This is line1')
		Update TestAndysAwesomeInsertGenerator Set TestDesc = 'This is line1' where TestID = 1
	End

IF NOT EXISTS (Select 1 from TestAndysAwesomeInsertGenerator where TestID = 2)
	Begin
		Insert TestAndysAwesomeInsertGenerator (TestID, TestDesc) Values (2, 'This is line2')
	End
Else
	Begin
		IF NOT EXISTS (Select 1 from TestAndysAwesomeInsertGenerator where TestID = 2 and TestDesc = 'This is line2')
		Update TestAndysAwesomeInsertGenerator Set TestDesc = 'This is line2' where TestID = 2
	End

IF NOT EXISTS (Select 1 from TestAndysAwesomeInsertGenerator where TestID = 3)
	Begin
		Insert TestAndysAwesomeInsertGenerator (TestID, TestDesc) Values (3, 'This is line3')
	End
Else
	Begin
		IF NOT EXISTS (Select 1 from TestAndysAwesomeInsertGenerator where TestID = 3 and TestDesc = 'This is line3')
		Update TestAndysAwesomeInsertGenerator Set TestDesc = 'This is line3' where TestID = 3
	End

IF NOT EXISTS (Select 1 from TestAndysAwesomeInsertGenerator where TestID = 4)
	Begin
		Insert TestAndysAwesomeInsertGenerator (TestID, TestDesc) Values (4, 'etc...')
	End
Else
	Begin
		IF NOT EXISTS (Select 1 from TestAndysAwesomeInsertGenerator where TestID = 4 and TestDesc = 'etc...')
		Update TestAndysAwesomeInsertGenerator Set TestDesc = 'etc...' where TestID = 4
	End

/* End Codes for TestAndysAwesomeInsertGenerator*/

This takes care of the data – here are some test cases, run one or both of these scripts, then re-run the output script above and the data will be back to what you initially set it for.

Delete from TestAndysAwesomeInsertGenerator where TestID = 2
--
Update TestAndysAwesomeInsertGenerator set testdesc = 'Row Updated' where testid = 4
--
Select * from TestAndysAwesomeInsertGenerator

Create a List of Links from Google Reader Starred Items with Powershell

Inspired by Jason Strate’s (Blog| Twitter) post “PowerShell: Download List of SQL Server MVPs”, which was inspired by John Samson’s (Blog| Twitter) post “The Best Database Administrators Automate Everything” – which also inspired me.  I decided to create a Powershell script to grab my starred items in Google Reader and format them into an HTML Page with links for my Weekly Challenges/Good Reading Posts.

The Set Up

I had an HTML template created for these pages, so I split it into three parts:

  1. The portion above the Good Reading and Links – I saved this to a text file located at “c:\temp\TopTemplate.txt”
  2. The Good Reading and Links
  3. The portion below the Good reading and Links – I saved this to a text file located at “c:\temp\BotTemplate.txt”

I then looked at my public page for Starred Items on Google Reader.  Note this script works for any public listing on Google Reader, so I can create a Public Page and change the URL in this script and it still works.

The Code

##------------------------##
##--Reader Starred Items--##
##------------------------##
$webclient = New-Object system.net.webclient
$SourceFile = "c:\temp\Reader.txt"
$HTMLPage = "C:\temp\GoodReading.txt"
$TopTemplate = "c:\temp\TopTemplate.txt"
$BotTemplate = "c:\temp\BotTemplate.txt"
$url="http://www.google.com/reader/shared/user/14135896925018987441/state/com.google/starred"
$webclient.DownloadFile($url,$SourceFile)
#Get the titles with links
$itemtitle = Select-String $SourceFile -pattern "h2 class=""item-title""" |
% {$_ -replace "<div id=""items""><div class=""item""><h2 class=""item-title""><div class="""">", ""} |
% {$_ -replace "</div></h2>", ""} |
% {$_ -replace "<div class=""clear""></div></div></div> <div class=""item""><h2 class=""item-title""><div class="""">", ""} |
% {$_ -replace "c:\\temp\\Reader\.txt:[0-9]*:", ""}
#Get the details with links
$iteminfo = Select-String $SourceFile -pattern "<div class=""item-info"">" |
% {$_ -replace "c:\\temp\\Reader\.txt:[0-9]*:<div class=""item-info"">", ""} |
% {$_ -replace "</div>", ""}
$i=0
$FinalContent = " "
foreach ($t in $itemtitle)
{$FinalContent= $FinalContent +"<li>"+ $itemtitle[$i] + " " + $iteminfo[$i]+"</li>" + "`r`n "
$i++}
$FinalContent = "<p><strong>Good Reading from the Week</strong>:</p>" + "`r`n" + "<ul>" + "`r`n" + $FinalContent + "`r`n" + "</ul>"
$TopTemp = Get-Content $TopTemplate
$BotTemp = Get-Content $BotTemplate
$FinalContent = $TopTemp + $FinalContent + $BotTemp
Set-Content $HTMLPage $FinalContent
invoke-item $HTMLPage

When the script completes, I simply update a couple of statuses and change the photo and it’s good to go!

Index Usage with Row Counts

I got into a bit of a situation today.  There were some performance issues in our performance testing environment and my changes (and a few others) were finally being tested.  I was also told by various levels of management not to work on this project – concentrate my efforts on Project B.   All I could think was my changes had to be using the correct indexes – how could I tell with out a lot of work – DMVs on index usage stats.  I fired up google and found this from mssqltips.com.  I changed the select a little and added the fairly accurate way to get row counts as a CTE.  Here’s what I came up with:

;With RowCounts_CTE as
(SELECT
so.id,
[RowCount] = MAX(si.rows)
FROM
sysobjects so,
sysindexes si
WHERE
so.xtype = 'U'
AND
si.id = OBJECT_ID(so.name)
GROUP BY
so.id, so.name)

SELECT   OBJECT_NAME(S.[OBJECT_ID]) AS [OBJECT NAME],
I.[NAME] AS [INDEX NAME],
RC.[RowCount],
USER_SEEKS,
USER_SCANS,
USER_LOOKUPS,
USER_UPDATES
FROM     SYS.DM_DB_INDEX_USAGE_STATS AS S
INNER JOIN SYS.INDEXES AS I
ON I.[OBJECT_ID] = S.[OBJECT_ID]
AND I.INDEX_ID = S.INDEX_ID
Join RowCounts_CTE RC on RC.id = S.object_id
WHERE    OBJECTPROPERTY(S.[OBJECT_ID],'IsUserTable') = 1
Order by 1

How To: Replace a string in all files in a directory with Powershell

Today I realized I made a fairly significant mistake.  I had misspelled a column name in a bunch of procedures and views that I had updated.  Not a big deal, except each procedure and view was a file and I had about 75 files.  Only a few of them contained this change, I don’t know which ones, but most of them had other changes.  What to do…

  • I could manually check all files with a few routine of Keystrokes – that might take an couple of hours.
  • I could check sysdepends for dependancies – nope none of the procedures have been executed.
  • I could check syscomments for the column name – that would kind of work, but I would still need to manually update it.
  • Powershell!!  I could find and replace in the files and execute them as well!

I used the following sites as they have good examples.

Here’s what I came up with:

##Finds all the occurrences in the Views folder##
foreach ($f in Get-ChildItem -path "C:\Temp\Views\" -Filter *.sql | sort-object)
{$Location = $f.fullname
Select-String -path $Location -pattern "Andee"| out-file -filePath "C:\Temp\ListoObjects.txt" -append}

##Replaces all the occurrences in the Views folder##
foreach ($f in Get-ChildItem -path "C:\Temp\Views\" -Filter *.sql | sort-object)
{(Get-Content $f.fullname) |
Foreach-Object {$_ -replace "Andee", "Andy"} |
Set-Content $f.fullname}

How To: find if a stored procedure has been run recently

Today I was asked which procedure (of two with similar names) is used in production.  Looking at the naming convention, I had a guess, but I needed proof as we are looking to clean up old unused procedures.  So I found this article Monitoring Stored Procedure Usage at Database Journal.  I modified the script to this:

Select DB_NAME(st.dbid) DBName
    ,OBJECT_SCHEMA_NAME(st.objectid,dbid) SchemaName
    ,OBJECT_NAME(st.objectid,dbid) StoredProcedure
    ,max(cp.usecounts) Execution_count
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
where DB_NAME(st.dbid) = '<<SET DBNAME HERE>>'
and cp.objtype = 'proc'
and OBJECT_NAME(st.objectid,dbid) in ('<<LIST PROCS HERE>>')
Group by cp.plan_handle, DB_NAME(st.dbid),
OBJECT_SCHEMA_NAME(objectid,st.dbid),
OBJECT_NAME(objectid,st.dbid)
order by max(cp.usecounts) desc