Archive for the ‘SQL Server’ Category
Performanceprobleme bei Merge-Replikation
Im Zuge einer Neueinrichtung einer Merge-Replikation auf einem Produktivsystem hatten wir auf der Abonnenten-Seite mit massiven Performance-Problemen zu kämpfen. Diese traten gefühlsmäßig immer dann auf, wenn die Replikation lief. Die CPU-Auslastung war sehr gering, aber die Datenträgerwarteschleife schoss teilweise auf 250!
Augenscheinlich war aber alles korrekt eingerichtet.
Um dem Problem auf die Schliche zu kommen, erwies sich die Systemtabelle dm_exec_query_stats als äußerst hilfreich. Hierüber kann man sich Ausführungsstatistiken der letzten Befehle abholen. Unter anderem werden hier CPU-Zeit, Lese- und Schreibzugriffe protokolliert.
SELECT TOP 5 last_physical_reads, sql_handle, plan_handle FROM sys.dm_exec_query_stats ORDER BY last_physical_reads DESC
In den Spalten sql_handle und plan_handle werden zwei Handle zurückgeliefert über die man Zugriff auf das Kommando im Klartext sowie den Execution Plan bekommt.
Diese kann man sich mit den Systemfunktionen sys.dm_exec_sql_text und sys.dm_exec_text_query_plan zurückgeben lassen.
Ich führte also folgendes Statement aus
SELECT * FROM sys.dm_exec_sql_text(0x020000006D3E963947E734508EEE5BDFDA222AE4C2FE9432)
und bekam wie vermutet ein Replikationskommando zurück
SELECT TOP 100 mc.tablenick, mc.rowguid, mc.generation, mc.lineage, mc.colv1, t.* FROM [Shop].[dbo].[MSmerge_contents] mc, [Shop].[dbo].[OrderItemCondition] t WHERE mc.generation = 45940 AND mc.tablenick = 26251010 AND mc.rowguid = t.rowguidcol ORDER BY mc.tablenick, mc.rowguid
Ich führte das Kommando mit der Option “Include Actual Execution Plan” aus und bekam die Meldung, dass ein Index fehlt.
CREATE NONCLUSTERED INDEX [<Name of Missing INDEX, sysname,>] ON [dbo].[OrderItemCondition] ([rowguid])
Tatsächlich fehlte ein Index auf der rowguid-Spalte, nach Anlegen dieses Index verringerte sich die Aktualisierungszeit der Replikation von rund 7 Minuten auf eine Minute. Die Datenträgerwarteschleife verhält sich seitdem auch ruhig und die Performanceeinbrüche existieren nicht mehr.
Normalerweise werden solche wichtigen Indizes von den Replikationstools automatisch angelegt. Warum das in diesem Fall nicht passiert ist kann ich nicht sagen, ich bin aber froh, das Problem gefunden und gelöst zu haben.
Database in SUSPECT-Modus
Nach einem Server-Neustart beglückte mich heute der SQL Server mit einer Meldung, dass eine Datenbank “suspect” sei. “Was ist das denn???”, fragte ich mich.
Nach ein wenig Recherche fand ich heraus, wann eine Datenbank als suspect markiert wird:
If one or more database files are not available.
If the entire database is not available.
If one or more database files are corrupted.
If a database resource is being held by the operating system.
Quelle: SQL Server Books Online
Da ich weder die Datenbank umbenannt noch Dateien gelöscht hatte, konnte der Status nur bedeuten, dass die Datenbank korrupt ist.
Zur Lösung des Problems kamen folgende Kommandos zum Einsatz:
1) Setzen der Datenbank in exklusiven Modus für Admin-Tätigkeit
ALTER DATABASE myDatabase SET Single_User
2) Setzen der Datenbank in Wartungsmodus
ALTER DATABAS myDatabase SET Emergency
3) Überprüfen der Datenbank Teil 1
DBCC CheckDB ('myDatabase ')
Hier erhält man eine Meldung, mit welchem Modus die Datenbank repariert werden kann
4) Überprüfen der Datenbank Teil 2
DBCC CheckDB ('myDatabase', REPAIR_ALLOW_DATA_LOSS)
5) Zurücksetzen der Datenbank in shared-Modus
ALTER DATABASE myDatabase SET Multi_User
Das hat das Problem bei mit gelöst. Zum Glück gingen trotz REPAIR_ALLOW_DATA_LOSS keine Daten verloren.
SSMS: Adding submenus to context menu
If you want to add a menu item to the ObjectExplorer’s context menu, the proceeding is quite simple.
Create a class that inherits from ToolsMenuItemBase
public class MenuItem : ToolsMenuItemBase { public MenuItem() { this.Text = "New menu item"; } protected override void Invoke() { // do something } public override object Clone() { return new MenuItem(); } }
and attach it to the ObjectExplorer’s menu
IObjectExplorerService objectExplorer = ServiceCache.GetObjectExplorer(); objectExplorer.GetSelectedNodes(out nodeCount, out nodes); INodeInformation node = (nodeCount > 0 ? nodes[0] : null); if (_tableMenu == null) { _tableMenu = (HierarchyObject)node.GetService(typeof(IMenuHandler)); MenuItem item = new MenuItem(); _tableMenu.AddChild(string.Empty, item); }
What if you now intend to create a menu item that again contains a sub menu? ToolsMenuItemBase provides a method called AddChild. Using this method should be the obvious way to create sub menus.
But any call like item.AddChild(string.Empty, new SubMenuItem()) simply does nothing.
Using Reflector, I found a solution to achieve the desired behaviour.
The trick is to implement an interface called IWinformsMenuHandler. You will need to create a method GetMenuItems. It is here where you can create a new menu hierarchy.
The new class looks like this:
public class MenuItem : ToolsMenuItemBase, IWinformsMenuHandler { public MenuItem() { } protected override void Invoke() { } public override object Clone() { return new MenuItem(); } public System.Windows.Forms.ToolStripItem[] GetMenuItems() { ToolStripMenuItem item = new ToolStripMenuItem("Menu Item"); ToolStripMenuItem subItem = new ToolStripMenuItem("Sub item"); subItem.Click += new EventHandler(SubItem_Click); item.DropDownItems.Add(subItem); item.DropDownItems.Add(new ToolStripSeparator()); item.DropDownItems.Add(new ToolStripMenuItem("Sub item2")); return new ToolStripItem[] { item }; } }
When implementing IWinformsMenuHandler setting the properties of ToolsMenuItemBase will be useless. E.g. this.Text = "Menu item" will be relpaced by ToolStripMenuItem item = new ToolStripMenuItem("Menu Item");. Also Invoke() will never be called, but the event handler of your ToolStripMenuItem.
New SSMS Addin: DataScripter
I am glad to introduce a new addin for SSMS 2008 you might find useful.
The addin attaches a new function to the ObjectExplorer’s context menu.
You can easily generate INSERT statements for all data of a selected table.

Find the setup and sourcecode on my codeplex project CodePlex: DataScripter
Fulltext Manager for SQL Express
If you missed the Storage node in the Object Explorer of your SSMS installation to manage your SQL Express fulltext catalogs, here is the solution!
Find below my new addin for both SSMS 2005 and SSMS 2008.
Once installed the addin, you’ll find a new context menu entry when you select a database:

A click on the menu entry opens the management dialog:

Find the setup and sourcecode on my CodePlex project http://www.codeplex.com/FulltextManager.
Replikation hängt bei “uploading data changes”
Nachdem die Merge-Replikation bei einem unserer Kunden monatelang problemlos lief, ist sie plötzlich ausgefallen. Ein Blick ins Merge-Protokoll zeigte, dass sie nach dem Initialisieren im Status “uploading data changes to the Publisher” stehen geblieben ist. Einige Minuten später folgte dann ein Timeout. Als dieses Problem zum ersten Mal aufgetreten ist, half nur eine komplette Neuinitialisierung des Snapshots mit anschließenden Rebuild. Doch leider ist nach einigen Wochen dasselbe Problem wieder aufgetreten.
Nach längerer Recherche im Internet fand ich einen interessanten Thread über dieses Thema. Anscheinend ist wohl eine SP der Replikation fehlerhaft, was unter bestimmten Umständen zu einer Endlosschleife führt. Dieser Fehler bis zur SQL Server Version 9.0.3282 (Kumulatives Update-Paket 9) aber leider noch nicht behoben.
Ein Workaround besteht momentan im im Setzen des “generation_leveling_threshold” auf den Wert 0. Hierfür muss auf dem Subscriber folgendes Statement ausgeführt werden:
UPDATE sysmergepublications SET [generation_leveling_threshold] = 0
Laut Dokumentation soll der Wert “generation_leveling_threshold” eigentlich über die SP sp_changemergepublication gesetzt werden. Doch das hat in unserem Fall leider keinen Erfolg gebracht. Das korrekte Statement für sp_changemergepublication würde lauten:
exec sp_changemergepublication @publication = 'My Publication', @property = 'generation_leveling_threshold', @value = '0'
Dieser Workaround hat in unserem Fall problemlos funktioniert und seid einigen Wochen läuft die Replikation wieder fehlerfrei. Ein großer Vorteil bei diesem Vorgehen ist, dass der Snapshot nicht neu erstellt werden muss und auch der Subscriber nicht erneut initialisiert werden muss. Nachdem man den Befehle ausgeführt hat läuft beim nächsten Abgleich alles wieder problemlos.
ExecuteScalar schneidet XML bei 2033 Zeichen ab
Seit dem SQL Server 2000 ist es einfach, XML Dokumente als Ausgabe zurückzuliefern. Hier muss man nur FOR XML [RAW|AUTO|EXPLICIT] dem SELECT-Statement anhängen und schon wird die Ausgabe nicht mehr als Recordset, sondern als XML-Dokument in die Anwendung zurückgegeben.
Als Beispiel soll eine Tabelle mit den 3 Spalten “ID”, “Titel”, “Autor” dienen.
Ein SELECT-Statement, welches den Inhalt der Tabelle als XML-Dokument zurückliefert, wäre
SELECT 1 AS Tag, NULL AS Parent, NULL AS [Buecher!1], NULL AS [Buch!2!ID], NULL AS [Buch!2!Titel], NULL AS [Buch!2!Autor] FROM Buecher UNION SELECT 2 AS Tag, 1 AS Parent, NULL AS [Buecher!1], ID AS [Buch!2!ID!], Titel AS [Buch!2!Titel], Autor AS [Buch!2!Autor] FROM Buecher FOR XML EXPLICIT
Das XML-Dokument hat folgende Struktur
<Buecher> <Buch ID="411B49C4-4F8F-4D31-B563-0AC140A5EBD5" Titel="Die Liga der aussergewöhnlichen Gentleman. Der Roman zum Film. 1. Aufl. 10.2003." Autor="Anderson, Kevin J" /> <Buch ID="F6900F6A-0E65-4F35-8FE3-148253551665" Titel="Die linkshändige Frau" Autor="Handke, Peter" /> <Buch ID="F6BCB885-B334-4905-BA78-25289BF810C1" Titel="Feindbild und Frieden" Autor="Böll, Heinrich" /> <Buch ID="EA915B82-4BF9-4735-8FEA-2ADD567FD53F" Titel="Antigones Verlangen" Autor="Butler, Judith" /> </Buecher>
Möchte man das XML-Dokument nun im eigenen Code z.B. mit Hilfe eines XmlDocument auswerten und liest die Rückgabe über ExecuteScalar ein, so wird die Ausgabe nach 2033 Zeichen abgeschnitten, das XML-Dokument wird damit ungültig und eine Reihe von Xml-Exceptions treten auf.
Der Grund ist, dass der SQL Server XML Dokumente an den Client in Blöcken zu je 2033 Zeichen zurückgibt.
Die Lösung für das o.g. Problem wäre also, nicht ExecuteScalar, sondern etwa folgendes Konstrukt zu verwenden:
connection.Open(); StringBuilder sb = new StringBuilder(); System.Xml.XmlReader reader = command.ExecuteXmlReader(); if (reader.Read()) { while (reader.ReadState != System.Xml.ReadState.EndOfFile) sb.Append(reader.ReadOuterXml()); } reader.Close(); connection.Close(); string xml = sb.ToString();
Developing Addins for SSMS 2008
There are few good articles if you are interested in developing addins for SQL Server Management Studio 2005.
Basically the concepts are similar to writing an addin for Visual Studio. I linked two sites that deal with addins for SSMS 2005.
http://aspalliance.com/1374_Extend_Functionality_in_SQL_Server_2005_Management_Studio_with_Addins.all
http://jcooney.net/archive/2007/11/26/55358.aspx
Even when SQL Server Management Studio 2008 CTP had been released, the development concepts for addins remained unchanged to those in SSMS 2005.
Surprisingly the final release of SSMS 2008 brought some significant changes to the addin interface. However, there is no need to panic if you have written an addin for SSMS 2005 and you now want to run it in SSMS 2008. Although the changes Microsoft made will break your addin, the work needed to reanimate it is kept to a minimum:
If you try running your SSMS 2005 addin in SSMS 2008 for the first time it will fail with a typecast exception.
Let’s take a look at the OnConnect-Method in your Connect class, where you typically stored a reference to the DTE2-Application object.
public class Connect : IDTExtensibility2 { private DTE2 _applicationObject; private AddIn _addInInstance; public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _applicationObject = (DTE2)application; _addInInstance = (AddIn)addInInst; // do something with the application, e.g. reference the Commands Commands2 commands = (Commands2)_applicationObject.Commands } }
As you run this code in SSMS 2008, the typecast exception will occur in Line 8.
So what now? We need the Application reference to create toolbar items or windows. The application-object we get in SSMS 2008 is simply void, there are no properties, no methods, no implemented interface: just empty?!?!
This is a problem, but will not break our necks! One more time Reflector saves our lives: Using Reflector I figured that ServiceCache now has a new static property called ExtensibilityModel.
And this property provides all the functionality we formerly knew from the application object.
Now to convert your addin from SSMS 2005 to SSMS 2008, simply change the application-object calls to ServiceCache.ExtensibilityModel and it will work:
public class Connect : IDTExtensibility2, IDTCommandTarget { private AddIn _addInInstance = null; public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _addInInstance = (AddIn)addInInst; // do something with the commands Commands2 commands = (Commands2)ServiceCache.ExtensibilityModel.Commands; } }
Although Microsoft changed the addin model to make our lives harder, we will still be able to develop useful addins for SQL Server Management Studio 2008.
Find some nice addins on my codeplex project SSMSAddins: http://www.codeplex.com/SSMSAddins.









