Friday 23 August 2013

T-SQL: Reminders 1

For the last few month I have spent most of my development time working with T-SQL.  This is just down to the nature of the current project I am working on.  On other projects I will spend far less time on T-SQL tasks and more time on front-end and middle-tier development.  Over time, especially when I am working on projects that don't rely heavily on SQL, I tend to forget some of the useful commands and tricks that are available in T-SQL.

This post is just as a reminder to myself about some of those commands that I have seen in the past and forgotten about until recently, and some new ones I would like to remember (or find here) in the future.

These first two I have seen before:

SET STATISTICS TIME ON

This will turn on time statistics which in turn will produce time info in the messages pane after a query is run.

Example:
SET STATISTICS TIME ON

WAITFOR DELAY '00:00:02.5'


GO

Output:



SET STATISTICS IO ON

This will turn on IO statistics which in turn will produce IO info in the messages pane after a query is run.

Example:
SET STATISTICS IO ON

SELECT * FROM Person p
INNER JOIN Address a ON p.PostalAddressID=a.AddressID
WHERE IDNumber=456
GO

Output:










Once you no longer want time or IO statistics you can turn the statistics off using:
SET STATISTICS TIME OFF

GO

or
SET STATISTICS IO OFF

GO


And these ones I haven't:

REPLICATE

This function takes 2 parameters: REPLICATE(string_expressing, integer_expression). Whatever is contained in the string expression will be replicated the number of times specified in the integer_expression.

Example:
SELECT REPLICATE('abc', 3) AS replicate_example

Output:




BATCH INSERT TEST DATA
This trick reminds me of the strange word function that allows you to create random text in a word document.  Next time you have word open try typing (followed by Enter): =rand(9)

To batch insert into a table:

Example:
CREATE TABLE MyTest (Id int identity(1,1) primary key, Name varchar(100) not null)
GO

INSERT MyTest (Name) VALUES (CAST(NEWID() AS varchar(36)))
GO 5



SELECT * FROM MyTest

Output:




Wednesday 21 August 2013

tSQLt Age Calculator kata

Prior to writing the blog post below I did what I thought was a fairly thorough search of the internet to see if anyone had already produced an "Age Calculator kata".  I have since searched again and was rather embarrassed to find a fairly similar blog post on tSQLt (although not actually a kata) that discusses an age calculator in relation to discovering test cases .  Further more, I believe the post was created by one of the authors of tSQLt.  Although the kata below was inspired by the tests one of my colleagues wrote to calculate a person's age in a production project it is very similar to the existing solution presented here.


I recently did a whole lot of work to put together a tSQLt training course.  The course consisted of a number of exercises to demonstrate how, where and why you might use tSQLt tests. I also wanted to give the developers doing the training course some problems to solve that would help them understand some of the principles of TDD (and more specifically test first development).

In C# there are a number of katas that you can find online to help demonstrate TDD.  There aren't nearly so many for database unit testing. There are one or two places where you might find tSQLt katas online.  I think the best examples can be found at http://datacentricity.net/tag/kata/.

As there are so few tSQLt katas out there I thought I would try my hand at putting together a kata.  The kata I have created is an extension of the "Age Calculator kata", a kata that is great for demonstrating boundary conditions.

Here is my kata adapted for tSQLt.

User Story

Create a simple report that will return all people that have reached the legal age requirement to obtain their driver’s license. 

Business Rules


  • The report results should be based on a given current date (e.g. @CurrentDate).
  • The report should show the following columns: FullName (e.g. ‘Smith, John’), IDNumber and Age.

Possible Tests


  • Write a test to check for the existence of a scalar-valued function called ‘CalculateCurrentAge’.
  • Write tests against ‘CalculateCurrentAge’ to verify that the function correctly calculates the current age of a person given the person’s date of birth and the current date.  Make sure you consider the boundary conditions.  E.g. What about leap years? What if the current date is before the date of birth.
  • Write a test to check for the existence of a table-valued function called ‘LegalDrivingAgeReport’.
  • Write a test to confirm that only people who have reached the legal driving age appear in the report.
  • Write a test to verify that the FullName and IDNumber are returned.  The test should prove that the FullName is correctly formatted.



The Code

Use the code below to create the initial database schema.

IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='dbo' AND TABLE_NAME='Person')
BEGIN
       DROP TABLE [Person]
END
GO

IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='dbo' AND TABLE_NAME='Address')
BEGIN
       DROP TABLE [Address]
END
GO

CREATE TABLE [Address]
(
       AddressID int identity(1,1) not null,
       AddressLine1 varchar(200) not null,
       AddressLine2 varchar(200) null,
       City varchar(200) null,
       PostCode varchar(20) not null,
       Country varchar(100) not null,
       CONSTRAINT PK_Address PRIMARY KEY (AddressID)
)
GO


CREATE TABLE Person
(
       PersonID int identity(1,1) not null,
       FirstName varchar(100) not null,
       Surname varchar(100) not null,
       IDNumber varchar(20) not null,
       Email varchar(200) not null,
       DateOfBirth datetime not null,
       ResidentialAddressID int not null,
       PostalAddressID int not null,
       CONSTRAINT PK_Person PRIMARY KEY (PersonID),
       CONSTRAINT FK_ResidentialAddress FOREIGN KEY (ResidentialAddressID) REFERENCES [Address](AddressID),
       CONSTRAINT FK_PostalAddress FOREIGN KEY (PostalAddressID) REFERENCES [Address](AddressID)
)