Tuesday, 13 May 2014

Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.

Or

Can not execute SQL Server RMO PublicationMonitor TransPendingCommandInfo from a .NET 4.0 application.

or

Load LegacyV2Runtime at runtime

There are a couple of solutions to this issue. The simplest one is to add the following to App.config

xml version="1.0"?>
   
     useLegacyV2RuntimeActivationPolicy="true">    
          version="v4.0" sku=".NETFramework,Version=v4.0"/>  
    


That will allow you application to use Mixed mode assemblies (CLR 2 and 4 in this case).

However, I needed to do this programmatically at runtime as the error was occurring in a reusable user control, so I do not have access to the App.config. To do this, I borrowed the following helper class to load legacy V2 runtime :

public static class RuntimePolicyHelper
{
    public static bool LegacyV2RuntimeEnabledSuccessfully { getprivate set; }
 
    static RuntimePolicyHelper()
    {
        ICLRRuntimeInfo clrRuntimeInfo =
        (ICLRRuntimeInfo)RuntimeEnvironment.GetRuntimeInterfaceAsObject(
        Guid.Empty,
        typeof(ICLRRuntimeInfo).GUID);
        try
        {
            clrRuntimeInfo.BindAsLegacyV2Runtime();
            LegacyV2RuntimeEnabledSuccessfully = true;
        }
        catch (COMException)
        {
            // This occurs with an HRESULT meaning 
            // "A different runtime was already bound to the legacy CLR version 2 activation policy."
            LegacyV2RuntimeEnabledSuccessfully = false;
        }
    }
 
    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("BD39D1D2-BA2F-486A-89B0-B4B0CB466891")]
    private interface ICLRRuntimeInfo
    {
        void xGetVersionString();
        void xGetRuntimeDirectory();
        void xIsLoaded();
        void xIsLoadable();
        void xLoadErrorString();
        void xLoadLibrary();
        void xGetProcAddress();
        void xGetInterface();
        void xSetDefaultStartupFlags();
        void xGetDefaultStartupFlags();
 
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
        void BindAsLegacyV2Runtime();
    }
}
 
 To use this, just call and check the LegacyV2RuntimeEnabledSuccessfully before creating your class. e.g.


if (RuntimePolicyHelper.LegacyV2RuntimeEnabledSuccessfully)
{
     DataAccessConnections.Publisher = _publisher;
 
     _replMonitorHelper = new ReplicationMonitorHelper();
} .....

Tuesday, 25 March 2014

Merge Replication and Geography types Error converting data type varchar to geography

If your table has Geography data types, then with Merge Replication you will get the following error:

Error messages:
The Merge Agent failed because the schema of the article at the Publisher does not match the schema of the article at the Subscriber. This can occur when there are pending DDL changes waiting to be applied at the Subscriber. Restart the Merge Agent to apply the DDL changes and synchronize the subscription. (Source: MSSQL_REPL, Error number: MSSQL_REPL-2147199398)
Get help: http://help/MSSQL_REPL-2147199398
Error converting data type varchar to geography. (Source: MSSQLServer, Error number: 8114)
Get help: http://help/8114
The process was successfully stopped. (Source: MSSQL_REPL, Error number: MSSQL_REPL-2147200999)
Get help: http://help/MSSQL_REPL-2147200999

 The fix to this is as follows:
 
sp_changemergearticle 'publicationName','tableArticle','schema_option','0x000000000C034FD1',1,1
go
sp_changemergearticle 'publicationName','tableArticle','stream_blob_columns','false',1,1
go

Replace publicationName and tableArticle as required. You will need to generate a new snapshot and apply.
 
Looking at what these options mean, it's not entirely clear why they correct the issue with Geography types. They concentrate on streaming blobs, so can only assume that internally Geography is dealt with the same as a blob. Interesting...
 
Thank you Microsoft for this solution btw - I would never have guessed it !

Transactional Replication : How to find erroring command and remove it

Had an interesting situation today where a replicated stored procedure call was in error. The issue was that the procedure call had some single quotes in a string parameter. This was giving the following error in Replication Monitor:

Error messages:
Invalid distribution command, state 2: transaction 0x0x000007c000005e36005000000000 command 3{offset : 258 token offset: 44 state: 50) }. (Source: MSSQL_REPL, Error number: MSSQL_REPL21001)
Get help: http://help/MSSQL_REPL21001


There is actually everything you need in that error, the transaction sequence number is the key piece of information.

But out of interested, I listed up the replication errors at the Publisher using sp_helpsubscriptionerrors as follows:


exec sp_helpsubscriptionerrors  @publisher = 'myPublisher', @publisher_db = 'dbName', @publication='publicationName', @subscriber='mySubscriber', @subscriber_db='dbName'

(I've obviously changed the parameters to hide the sensitive bits!)

This command lists up all of the failed transactions for the subscription. In a normal implementation, this may be just one. However if you have multi-threaded your subscription then there may be more than one.

Once the offending command has been identified (and it will be the same as the one reported in Replication Monitor) you can decide what to do. In this case I wanted to delete the command and apply it by hand (minus the single quotes!). There is a nice command which allows this, the important piece of information is the xact_seqno (otherwise known as the transaction sequence number). sp_setsubscriptionxactseqno will remove the command at the subscriber. Yep, not obvious from the name of the command. The crucial bit to note here is you have to run it in the subscriber database, it will not work from the distributor.

exec sp_setsubscriptionxactseqno @publisher = 'myPublisher',@publisher_db ='publicationName', @publication='mySubscriber', @xact_seqno = 0x000007C000005E36005000000000

Tuesday, 18 March 2014

SQL Server Transactional Replication Subscription Streams not being used

I was recently looking at increasing the performance of Transactional Replication across a very high latency line (plenty of bandwidth). I had exhausted all of the agent parameter tweaks so decided to look at subscription streams on the subscriber. There's a bunch of good articles out there describing this, not least this article from SQL Server Advisory team which describes the relative benefits (and dangers) of using subscription streams.

So I gave it a go. If you set the -VerboseHistoryLevel to 2 you will get a useful statistics report periodically. Upon checking this I was pleased to see 16 separate threads, so my set up was right. However, when running tests only the first thread was ever used. Looking at the BOL it says :


Note Note
Subscriptionstreams do not work for articles configured to deliver Transact-SQL. To use subscriptionstreams, configure articles to deliver stored procedure calls instead.


Fine, I'm replicating Stored Procedures calls (rather than table articles) so this should work.
Well no, my bad, what that note means is the Standard procedures i.e. SCALL, XCALL, MCALL routines. Not replicated Stored Procedures articles. Simple misunderstanding that made all the difference.

Changed replication to be Table Article based, and behold 16 threads of goodness and over 10 times increase of performance.

Monday, 10 March 2014

SQL 2012 Full Restore to a new database - error Database in use

A colleague decided to use the new(ish) SQL 2012 Management Studio to restore a database to a new named database. Something that is simple in previous versions, and very useful when you don't want to overwrite an existing database. However, whenever he tried it always failed with Database in use error. Weird.

So I had a look. The user interface has changed a little, maybe to make it less confusing, however it does pretty much the same thing. After selecting the Device radio button, selecting the backup file, you now have a separate Database in the Destination section. So far so good.

Off to the Files page. This is a little clearer than before, quite like the Relocate all files to folder option. Here is where you rename the Restore As path just as before.

Next is the Options page - this is new and has a few more options. Very welcoming is the Close existing connections to destination database, however that also scares me a bit as I can see people using that to force a restore through when the error lies elsewhere. Not least because my colleague suggested using this to overcome the error. Luckily I said no !

By default the Tail-Log backup option is checked.

So if you now click OK, you get a nice progress bar at the top and the error.

Curious - this is a restore to a new database, no one is on it (I did an sp_who2 just to be sure).

The Solution

The Tail-Log backup option is interesting. On a whim, I unchecked this (it's a full backup full restore no logs to a new database - why do I want to take a Tail-Log backup ?) and it worked. In fact it is the sub option - Leave source database in restoring state - that is the issue. Just uncheck that option and it will all work.


I can see the usefulness of the Tail-Log backup option in a disaster recovery situation. Very cool option in the UI. However, I'm not sure why the Database in use error occurs and I think it is very dangerous to have to check to Server connections option. Not a good habit to get into.

Anyhow, a solution (of sorts).

Thursday, 23 May 2013

TSQL Disk Usage

Only because I have a server with the Reports...Disk Usage doesn't like to work....




       
SELECT Name,  
  (CONVERT(FLOAT,size))  * (8192.0/1048576) File_Size,  
  (CONVERT(FLOAT,FILEPROPERTY(name,'SpaceUsed')))
                         * (8192.0/1048576) MB_Used,  
  ((CONVERT(FLOAT,size)) * (8192.0/1048576)
                         - (CONVERT(FLOAT,fileproperty(name,'SpaceUsed')))
                         * (8192.0/1048576)) MB_Free  
 FROM sysfiles  
ORDER BY FILEPROPERTY(name,'IsLogFile')  
 


This will show actual files sizes and MB used internally.

Tuesday, 18 September 2012

TFS2010 View and unsubscribe another users alerts

Unfortunately in TFS2010 there is on UI that allows you to view and remove another users post. The issue I had was that a user left, however emails were still being sent from TFS to his account.

The solution - a bit messy.

To view all alerts in a collection:

USE [Tfs_DefaultCollection]

-- Find all subscriptions
SELECT * FROM tbl_EventSubscription ORDER BY Address

*Note that the USE will need to reflect the correct collection database name


Next to unsubscribe. The follow SQL will create a command line to do that :


-- Create script to unsubscribe subscriptions
SELECT 'bissubscribe /unsubscribe /id ' + CAST(id as varchar(255))+ ' /collection http://tfs-server-name:8080/tfs/collection-name'
FROM dbo.tbl_EventSubscription
WHERE Address = 'someuser@mycompany.com'

*Note change tfs-server-name to your TFS server name, and collection-name to reflect the collection you are interested in.


Next, remote onto the TFS server. Open an elevated command line prompt. cd to the TFS installation folder \Tools.
Paste in the result line from the SQL above into the command line prompt and execute (or create a batch file, copy over to TFS server and execute it) .

Now the alert should be removed from the system.

Thursday, 15 March 2012

Transactional replication, identity and NOT FOR REPLICATION

Ouch.

Here's the scenario. Have a database server (call it DBS-A, DB1) transactionally replicating to another server (DBS-B, DB1). The tables have identities on them as the unique index (to save space, and as DBS-B is readonly have no problems with duplicate data). All works fine.

So now have another server (DBS-C, DB2) replicating data into DBS-A (into another database DB2) using transactional replication. However, the replication is non standard, in that the sp_MSins_xxxx routine has been amended. This change calls a stored procedure which copies data in the DBS-A, DB1 database. This is a SELECT..INSERT type of affair which works perfectly when called from SSMS or via an application.

However, SQL Server thinks that this data is being created by replication, so obeys the NOT FOR REPLICATION flag on the IDENTITY field, and subsequently returns a not null error, expecting a value etc.

What is annoying is that when you add a table into a replication article, it automatically adds NOT FOR REPLICATION onto any IDENTITY fields and I can not see how to remove this via the UI.

After some investigation I found the following system stored procedure which will do this for you:

DECLARE @tableID INT


SELECT @tableID = object_id('myTable')
EXEC sys.sp_identitycolumnforreplication @tableID, 0
Nice !


Problem 2...
Now this seems to work perfectly on empty tables, however to my horror I found that when I tried it on a populated table (over 1million rows) that the log reader blew up with :

The process could not execute 'sp_replcmds' on 'mydatabase'. (Source: MSSQL_REPL, Error number: MSSQL_REPL20011)

Get help: http://help/MSSQL_REPL20011

The Log-Scan Process failed to construct a replicated command from log sequence number (LSN) {000017d6:000016d7:00b9}. Back up the publication database and contact Customer Support Services. (Source: MSSQLServer, Error number: 18805)
Get help: http://help/18805

The process could not execute 'sp_replcmds' on 'mydatabase'. (Source: MSSQL_REPL, Error number: MSSQL_REPL22037)
Get help: http://help/MSSQL_REPL22037
Arrrrgggh !
This was traced to be definitely the table that I changed, as the error disappeared once the article was dropped.

After struggling through dropping and recreating the replication several times, the (unconfirmed as I don't want to try it again) solution appeared to be an order thing:
  • Create an empty table with the same schema as the one you want to add to the replication
  • Add empty table to replication
  • Remove NOT FOR PUBLICATION using above stored procedure call
  • Populate the data from full table to empty table
  • Move any foreign keys over from referencing tables
That seems to work.

Friday, 24 September 2010

SSRS load image from external assembly

This has been a bit of and adventure to get this to work.

 
Scenario
I had a standalone C# assembly for creating data matrixes (2D barcodes) and it exported as a bitmap. This is really no different from loading a bmp from disk.

 
I wanted to create a report that displayed a data matrix per part in a table.

 
All sounds plausable!

 
Issues
There are a few issues with this:

 
1. The custom assemblies need to be installed and referenced. This needs to be set on the development PC and the server that the report is published to.

 
2. The Image control in SSRS (2005) needs to have the Value poperty set.

 
3. The Image Image expects a Byte array.

 
Configuration
Assuming that you have already created your assembly...

 
1. Copy the assembly to the following locations:
Development environment (may also be reporting server):
Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblie

 
Reporting Server:
Program Files\Microsoft SQL Server\MSSQL.3\Reporting Services\ReportServer\bin

 
Note that these may be different depending on your installation.

 
2. Add reference to the assembly in your report.
  • Open the report that will reference the custom assembly.
  • On the Report menu, click Report Properties.
  • In the Report Properties dialog box, click the References tab.
  • Under References, click the ellipsis (...) button that is next to the Assembly name column header.
  • In the Add References dialog box, click Browse. (In SQL Server 2005, click the Browse tab.)
  • Locate and then click the custom assembly. Click Open. (In SQL Server 2005, click Add instead of Open.)
  • In the Add References dialog box, click OK.
  • In the Report Properties dialog box, click OK.

 
That's the reference installed.

 
3. Create custom code to use the assembly
This custom code will also need to convert the bitmap to a byte array...
  • Right click the report and choose properties. Select the Code tab and enter code similar to the following :

Public Function GetMatrixImage(ByVal code As String) As Byte()

 
Dim photo as System.Drawing.Image
photo = myAssembly.GetTheImage(code)

 
Dim ms AS System.IO.MemoryStream = new System.IO.MemoryStream()
photo.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp)
Dim imagedata as byte()
imagedata = ms.GetBuffer()

 
return imagedata

 
End Function

 
4. Go to Layout tab and drop your Image control. Set it up as follows:
  • Set Source = Database
  • Set MIMEType = image/bmp (from the pulldown)
  • Choose Expression from the Value drop down. In the Expression editor enter :
=Code.GetMatrixImage(Fields!ID.Value)

 
Where ID.Value is the key to creating/finding the image.

 

 
And that's it. Pretty easy really, however the documentation on how to do it is a bit thin. Hopefully this will help people out !

Wednesday, 3 February 2010

SQL Server Stats and Query Optimiser tips

Great article here with 13 Things You Should Know About Statistics and Query Optimiser. Covers a lot of ground and is worth a read.

Tuesday, 19 January 2010

SQL Plan Parallelism issue

Had a strange scenario whereby a stored procedure would randomly execute very slowly. Normally took half a second, then occasionally took 30 seconds or more. Thought initially as this was on busy tables (i.e. results varied considerably even though the SQL was static with no parameters) that the plan created was incorrectly optimised. However, straight SQL provided the same issue. WITH RECOMPILE seemed to work around the problem, however that added a couple of seconds to the execution time.

So I decided to dig a little deeper into the plan, and noticed that there was a Parallelism item for just about every SQL. Even stranger, I found that if I removed a particular column from the select list (that happened to be a DECIMAL 20,9) that the parallelism went and the SQL flew.

Now I'm not sure if there is any more work required when selecting a DECIMAL field, however it was definitely related. Remove any other field and the parallelism (and the performance) didn't change.

Finally settled on stopping SQL from pursueing parallelism by adding the following after the end of the SELECT statement :

OPTION ( MAXDOP 1 )


This forces the SQL to be run on one processor, and lo the performance is now back to less that half a second.

Not maybe an ideal solution as I'm telling SQL that it is wrong. However, looking at the plan, all the estimated costs are well off so I can only assume that this is some weird set of circumstances.

Monday, 18 January 2010

Detecting blocking SQL 2005

I've got a very occasional problem whereby a process is blocking the incremental rebuild of some fulltext indexes which in turn blocks loads of other statements. Trapping this in a reasonable amount of time so that it doesn't affect users too much has been a problem. I then found this Tony Rogerson article which went a great way to implementing a solution.

Repeating Tony's article (look there for the full details, this is just the code)->

First create the trace threshold. Show advance options needs to be enabled:

sp_configure 'show advanced options', 1
go
reconfigure
go
sp_configure
go

-- Set up report capture threshold. Set to 10 seconds here - change figure
-- to suit needs.
sp_configure 'blocked process threshold', 10
go
reconfigure
go


Next create a database to receive the messages:

--CREATE DATABASE RBT.DBAEventManagement
--go

USE [RBT.DBAEventManagement]
go

ALTER DATABASE [RBT.DBAEventManagement] SET ENABLE_BROKER
go

CREATE QUEUE syseventqueue
go

CREATE SERVICE syseventservice
ON QUEUE syseventqueue ( [http://schemas.microsoft.com/SQL/Notifications/PostEventNotification] )
go

CREATE EVENT NOTIFICATION notify_locks
ON SERVER
WITH fan_in
FOR blocked_process_report
TO SERVICE 'syseventservice', 'current database';

GO


This now enough to capture the messages and queue them up. The next part is to receive the messages and to email out/store history. Only 1 email is sent per block - don't want an email every 10 seconds ! The history table is as follows

USE [RBT.DBAEventManagement]
GO

IF OBJECT_ID('dbo.tbl_messages', 'U') IS NOT NULL
DROP TABLE dbo.tbl_messages
GO

CREATE TABLE dbo.tbl_messages
(
Message_Body XML NOT NULL,
Message_Sequence_Number INT NOT NULL,
xactid VARCHAR(20) NOT NULL,
Emailed VARCHAR(1) NOT NULL CONSTRAINT [DF_tbl_messages_Emailed] DEFAULT 'N',
TMStamp SMALLDATETIME NOT NULL CONSTRAINT [DF_tbl_messages_TMStamp] DEFAULT (GETDATE()),
CONSTRAINT PK_messages PRIMARY KEY NONCLUSTERED(TMStamp ASC, Message_Sequence_Number ASC)
)

GO


The stored procedure to receive the messages is as follows:

CREATE PROCEDURE [dbo].[proc_capture_queue]
AS


/* ========================================================================
proc_capture_queue

Capture service broker queue and insert into history table.
Email new message

===========================================================================
Version Date Author Comment
------- ---- ------ -------
v0.00 14/01/2010 Brian Jones Initial Version
=========================================================================== */
SET NOCOUNT ON

DECLARE @msg_body XML,
@DatabaseID INT,
@Process XML,
@xactid VARCHAR(20),
@emailBody NVARCHAR(MAX),
@13LineFeed10 NVARCHAR(2)

DECLARE @msgs TABLE ( message_body XML NOT NULL,
message_sequence_number INT NOT NULL,
xactid VARCHAR(20))

SET @13LineFeed10 = CHAR(13) + CHAR(10)

IF NOT EXISTS (SELECT * FROM sys.server_event_notifications)
BEGIN
EXEC msdb.dbo.sp_send_dbmail @profile_name = 'MailProfile',
@recipients = N'myemail@myaddress',
@subject = 'WARNING: SQL Blocking Service Queue',
@body = 'The SQL message queue for tracking blocking sessions is missing, please investigate.'
RETURN
END;

RECEIVE message_body, message_sequence_number,
CAST( message_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocked-process[1]/process[1]/@xactid', 'varchar(20)')
FROM syseventqueue
INTO @msgs;

INSERT INTO dbo.tbl_messages(Message_Body, Message_Sequence_Number, xactid)
SELECT Message_Body, Message_Sequence_Number, xactid
FROM @msgs
ORDER BY message_sequence_number

DECLARE email_cur CURSOR FOR
SELECT Message_Body, xactid
FROM @msgs
ORDER BY message_sequence_number

OPEN email_cur
FETCH NEXT FROM email_cur INTO @msg_body, @xactid

WHILE @@FETCH_STATUS = 0
BEGIN
IF NOT EXISTS(SELECT * FROM tbl_messages WHERE xactid = @xactid AND Emailed = 'Y')
BEGIN
SELECT @DatabaseId = CAST( @msg_body AS XML ).value( '(/EVENT_INSTANCE/DatabaseID)[1]', 'int' )

SELECT @emailBody = N'The following process is being blocked.' + @13LineFeed10 + @13LineFeed10 +
'Blocked Process ' + @13LineFeed10 +
'=============== ' + @13LineFeed10 +
'Spid : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocked-process[1]/process[1]/@spid', 'varchar(10)')
+ @13LineFeed10 +
'HostPid : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocked-process[1]/process[1]/@hostpid', 'varchar(10)')
+ @13LineFeed10 +
'HostName : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocked-process[1]/process[1]/@hostname', 'varchar(100)')
+ @13LineFeed10 +
'LoginName : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocked-process[1]/process[1]/@loginname', 'varchar(100)')
+ @13LineFeed10 +
'DatabaseName : ' + DB_Name(@DatabaseID)
+ @13LineFeed10 +
'Batch Started : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocked-process[1]/process[1]/@lastbatchstarted', 'varchar(25)')
+ @13LineFeed10 +
+ @13LineFeed10 +
'Blocking Process ' + @13LineFeed10 +
'================ ' + @13LineFeed10 +
'Spid : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocking-process[1]/process[1]/@spid', 'varchar(10)')
+ @13LineFeed10 +
'HostPid : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocking-process[1]/process[1]/@hostpid', 'varchar(10)')
+ @13LineFeed10 +
'HostName : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocking-process[1]/process[1]/@hostname', 'varchar(100)')
+ @13LineFeed10 +
'LoginName : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocking-process[1]/process[1]/@loginname', 'varchar(100)')
+ @13LineFeed10 +
'DatabaseName : ' + DB_Name(@DatabaseID)
+ @13LineFeed10 +
'Batch Started : ' + CAST( @msg_body AS XML ).value( '/EVENT_INSTANCE[1]/TextData[1]/blocked-process-report[1]/blocking-process[1]/process[1]/@lastbatchstarted', 'varchar(25)')
+ @13LineFeed10 +
+ @13LineFeed10 +
'Message in full : ' + @13LineFeed10 +
CAST(@msg_body AS VARCHAR(MAX))

EXEC msdb.dbo.sp_send_dbmail @profile_name = 'MailProfile',
@recipients = N'myemail@myaddress',
@subject = 'SQL Blocking Report',
@body = @emailBody

UPDATE tbl_messages
SET Emailed = 'Y'
WHERE xactid = @xactid
END

FETCH NEXT FROM email_cur INTO @msg_body, @xactid
END

CLOSE email_cur
DEALLOCATE email_cur


------------------------ END OF PROCEDURE --------------------------------


OK, I've used a SQL cursor but it's on a small memory table so it isn't an issue.

This stored procedure is in a job which executes every couple of minutes (uses WAITFOR DELAY 00:01 to loop continuously). There is another job which clears the history table down every week.

This is now happily whirring away waiting for the blocking process. This may need (unfortunately) expanding to run a SQL Trace also, so that the blocking process can be investigated further. But that's another blog post !

Thursday, 17 December 2009

TFS merge filename collision error

The scenario is that I have a "Main" repository, this is branched to the "development" tree. Amendments are applied to the "development" tree, and when released this is merged back to the "main" repository. It was this merge back/check in that was giving the error. Seeing as there had been no changes to filenames and locations (there were some new files though), the collision error is a little strange.

Anyhow, to sort this out I force got latest for the "main" repository, overwriting all the files. Then I merged the "development" tree back, and check in pending changes then worked with no error. All via the GUI.

There are a number of non-gui based solutions by performing a baseless merge. I'm not sure of the side-effects of doing this, the solution described above seems to retain all history as expected.

Look here for details on a baseless merge.

Tuesday, 24 November 2009

SSMS Tool Pack

A brilliant addon for SSMS 2005/2008, and it's free ! Features include:

•SQL Snippets
•Window Connection Coloring
•Query Execution History and Current Window History
•Format SQL
•Search Table, View or Database Data
•Run one script on multiple databases
•Copy execution plan bitmaps to clipboard or file
•Search Results in Grid Mode or Execution Plans
•Generate Insert statements from resultsets, tables or database
•Regions and Debug sections
•Running custom scripts from Object Explorer
•CRUD stored procedure generation
•New query template
•General options

Install now, if only for the colour coded connection windows !!

Tuesday, 3 November 2009

SQL Server Memory Settings 64bit

Rather interesting blog posting here talking about SQL Server 2005/2008 64bit and memory settings. The recommendation is to always set these as the following table outlines:

Physical RAMMaxServerMem Setting
2GB1500
4GB3200
6GB4800
8GB6400
12GB10000
16GB13500
24GB21500
32GB29000
48GB44000
64GB60000
72GB68000
96GB92000
128GB124000


What makes this interesting is the comments posted, where one poster mentions that they have seen instability when the memory is not set.

Tuesday, 27 October 2009

Run SQL Trace in background

Rather useful this if you need to monitor a trace for a period of time and can not leave the session logged in. Found how to do it here.

First use Profiler to define the events, columns, and filters needed. Some Events are : SQL:BatchCompleted and RPC:Completed, SP:StmtCompleted. Important columns are : Duration, CPU, Reads and Writes. Some advanced events are SP:Recompile and Scan:Started to check for table and index scans

Click the Run button. Immediately stop the trace

Click the File menu, expand the Export option, and then expand the Script Trace Definition option. Choose For SQL Server 2005 (or SQL 2000 if creating script for older SQL Server) and select a filename to save the script.

Once the script has been saved, open it for editing in SQL Server Management Studio.

The following line of the script must be edited, and a valid path must be specified, including a filename:
exec @rc = sp_trace_create @TraceID output, 0, N'InsertFileNameHere',
@maxfilesize, NULL. The @maxfilesize is 5MB by default

Run the script. The generated script will also select back a @traceID

Once you are done use the @traceID to stop and close the trace:

EXEC sp_trace_setstatus @traceid=99, @status=0
EXEC sp_trace_setstatus @traceid=99, @status=2

The fn_trace_gettable function can be used to read the data from the trace file :

SELECT * FROM ::fn_trace_gettable('C:\Traces\myTrace.trc', 999) where 999 is the number of rollover trace files to read


Easy !

Monday, 26 October 2009

Benchmarking SQL Server

A very useful blog entry on Benchmarking SQL Server.

The article looks at the PAL tool found here

The process is straight forward. Create a performance log for a period of time on the SQL Server you need to benchmark. Typical metrics to record are :

Physical Disk
Logical Disk
Process (I will sometimes tweak this down to just the SQL Server process)
Memory
Network Interface (sometimes I will tweak down to just the counters needed and just the NICs involved)
Paging File
Processor
System
SQL Server: Access Methods
SQL Server: SQL Statistics
SQL Server: Buffer Manager
SQL Server: General Statistics
SQL Server: Latches (Often I just look at the two required counters rather than the object)
SQL Server: Locks
SQL Server: Memory Manager

Save this for a reasonable slice of time and then run it through PAL. This then generates a nice report, highlighting any issues. See the above blog link for a more indepth description.

Thursday, 8 October 2009

Find sql usage in a database

The following searches all objects in a database for a particular piece of text:

DECLARE @searchString VARCHAR(128)

SET @searchString = 'xp_smtp'

SELECT DISTINCT so.name, so.type, so2.name as source
FROM sysobjects so
INNER JOIN syscomments sc ON so.id = sc.id
LEFT OUTER JOIN sysobjects so2 ON so2.ID = so.parent_obj
WHERE text LIKE '%' + @searchString + '%'
ORDER BY so.name