|
|
Recently I was trying to reproduce an error in a test environment and I needed to insert data that didn’t fit into the current constraints. Long story on how it got there, but it was something I had to deal with. The question someone asked me was could we disable a constraint – instead of dropping it, adding the data, then adding the constraint back without checking the existing data. It’s pretty simple to do using the NOCHECK clause. Some legit reasons to use this would be to:
- Bulk loaddata
- Reproducing errors
- Send a team of DBAs on a wild goose chase
To disable a constraint, the TSQL is:
ALTER TABLE dbo.<<TableName>> NOCHECK CONSTRAINT <<ConstraintName>>;
To enable it
ALTER TABLE dbo.<<TableName>> CHECK CONSTRAINT <<ConstraintName>>;
Here’s where I got my info from…
http://msdn.microsoft.com/en-us/library/ms190273.aspx

Recently, I was tasked with writing a job that would delete data — with a bunch of rules on what could be deleted and what couldn’t. I thought it was going to be fairly straight forward, but the finer points of the situation made it quite complicated.
The Situation:
The diagram below works as an example of the situation. Sometimes I needed to delete data from Table1 as well as all other dependent data, sometimes I needed to delete it from Table3, sometimes from Table4, etc. I wrote the procedures to do delete each set of dependent data, but I was never sure if I had gotten it all – the real life situation wasn’t this linear and sometimes tests would fail and I would add another table to my deletion steps. I needed a script…

The Setup:
I decided to create the situation above and see if I could come up with a script that would identify PKs that would cause my deletions to fail.
Here’s the script:
CREATE TABLE [dbo].[Table1](
[T1ID] [int] NOT NULL,
CONSTRAINT [T1ID_PK] PRIMARY KEY CLUSTERED
(
[T1ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Go
CREATE TABLE [dbo].[Table9](
[T9ID] [int] NOT NULL,
CONSTRAINT [T9ID_PK] PRIMARY KEY CLUSTERED
(
[T9ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Go
CREATE TABLE [dbo].[Table3](
[T3ID] [int] NOT NULL,
[T1ID] [int] NULL,
[T9ID] [int] NULL,
CONSTRAINT [T3ID_PK] PRIMARY KEY CLUSTERED
(
[T3ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Go
CREATE TABLE [dbo].[Table2](
[T2ID] [int] NOT NULL,
[T1ID] [int] NULL,
CONSTRAINT [T2ID_PK] PRIMARY KEY CLUSTERED
(
[T2ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Go
CREATE TABLE [dbo].[Table7](
[T7ID] [int] NOT NULL,
[T3ID] [int] NULL,
CONSTRAINT [T7ID_PK] PRIMARY KEY CLUSTERED
(
[T7ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Go
CREATE TABLE [dbo].[Table6](
[T6ID] [int] NOT NULL,
[T3ID] [int] NULL,
CONSTRAINT [T6ID_PK] PRIMARY KEY CLUSTERED
(
[T6ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Go
CREATE TABLE [dbo].[Table5](
[T5ID] [int] NOT NULL,
[T2ID] [int] NULL,
CONSTRAINT [T5ID_PK] PRIMARY KEY CLUSTERED
(
[T5ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Go
CREATE TABLE [dbo].[Table4](
[T4ID] [int] NOT NULL,
[T2ID] [int] NULL,
CONSTRAINT [T4ID_PK] PRIMARY KEY CLUSTERED
(
[T4ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Go
CREATE TABLE [dbo].[Table8](
[T8ID] [int] NOT NULL,
[T4ID] [int] NULL,
CONSTRAINT [T8ID_PK] PRIMARY KEY CLUSTERED
(
[T8ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Go
ALTER TABLE [dbo].[Table2] WITH CHECK ADD CONSTRAINT [FK_Table2_Table1] FOREIGN KEY([T1ID])
REFERENCES [dbo].[Table1] ([T1ID])
Go
ALTER TABLE [dbo].[Table2] CHECK CONSTRAINT [FK_Table2_Table1]
Go
ALTER TABLE [dbo].[Table3] WITH CHECK ADD CONSTRAINT [FK_Table3_Table1] FOREIGN KEY([T1ID])
REFERENCES [dbo].[Table1] ([T1ID])
Go
ALTER TABLE [dbo].[Table3] CHECK CONSTRAINT [FK_Table3_Table1]
Go
ALTER TABLE [dbo].[Table3] WITH CHECK ADD CONSTRAINT [FK_Table3_Table9] FOREIGN KEY([T9ID])
REFERENCES [dbo].[Table9] ([T9ID])
Go
ALTER TABLE [dbo].[Table3] CHECK CONSTRAINT [FK_Table3_Table9]
Go
ALTER TABLE [dbo].[Table4] WITH CHECK ADD CONSTRAINT [FK_Table4_Table2] FOREIGN KEY([T2ID])
REFERENCES [dbo].[Table2] ([T2ID])
Go
ALTER TABLE [dbo].[Table4] CHECK CONSTRAINT [FK_Table4_Table2]
Go
ALTER TABLE [dbo].[Table5] WITH CHECK ADD CONSTRAINT [FK_Table5_Table2] FOREIGN KEY([T2ID])
REFERENCES [dbo].[Table2] ([T2ID])
Go
ALTER TABLE [dbo].[Table5] CHECK CONSTRAINT [FK_Table5_Table2]
Go
ALTER TABLE [dbo].[Table6] WITH CHECK ADD CONSTRAINT [FK_Table6_Table3] FOREIGN KEY([T3ID])
REFERENCES [dbo].[Table3] ([T3ID])
Go
ALTER TABLE [dbo].[Table6] CHECK CONSTRAINT [FK_Table6_Table3]
Go
ALTER TABLE [dbo].[Table7] WITH CHECK ADD CONSTRAINT [FK_Table7_Table3] FOREIGN KEY([T3ID])
REFERENCES [dbo].[Table3] ([T3ID])
Go
ALTER TABLE [dbo].[Table7] CHECK CONSTRAINT [FK_Table7_Table3]
Go
ALTER TABLE [dbo].[Table8] WITH CHECK ADD CONSTRAINT [FK_Table8_Table4] FOREIGN KEY([T4ID])
REFERENCES [dbo].[Table4] ([T4ID])
Go
ALTER TABLE [dbo].[Table8] CHECK CONSTRAINT [FK_Table8_Table4]
Go
The Script:
I looked around the old information super highway and found Pinal Dave (Blog/Twitter) had something that fit into what I was looking for here:
SQL SERVER – Query to Display Foreign Key Relationships and Name of the Constraint for Each Table in Database
I tweaked it and used the ABCs of CTEs (from Jason Strate (Blog/Twitter) and came up with the following script to identify tables which have primary keys to the initial table, so you will never forget a table in a deletion again :
DECLARE @TableName VARCHAR(255) = 'Table1' --Change this to find the different situations.
;WITH FKStructure (K_Table, FK_Column, PK_Table, PK_Column, Constraint_Name, Level)
as
(-- Anchor member definition
SELECT
K_Table = FK.TABLE_NAME,
FK_Column = CU.COLUMN_NAME,
PK_Table = PK.TABLE_NAME,
PK_Column = PT.COLUMN_NAME,
Constraint_Name = C.CONSTRAINT_NAME,
1 AS Level
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
INNER JOIN (SELECT i1.TABLE_NAME, i2.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY') PT ON PT.TABLE_NAME = PK.TABLE_NAME
WHERE PK.TABLE_NAME= @TableName
UNION ALL
-- Recursive member definition
SELECT
K_Table = FK.TABLE_NAME,
FK_Column = CU.COLUMN_NAME,
PK_Table = PK.TABLE_NAME,
PK_Column = PT.COLUMN_NAME,
Constraint_Name = C.CONSTRAINT_NAME,
Level + 1
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
INNER JOIN (SELECT i1.TABLE_NAME, i2.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
) PT ON PT.TABLE_NAME = PK.TABLE_NAME
JOIN FKStructure FKS on FKS.K_Table = PK.TABLE_NAME)
Select * from FKStructure
The title says it all, but here’s the story. The other day I was modifying a procedure and I didn’t know if the application was going to pass in True/False or 1/0 for a bit column. Being lazy, I created my procedure with a 1 or 0 and figured I’d update it later if the app was using True/False. The App was passing in a True/False, but I didn’t need to update my procedure because SQL auto converts it. More info can be found here. It’s pretty basic, but it save me some time, so I figured I’d remind people. Here’s an example to show it:
Create table Test (IDCol int identity(1,1),
TestBit bit)
Insert Test Values(0)
Insert Test Values(1)
Select * from test
Insert Test Values('False')
Insert Test Values('True')
Select * from test
Here’s an XML example:
Declare @XMLTest xml
Set @XMLTest = '<Test>
<BitTest>False</BitTest>
</Test>
<Test>
<BitTest>True</BitTest>
</Test>'
Select x.x.value('BitTest[1]', 'bit')
from @XMLTest.nodes('/Test') as x(x)
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!
There are quite a few times I need to number rows of data based on some grouping column. I often forget how to do it. Well, here’s a couple of examples:
Create table TestTable1
(ID int,
InsertDate datetime)
Go
Insert TestTable1 (ID, InsertDate)
Values(1,'8/1/10'),
(1,'7/1/10'),
(1,'6/1/10'),
(2,'3/15/10'),
(2,'3/16/10'),
(2,'3/13/10'),
(2,'3/11/10')
--Note the values are out of order for ID 2
Select *,
(select count(*) from TestTable1 TT2
where TT1.ID = TT2.ID
and TT1.InsertDate < TT2.InsertDate) as 'rank'
from TestTable1 TT1
Order by ID, rank
Or a simpler way…
SELECT *,
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY insertdate ) as 'rank'
from TestTable1 TT1
Order by ID, rank
Here are some examples and explanations:
I’ve been tasked with the SQL portion of our data migration and conversion testing for our big release we’ve got coming up. What’s that mean? Well, we’ve got an upgrade coming to the application we support. We’ve got 15 databases on 7 servers. All of them have database schema changes coming, data moving around from one table to another, new tables getting created, old ones getting dropped, new columns populated from other databases, etc. I’ve got to make sure all the data that moves makes it there and is correct. Here was how I attacked it:
The High Level Overview:
- Script production databases from our log shipping servers
- Create two copies of each database on a test server(s)
- Upgrade one database of each set
- Compare schema on Pre and Post upgraded databases
- List out all the diffences
- Catagorize how each new column or table is populated
- Ignore the new tables that are populated on use or populated with defaults – no data migration
- Create Checksum and row count scripts for all the migrated data
- Run the scripts and record the results on our Prod-Stage environment before the upgrade
- Upgrade Prod-Stage databases
- Run the scripts and compare the results to the Pre-upgrade.
- Address any issues
This approached seemed to work well, so my plan is to write a post on a bunch of these individual steps, so stay tuned.

When someone states that adding a column to an existing table is easy and you need a list of risks, feel free to copy and paste this, add your own, etc. In fact, contact me via email, comments, twitter – @SQLQuill with any additions.
Adding a column to an existing table – in theory and best practices world is easy – but in reality it is not always as easy as ALTER TABLE ADD COLUMN. The risk is that any procedure, view or application code that uses a “Select *” or an “Insert <TableName> Values <…>” (without column names explicitly stated) may not function, or may not function correctly, with an additional column added to the table. Also note there could be temp tables, table variables and table value parameters built off the table that an additional column affects as well. Best practices and my database development guidelines says not to use the “Select *” or the “Insert <TableName> Values <…>”.
So apparently I like scripts that create random data. I’ve written a couple of posts that create random numbers and random characters. This week I was working on a clean up job that would delete data that was over 50 days old. To test my clean up routine, I had to combine the random number script with the DATEADD function – here’s the basis for it:
Select Dateadd(D, (-1)* ROUND((RAND() * (100)),0), Getdate())
Breaking it down, the ROUND((RAND() * (100)),0) section randomly chooses an integer from 0 to 100, then multiplies that by –1 as we want to go back in time. Finally the DATEADD function adds the negative random integer number of days to today’s date.
Here’s an example that populates a test table with 1000 rows of data:
SET NOCOUNT ON
Go
Declare @RandomDates Table (DateTest datetime)
Declare @Counter int
Set @Counter = 0
While @Counter < 1000
Begin
Insert @RandomDates (DateTest)
Select Dateadd(D, (-1)* ROUND((RAND() * (100)),0), Getdate()) --Random Date 0 to 100 days old
Set @Counter = @Counter + 1
End
Select Datediff(d,DateTest, getdate()) as DaysOld,
Count(Datediff(d,DateTest, getdate())) as Count
from @RandomDates
Group by Datediff(d,DateTest, getdate())
Order by DaysOld Desc

|
|