Tuesday, November 29, 2016

SQLCMD vs. Registered Servers

I always like it when my regular testing produces a usable example of something fun. For example, for a long time I have held that even on a local VM, connecting using SQLCMD is slower than Registered Servers.

I love SQLCMD, don't get me wrong, and I've written some very useful system maintenance scripts that would be very, very difficult to have gotten right any other way. That said, in my opinion, SQLCMD is just another tool for us to use when solving various problems.

For what I wanted to do with this test, though, was run a dead-simple query, SELECT GETDATE(); against all seven SQL 2016 instances on my VM.

Using SQLCMD, it took 29 seconds to run, and using Registered Servers, the same query against all seven instances took less than one second. Amazing performance.

I then added a bit of timing metrics to the output of the SQLCMD version, and figured out that SSMS, using Registered Servers, is so much faster because it's a multi-threaded, multi-connecting application. SQLCMD is, but it's very nature, single threaded, due to running all commands in one SPID.

Like I said earlier, I've had great experiences using SQLCMD and use it regularly for a whole host of things, but it's selling point isn't speed.



Tuesday, November 22, 2016

Enjoy your Thanksgiving!


If you're in the US, enjoy your time with family and friends this week for the Thanksgiving Holiday!

Tuesday, November 15, 2016

Another "Oh Nice!" Moment

I didn't realize you can log in to a remote instance using SQL Server authentication and SQLCMD from with SSMS.

The syntax looks like this:

:CONNECT servername\instance -U usernamegoeshere -P passwordgoeshere
GO
SELECT GETDATE();
GO

Tuesday, November 8, 2016

Summit 2016 Wrap Up




This year, I was fortunate enough to be able to attend PASS Summit 2016.

As a first timer, I have a few post-Summit thoughts:


  1. Take hand sanitizer next time. Wound up having to buy some from a local pharmacy, and it was an easily packed item.
  2. Pack extra vitamin C + cold/flu prevention strategy. Again, I ran out, and had to buy some from a local place.
  3. Take an extra suitcase. Books, goodies, mementos for the kids all add up, and it would have been easier to pay for a second checked bag than throwing things away.
  4. Remember the kilt. It was awesome seeing the display of support for the Women In Technology lunch.
  5. Build in a weekend before or after to decompress, best spent in Seattle doing "stuff". I flew in on far too tight a timeline this time around, and missed seeing all the city had to offer due to an almost punishing schedule.



Tuesday, November 1, 2016

The type initializer has failed ! Oh Noes!

So I was trying to connect to my VM, and I got a very strange error.


The full error message says

Cannot connect to
The type initializer for "Microsoft.SqlServer.Management.Common.ConnectionInfoHelper" threw an exception. (Microsoft.SqlServer.ConnectionInfo)
AccessToken (Microsoft.SqlServer.ConnectionInfo)

As I was researching why my copy of Management Studio was unable to connect, I realized I was still on a previous version, 13.0.15700.28, and that the most current version was 13.0.16000.28.




One upgrade later to the latest release of Management Studio 2016, and I'm back in business, and able to connect successfully.




Wednesday, October 19, 2016

Getting Perfmon Data

In so many of the SQL Server instances I manage, getting to the console isn't possible (or isn't advisable) and having the ability to get these things via SQL is a great help.


To do that, we can grab a quick select like this, to see the various counters available:


select *
from sys.dm_os_performance_counters



For example, if I wanted to monitor how many user connections I had throughout the day, I could save the results of the query below to a monitoring table.


select *
from sys.dm_os_performance_counters
where object_name = 'MSSQL$WORLDWIDE:General Statistics'
and counter_name = 'User Connections';

Tuesday, October 11, 2016

Creating a stored proc in every database

So, the other day, I needed to create a stored procedure in every database. This is what I came up with.


First, we need to make a test set of simple, empty databases:

use master;
go

create database CreateSPTest1;
create database CreateSPTest2;
create database CreateSPTest3;
create database CreateSPTest4;
create database CreateSPTest5;
go

Now, here was my first pass at writing a simple loop to get an object created in the target databases.

declare @currentDB sysname;
declare @sqlToRun nvarchar(max);
declare LoopDBs cursor for
select [name] from sys.databases where name like '%SPTest%';
open LoopDBs;
fetch next from LoopDBs into @currentDB;
while @@FETCH_STATUS = 0
begin
set @sqlToRun = '';
set @sqlToRun = 'USE ' + @currentDB + '; create table t1(c1 int);';
--print @sqlToRun;
exec sp_executesql @sqltorun;
fetch next from LoopDBs into @currentDB;
end
close LoopDBs;
deallocate LoopDBs;
GO



Next, I wanted to extend my loop, above, to create an uber-simple stored procedure in every database. Here's where I hit my first roadblock. No matter what I seemed to, I received an error that looked like this:

Msg 111, Level 15, State 1, Procedure usp1, Line 1 [Batch Start Line 36]
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.

For what it's worth, here's the loop I was using. As you'll see, the only difference between this and the snippet above is the set @sqlToRun variable.

declare @currentDB sysname;
declare @sqlToRun nvarchar(max);
declare LoopDBs cursor for
select [name] from sys.databases where name like '%SPTest%';
open LoopDBs;
fetch next from LoopDBs into @currentDB;
while @@FETCH_STATUS = 0
begin
 set @sqlToRun = '';
 set @sqlToRun = 'USE ' + @currentDB + '; create proc usp1 as select getdate();';
 --print @sqlToRun;
 exec sp_executesql @sqltorun;
 fetch next from LoopDBs into @currentDB;
end
close LoopDBs;
deallocate LoopDBs;
GO



Lastly, this is what I came up with after a quick Google-Fu. In short, I found that sp_executesql calls can be nested, and, if the first call to sp_executesql contains the USE statement, then the second will operate against the proper database.


declare @currentDB sysname;
declare @sqlToRun nvarchar(max);
declare @sqlForInside nvarchar(max);
declare @params nvarchar(4000);
set @params = '@sqlToRunInside nvarchar(max)';
declare LoopDBs cursor for
select [name] from sys.databases where name like '%SPTest%';
open LoopDBs;
fetch next from LoopDBs into @currentDB;
while @@FETCH_STATUS = 0
begin
 set @sqlForInside = 'create proc usp1 as select getdate();';
 --print @sqlToRun;
 set @sqlToRun = '';
 set @sqlToRun = 'USE ' + @currentDB + '; exec sp_executesql @sqlToRunInside';
 exec sp_executesql @sqltorun, @params, @sqlToRunInside = @sqlForInside;
 fetch next from LoopDBs into @currentDB;
end
close LoopDBs;
deallocate LoopDBs;
GO