|
|
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
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
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
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
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:
- This is line1
- This is line2
- This is line3
- 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
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:
- The portion above the Good Reading and Links – I saved this to a text file located at “c:\temp\TopTemplate.txt”
- The Good Reading and Links
- 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!
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
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}
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
|
|