TSQL: Round to Nearest 15 Minute Interval

A colleague asked me if I had any TSQL to hand that would round down to a 15 minute interval and also round to nearest 15 minute interval. A quick search found several formulas but several had rounding errors. Here are both without any rounding errors.

declare @adate datetime = ‘2010/02/15 23:59:00’

 

— The epoch, or start of SQL Server time: ‘1900-01-01 00:00:00.000’

— select cast(0 as DateTime) as Epoch

 

— Both these formulas will only work until ‘5983-01-24 02:07:00.000’ !!

— select dateadd(n, 2147483647, cast(0 as DateTime))

 

 

— Round down to nearest 15 minute interval (avoiding any rounding issues)

select dateadd(n,(DATEDIFF(n, cast(0 as DateTime), @adate)/ 15) * 15, cast(0 as DateTime))

 

— Round to nearest 15 minute interval (avoiding any rounding issues)

select dateadd(n,((DATEDIFF(n, cast(0 as DateTime), @adate) + 7)/ 15) * 15, cast(0 as DateTime))

As noted, they have the limitation of working only until 5983 AD, but I figure I won’t be around!

Mark Russinovich: “Zero Day”

Microsoft Technical Fellow and all-round Windows Internals expert, Mark Russinovich, has an upcoming book release. And this time it’s not a new edition of ‘Windows Internals’!

His fiction debut, Zero Day, is set in a world completely reliant on technology (sounds familiar), and surrounds the events of cyber infrastructure attacks released on a largely unprepared world.

How to give great presentations

Assuming your user group is actually holding meetings :), User Group Support Services (UGSS) have released a series of videos on “How to give great presentations” aimed at first time speakers and anyone wanting to improve their skills (I know I need to):

This video series guides you through what you need to know to give your first presentation or to improve your existing presentation skills. You’ll learn how to choose the right subject for you, how to break subjects up so that your explanations and demonstrations are clear and understandable, how to construct your slide deck so that it covers essential subjects without sending your audience to sleep, why being nervous is completely normal and what you can do to make it manageable, tips and tricks for giving great demonstrations, how to prepare your laptop so that it does not fight you while you are presenting and finally how to deliver the presentation that you have worked so hard creating.

SQL Server 2008 ‘…/sec’ Performance Counters

SQL Server contains many Dynamic Management Views (DMV) for diagnosing server activity and health, including one for examining performance counters, sys.dm_os_performance_counters. Some of the counters in “sys.dm_os_performance_counters” with names ending with ‘/sec’ (such as like “Page lookups/sec”, or “Page writes/sec”) are known as “per second” counters. When you first come across this DMV you might not realise that the values for these counters are cumulative and require two measurements over an interval and then calculating the difference in those values based on the time between the two measurements.

Here is a simple TSQL snippet to perform this calculation for all per second counters in sys.dm_os_performance_counters:

-- Get first sample
SELECT *, getdate() as sampletime,
ROW_NUMBER() OVER (order by [object_name], instance_name, [counter_name]) as rowid
INTO #tempCounters1
FROM sys.dm_os_performance_counters
WHERE counter_name LIKE '%/sec%'
order by [object_name], instance_name, [counter_name];

-- Wait for 10 seconds
WAITFOR DELAY '00:00:10';

SELECT *, getdate() as sampletime,
ROW_NUMBER() OVER (order by [object_name], instance_name, [counter_name]) as rowid
INTO #tempCounters2
FROM sys.dm_os_performance_counters
WHERE counter_name LIKE '%/sec%'
order by [object_name], instance_name, [counter_name];

-- Calculate per second values
SELECT t1.[object_name], t1.instance_name, t1.[counter_name],
(t2.cntr_value - t1.cntr_value) / datediff(ss,t1.sampletime,t2.sampletime)
FROM #tempCounters1 t1 join #tempCounters2 t2 ON t1.rowid = t2.rowid;

DROP TABLE #tempCounters1
DROP TABLE #tempCounters2
GO
Here is a Table valued function version which accepts an interval parameter, and is easy to filter:
IF OBJECT_ID(N'dbo.PerSecondPerformanceCounters', N'TF') IS NOT NULL
DROP FUNCTION dbo.PerSecondPerformanceCounters;
GO

CREATE FUNCTION PerSecondPerformanceCounters(@intervalseconds int)
RETURNS @retCounterInformation TABLE
(
[object_name] nchar(128) not null,
[counter_name] nchar(128) not null,
[instance_name] nchar(128) not null,
[cntr_value] bigint not null
)
AS
BEGIN

DECLARE @tempTable1 TABLE
(
[object_name] nchar(128) not null,
[counter_name] nchar(128) not null,
[instance_name] nchar(128) not null,
[cntr_value] bigint not null,
[sampletime] datetime not null,
[rowid] int not null
)

DECLARE @tempTable2 TABLE
(
[object_name] nchar(128) not null,
[counter_name] nchar(128) not null,
[instance_name] nchar(128) not null,
[cntr_value] bigint not null,
[sampletime] datetime not null,
[rowid] int not null
)

-- Take first sample
INSERT INTO @tempTable1
SELECT [object_name],
[counter_name],
[instance_name],
[cntr_value],
getdate() as sampletime,
ROW_NUMBER() OVER (order by [object_name], [counter_name], [instance_name]) as rowid
FROM sys.dm_os_performance_counters
WHERE counter_name LIKE '%/sec%'
ORDER BY [object_name], [counter_name], [instance_name];

-- Wait for specified time
declare @start datetime = getdate()
while (1=1)
BEGIN
if datediff(ss, @start, getdate()) >= @intervalseconds
BREAK;
END

-- Take second sample
INSERT INTO @tempTable2
SELECT [object_name],
[counter_name],
[instance_name],
[cntr_value],
getdate() as sampletime,
ROW_NUMBER() OVER (order by [object_name], [counter_name], [instance_name]) as rowid
FROM sys.dm_os_performance_counters
WHERE counter_name LIKE '%/sec%'
ORDER BY [object_name], [counter_name], [instance_name];

---- calculate per second values (ignore fractional values)
INSERT @retCounterInformation
SELECT
t1.[object_name],
t1.instance_name,
t1.[counter_name],
(t2.cntr_value - t1.cntr_value) / datediff(ss,t1.sampletime,t2.sampletime) AS cntr_value
FROM @tempTable1 t1
join @tempTable2 t2 ON t1.rowid = t2.rowid;

RETURN;

END
GO


Here is how you would call it:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/white-space: pre;/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/white-space: pre;/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

-- Return only per second counters with non-zero values 

select * from PerSecondPerformanceCounters(10)
where cntr_value > 0

-- Return non-zero counter values for specified objects only

select * from PerSecondPerformanceCounters(10)
where cntr_value > 0 AND
(object_name = 'SQLServer:Buffer Manager' OR object_name = 'SQLServer:Access Methods')

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/white-space: pre;/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Useful SSMS Shortcuts

Here are a few useful SSMS shortcuts:

Ctrl-N  Open a new tabbed query window (with same credentials as current tab)
Ctrl–K + Ctrl–C / Ctrl–K + Ctrl–U Comment / Uncomment selected text
Ctrl-Shift-U / Ctrl-Shift-L Change selected text to upper / lowercase (wish it just affected keywords!)
Ctrl-L Include estimated execution plan
Ctrl-K Include actual execution plan
Ctrl+R Toggle Results pane
F4 Show Properties window
Ctrl+Shift+M Specify values for template parameters

Usual Suspects

Ctrl-F, Ctrl-H : Find/Replace

F5 or Ctrl-E   : Execute query

SQL Server Transaction Log size does not match the size of the data being loaded

I came across this cautionary tale from the SQL Server Development Customer Advisory Team, while looking for information on virtual log files (VLF):

The ISV created an empty database, setup a backup device, set the database recovery model to FULL, and then started loading data. They also intended to backup the transaction log occasionally to keep the log a manageable size. However, at the end of the load process although the database contained 92 GB of data, the log was only 1.2 GB. One would have expected that the transaction log in the case of FULL recovery would have been approximately the same size as the data.

After confirming the database was in FULL recovery mode, they tried BULK LOGGED and got the same result!

The reason? The database had never had an initial FULL backup!

From Books Online ‘Working with Transaction Log Backups’:

Before you can create the first log backup, you must create a full backup, such as a database backup or the first in a set of file backups. […]

Because there was no initial FULL database backup, the database ‘effectively’ remained in SIMPLE recovery mode; SQL Server was truncating the transaction log automatically.

So, for FULL and BULK-LOGGED recovery models, create a full database backup right after creating a database. (This initial Transaction Log backup will be ‘empty’ because the database contains no data)

Comparing the .NET Timer Classes

Quick comparison table taken from an excellent MSDN article: Comparing the Timer Classes in the .NET Framework Class Library 

System.Windows.Forms System.Timers System.Threading
Timer event runs on what thread? UI thread UI or worker thread Worker thread
Instances are thread safe? No Yes No
Familiar/intuitive object model? Yes Yes No
Requires Windows Forms? Yes No No
Metronome-quality beat? No Yes* Yes*
Timer event supports state object? No No Yes
Initial timer event can be scheduled? No No Yes
Class supports inheritance? Yes Yes No