PASS Essential: Analyse eines Microsoft SQL Server auf Performanceprobleme

Aus dem Januar Newsletter der SQL PASS Deutschland:

Dauer: 1 Tag
Ort: Karlsruhe, inovex GmbH
Datum: 18. April 2016, 09.00-16.30 Uhr
Teilnehmeranzahl: mindestens 4, maximal 12
Preis: Mitglieder PASS Deutschland e.V. 299€ (inkl. MwSt.), Nicht-Mitglieder 475€ (inkl. MwSt.)
Schulungszertifikat: Ja
Anmeldung: E-Mail an registrierung@sqlpass.de
Voraussetzungen:

Sprecher:
Uwe Ricken (Twitter | Blog) verfügt über mehr als 6 Jahre Praxiserfahrung als DBA und mehr als 14 Jahre Erfahrungen als Entwickler von Datenbankmodellen.
Im Mai 2013 wurde diese Erfahrung mit der Zertifizierung zum 7. deutschen „Microsoft Certified
Master – SQL Server 2008“ gekrönt. Im Juli 2013 wurde ihm zum ersten Mal der MVP-Award von Microsoft für
seine Unterstützung der Microsoft SQL Server Community verliehen. Damit war Uwe Ricken der erste MCM +
MVP in Deutschland. Uwe Ricken ist als Sprecher zu den Themen „Database Engine Internals“, „Query Optimizing“
und „Entwicklung“ auf vielen Konferenzen in ganz Europa anzutreffen.

Inhalt
Nach Abschluss des Workshops haben Sie fundierte Kenntnisse in diesen Themen

  • „Problemzonen“ einer Installation von Microsoft SQL Server, die auftreten, wenn eine „Standardinstallation“ implementiert
    wird.
  • an Hand eines Stufenplans kann Schritt für Schritt die Analyse eines Systems durchführt werden.
  • Ergebnisse der Messungen werden in einer Bewertungsmatrix auf Schwachpunkte untersucht.

Teilnehmer Voraussetzungen:

  • Grundkenntnisse über Microsoft SQL Server
  • Umgang mit Microsoft SQL Server Management Studio

Kursinhalte:

  • Engpässe im Betriebssystem, die einen Microsoft SQL Server ausbremsen
  • Konfigurationseinstellungen einer Instanz von Microsoft SQL Server
  • TEMPDB – falsche Konfiguration und ihre Auswirkungen auf die Performance
  • PLE – Analyse und Bewertung von „Page Life Expectancy“ im Zusammenhang mit verwendeten Datenbanken
  • Analyse der Belegung des Buffer Pool pro Datenbank!
  • PFS-Contention, Locking, Blocking, Deadlocks – welche Möglichkeiten gibt es für die Beseitigung
  • Korrekte Datenbankeinstellungen (Datenbankdateien, Protokolldateien)
  • PERFMON – Einblicke in die Arbeitsweise des Microsoft SQL Server zur Performancebewertung
  • Analyse von Wait Stats zur Bewertung von vorhandenen Engpässen
  • Anforderungen an eine Kundendokumentation
    •  Gliederung der Dokumentation
    • Präsentation der Analyseergebnisse & Handlungsempfehlungen
  • An konkreten Beispielen, die mit dem eigenen Laptop (mitgebracht incl. installierter Software) ebenfalls simuliert werden
    können, werden verschiedene Engpässe demonstriert, die es zu erkennen gilt. Wenn es um die Bewertung von Analyseergebnissen
    mittels Wait Stats geht, so können solche Ergebnisse für jeden EIGENEN Server simultan im Workshop ausgewertet
    werden.

PASS Essentials werden vom PASS Deutschland e.V. veranstaltet: http://www.sqlpass.de
Allgemeine Geschäftsbedingungen (AGB): http://www.sqlpass.de/Events/AllgemeineGeschäftsbedingungenAGB.aspx

Power BI – mein leichter Einstieg

Ich habe mich heute das erste mal mit einem ernsthaften Ansatz mit dem Power BI Desktop auseinander gesetzt.
Nein, nichts kompliziertes, aber für mich ausreichend und ein Einstieg in die Materie… also erwartet bitte (noch) keine komplexen Erläuterungen 😉

Power BI Desktop runtergeladen und installiert, dass war recht einfach und bekommen Sie garantiert hin.

Das Tool das erste Mal gestartet und ein simpler und selbsterklärender Dialog erscheint … natürlich will ich Daten laden 😉

Power BI Desktop - Steps 1

Im Rahmen eines Kunden-Projektes sollten wir einfache Performance-Daten ermitteln (alle 5 Minuten die Laufzeit eines definierten Insert-Statements ermitteln), diese Daten kann man ja auch historisch sammeln und als „Trendbarometer“ für die Performance Auslastung des SQL Servers nutzen. Dazu müssen die Daten aber entsprechend gesammelt und grafisch aufbereitet werden… was liegt da näher sich mit dem neuen Power BI Desktop zu beschäftigen.

Da die Daten im Kundennetz liegen, musste ich den Zwischenschritt über eine Excel-Datei als Datenquelle machen… also Excel ausgewählt, Datei geöffnet, Tabelle gewählt => die Vorschau hatte mir genau meine Daten gezeigt, ich war erstmal zufrieden, abschließend „Daten laden“.

Power BI Desktop - Steps 2

Nun wählt man einen Diagramm-Typen aus, in meinem Fall ein „Liniendiagramm“.
Jetzt braucht man nur noch die Datenfelder, Measures oder berechnete Spalten dem Diagramm hinzufügen, je nach Bedarf eben in die dafür vorgesehenen Felder.

Für mein Beispiel:

  • Achse => Check_Timestamp
  • Legende => bleibt vorerst einmal leer
  • Werte => Runtime

Damit haben einen ersten „Wurf“ und auch die erste grafische Darstellung meiner Werte.

Power BI Desktop - Steps 3

Da es sich um viel zu viele Werte handelt (daher das kleine Warndreieck oben links im Diagramm) muss eine Unterteilung her, hierzu füge ich eine zusätzlich berechnete Spalte hinzu, die mir nur Werte für bestimmte Tage ausgibt.

Mittels rechte Maustaste in der Felderspalte oder die Menüleiste erhalten wir eine neue berechnete Spalte

Datum = LEFT(Tabelle1[Check_Timestamp];10)

Diese neue Spalte hänge ich als zusätzlichen Filter ein, dadurch kann ich mir immer nur einen bestimmten Tag oder mehrere Tage auswählen und analysieren.Power BI - Steps 4

Sicherlich mag es elegantere und saubere Lösungswege geben, dies ist aber mein erster Versuch und an diesem wollte ich euch/Sie teilhaben lassen. In Zukunft werde ich noch mehr über dieses grandiose Tool Power BI berichten, dann sicherlich mit „hübscheren“ Lösungen und Analysen.

Windows Cluster patchen und automatisch Verteilung wieder herstellen

Jeder kennt es, Server und deren Betriebssysteme in produktiven Umgebungen (in Testumgebungen natürlich auch) müssen regelmäßig gepatcht werden. Solange es sich um Standalone-Systeme handelt gibt es selten Probleme…
Wenn man aber Ressourcen auf einem Cluster verteilt hat, dann sind so manche OS-Kollegen leider so uneinsichtig oder engstirnig, dass man sich ganz die Termine merken muss, wann diese Kollegen wieder das SQL Server Cluster patchen wollen.

Ich habe leider immer wieder die Erfahrung machen müssen, dass die Kollegen das Cluster nur als Aktiv-Passiv-System (=> alles läuft auf einem Knoten und schwenkt im Fehlerfall auf den Ersatzknoten) ansehen, ein Cluster kann aber eben auch als Aktiv-Aktiv-System betrieben werden. Ok, dann muss man darauf achten, dass im Fehlerfall alle Ressourcen auf einer Seite gemeinsam Lauffähig sind. Man muss sich also im Vorweg Gedanken machen zur optimalen Konfiguration (beispielhaft Min./Max Memory) um alle SQL-Server auf einer Cluster-Knoten betreiben zu können.

Aber was passiert mit den Cluster-Ressourcen nach einem Patch-Durchgang? Die oben bereits genannten Kollegen haben nur ihren Part im Kopf und patchen einfach nur die Systeme, heißt sie machen einen Knoten des Cluster frei und patchen diesen freie Knoten. Um natürlich auch Ressourcen und Kosten zu sparen, werden solche Dinge automatisiert in der Nacht durchgeführt.

Aber was passiert nach dem Patchen mit dem Cluster? Hat sich jemand die ursprüngliche Verteilung gemerkt? Werden die Ressourcen wieder auf die ursprünglichen Knoten verteilt?
Aus leidlicher Erfahrung muss ich leider „Nein“ sagen…

Auswirkungen dieser Nicht-Beachtung

Auch wenn man sich im Vorwege ausreichend Gedanken gemacht hat, dann ist es (zumindest bei uns) so, dass die einzelnen Cluster-Ressourcegruppen nicht alle die volle RAM-Ausstattung nutzen können, da es hier oft zu Engpässen kommt.

Wir konfigurieren unsere geclusterten SQL-Server meist so, dass der Parameter „Min.Memory“ die Hälfte des Parameters „Max.Memory“ erhält. Sollten dann tatsächlich alle Instanzen auf einem Knoten laufen, kommt es immer wieder zu RAM-Engpässen da alle SQL Server Instanzen versuchen unter Last ihren konfigurierten Wert für „Max.Memory“ erreicht. Da dies dann nicht passt, „prügeln“ sich die einzelnen Instanzen eben um die knappen System-Ressourcen.

Also wäre es das Idealste nach dem Patchen eben genau die vorgesehen Verteilung wieder herzustellen, damit alle Ressourcen möglichst optimal arbeiten können. Aber wie erreicht man dies?
Auf jedem System wird Powershell mitgeliefert, mittels Powershell werden immer mehr Skripte geschrieben, so dass man sein ganzes System oder sein ganzen Windows-Cluster sehr gut mit Powershell administrieren kann.

In der Theorie klingt das alles so einfach:

  • Systemzustand ermitteln
  • Cluster-Ressource-Gruppen ermitteln
  • durch die einzelnen Gruppen durchlaufen
    • Prefered-Owner der jeweiligen Ressource-Gruppe ermitteln
    • mit dem aktuellen Owner vergleichen
    • bei Abweichungen auf den Prefered-Owner schwenken
  • Fertig

Da ich diese Aktivität in der Hauptsache bei meinen Betriebssystem-Kollegen sehe, hatte ich dort einmal nachgefragt und erhielt Antworten, die mich nicht wirklich hoffen ließen. (ansatzweise habe ich das weiter oben schon angedeutet)
Also musste ich mir selber Gedanken machen… aber erst einmal googlen… warum sollte ich das Rad neu erfinden… 😉

hier die Lösung für die Cluster Umverteilung

Und siehe da, es hatte mir jemand die Arbeit abgenommen… 😉
Fermin Sanchez von der fsis GmbH hatte sich zu diesem Thema schon einmal Gedanken gemacht und (für mich) glücklicherweise in seinem Blog veröffentlicht.

Import-Module FailoverClusters
 
$clustergroups = Get-ClusterGroup | Where-Object {$_.IsCoreGroup -eq $false}
foreach ($cg in $clustergroups)
{
    $CGName = $cg.Name
    Write-Host "`nWorking on $CGName"
    $CurrentOwner = $cg.OwnerNode.Name
    $POCount = (($cg | Get-ClusterOwnerNode).OwnerNodes).Count
    if ($POCount -eq 0)
    {
        Write-Host "Info: $CGName doesn't have a preferred owner!" -ForegroundColor Magenta
    }
    else
    {
        $PreferredOwner = ($cg | Get-ClusterOwnerNode).Ownernodes[0].Name
        if ($CurrentOwner -ne $PreferredOwner)
        {
            Write-Host "Moving resource to $PreferredOwner, please wait..."
            $cg | Move-ClusterGroup -Node $PreferredOwner
        }
        else
        {
            write-host "Resource is already on preferred owner! ($PreferredOwner)"
        }
    }
}
Write-Host "`n`nFinished. Current distribution: "
Get-ClusterGroup | Where-Object {$_.IsCoreGroup -eq $false}

Da Fermin das Skript in seinem Blog „as-is“ veröffentlicht hat, musste ich das Skript natürlich erst einmal testen, hierzu habe ich die eigentliche „Move-Zeile“ auskommentiert. Das Ergebnis dieses Testlauf brachte genau das Ergebnis, was ich mir erhofft hatte. Im Rahmen des letzten Patchdays habe ich das Skript nach dem Patchen erfolgreich eingesetzt und konnte so Kundenbeschwerden über mangelhafte Performance des SQL-Server vermeiden.

Vielen Dank an Fermin und seine Leistung!

Login Migration von SQL Server 2005 auf 2008R2 inklusive Passwörtern

Gestern stand ich mal wieder vor der Aufgabe einen älteren SQL Server (SQL Server 2005) auf einen „neueren“ SQL Server (SQL Server 2008 R2) zu migirieren.

Auftrag:
Kopiere bitte die Datenbanken 1-10 auf den neuen Server
Kopiere bitte alle User/Logins auf den neuen Server inkl. aller Berechtigungen

Nun kennen wir eigentlich alle diese Login-Migrations-Skripte von Microsoft => Übertragen von Benutzernamen und Kennwörtern zwischen Instanzen von SQL Server oder aus dem KB-Artikel 246133

Aber bringt der uns wirklich weiter, wenn wir funktionierende Passwörter brauchen?
Kann man mit diesen Skripten erfolgreich die Berechtigungen kopieren?

Aus meiner Erfahrung… Leider NEIN.
Also bevor ich mich wieder „schwarz“ ärgere und hinterher mehr Arbeit mit dem Aufräumen und Passwörter korrigieren habe, habe ich erstmal fleißig gegoogelt, denn eigentlich müssten doch viel mehr Leute Probleme mit der Login Migration haben.

Beim Suchen bin ich über einen Beitrag „Transferring Logins to a Database Mirror“ und habe es ausprobiert.
Das Skript bzw die Stored Procedure macht genau das was ich gesucht habe bzw was ich gebraucht habe.

Allerdings fand ich keine Anleitung dazu, daher schreibe ich mein Vorgehen bis zur erfolgreichen Umsetzung auf. Es ist nicht wirklich kompliziert, aber man muss sich erst einmal durch den Skript Code wühlen, um zu verstehen was erwartet wird bzw wie vorzugehen ist.

  1. mit dem Ziel-SQL-Server verbinden
  2. den Quell-SQL Server als Linked-Server einrichten
  3. das Skript ausführen, um die Stored Procedure anzulegen
  4. die Stored Procedure mit Angabe des Quell-Servers ausführen
    EXEC dba_CopyLogins @PartnerServer=LinkedServer, @Debug = 0 (Default = Ausführen) / 1 (Print-Ausgabe)
  5. ggf. Fehlermeldungen / Hinweise überprüfen
  6. Datenbanken und User/Logins auf Funktion prüfen

Fertig!

Das Skript lässt sich mit dem Parameter @Debug = 1 auch auf die reine Textausgabe umstellen, so dass man sich das Ergebnis erst einmal anschauen kann.
Die Stored Procedure legt die Logins mit der gleichen SID an und kopiert auch die Passwörter in korrekter Weise mit, somit dürfte es später auch keinerlei Schwierigkeiten mit „Orphaned Users“ geben.

Die aktuelle Version des Skriptes findet ihr natürlich beim Autoren, hier ist der Stand vom 08.Oktober 2015 veröffentlicht.

Use master;
Go

If Exists (Select 1 From INFORMATION_SCHEMA.ROUTINES
			Where ROUTINE_NAME = 'dba_CopyLogins'
			And ROUTINE_SCHEMA = 'dbo')
	Drop Procedure dbo.dba_CopyLogins
Go

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO

Create Procedure dbo.dba_CopyLogins
	@PartnerServer sysname,
	@Debug bit = 0
As

Declare @MaxID int,
	@CurrID int,
	@SQL nvarchar(max),
	@LoginName sysname,
	@IsDisabled int,
	@Type char(1),
	@SID varbinary(85),
	@SIDString nvarchar(100),
	@PasswordHash varbinary(256),
	@PasswordHashString nvarchar(300),
	@RoleName sysname,
	@Machine sysname,
	@PermState nvarchar(60),
	@PermName sysname,
	@Class tinyint,
	@MajorID int,
	@ErrNumber int,
	@ErrSeverity int,
	@ErrState int,
	@ErrProcedure sysname,
	@ErrLine int,
	@ErrMsg nvarchar(2048)
Declare @Logins Table (LoginID int identity(1, 1) not null primary key,
						[Name] sysname not null,
						[SID] varbinary(85) not null,
						IsDisabled int not null,
						[Type] char(1) not null,
						PasswordHash varbinary(256) null)
Declare @Roles Table (RoleID int identity(1, 1) not null primary key,
					RoleName sysname not null,
					LoginName sysname not null)
Declare @Perms Table (PermID int identity(1, 1) not null primary key,
					LoginName sysname not null,
					PermState nvarchar(60) not null,
					PermName sysname not null,
					Class tinyint not null,
					ClassDesc nvarchar(60) not null,
					MajorID int not null,
					SubLoginName sysname null,
					SubEndPointName sysname null)

Set NoCount On;

If CharIndex('\', @PartnerServer) > 0
  Begin
	Set @Machine = LEFT(@PartnerServer, CharIndex('\', @PartnerServer) - 1);
  End
Else
  Begin
	Set @Machine = @PartnerServer;
  End

-- Get all Windows logins from principal server
Set @SQL = 'Select P.name, P.sid, P.is_disabled, P.type, L.password_hash' + CHAR(10) +
		'From ' + QUOTENAME(@PartnerServer) + '.master.sys.server_principals P' + CHAR(10) +
		'Left Join ' + QUOTENAME(@PartnerServer) + '.master.sys.sql_logins L On L.principal_id = P.principal_id' + CHAR(10) +
		'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
		'And P.name <> ''sa''' + CHAR(10) +
		'And P.name Not Like ''##%''' + CHAR(10) +
		'And CharIndex(''' + @Machine + '\'', P.name) = 0;';

Insert Into @Logins (Name, SID, IsDisabled, Type, PasswordHash)
Exec sp_executesql @SQL;

-- Get all roles from principal server
Set @SQL = 'Select RoleP.name, LoginP.name' + CHAR(10) +
		'From ' + QUOTENAME(@PartnerServer) + '.master.sys.server_role_members RM' + CHAR(10) +
		'Inner Join ' + QUOTENAME(@PartnerServer) + '.master.sys.server_principals RoleP' +
		CHAR(10) + char(9) + 'On RoleP.principal_id = RM.role_principal_id' + CHAR(10) +
		'Inner Join ' + QUOTENAME(@PartnerServer) + '.master.sys.server_principals LoginP' +
		CHAR(10) + char(9) + 'On LoginP.principal_id = RM.member_principal_id' + CHAR(10) +
		'Where LoginP.type In (''U'', ''G'', ''S'')' + CHAR(10) +
		'And LoginP.name <> ''sa''' + CHAR(10) +
		'And LoginP.name Not Like ''##%''' + CHAR(10) +
		'And RoleP.type = ''R''' + CHAR(10) +
		'And CharIndex(''' + @Machine + '\'', LoginP.name) = 0;';

Insert Into @Roles (RoleName, LoginName)
Exec sp_executesql @SQL;

-- Get all explicitly granted permissions
Set @SQL = 'Select P.name Collate database_default,' + CHAR(10) +
		'	SP.state_desc, SP.permission_name, SP.class, SP.class_desc, SP.major_id,' + CHAR(10) +
		'	SubP.name Collate database_default,' + CHAR(10) +
		'	SubEP.name Collate database_default' + CHAR(10) +
		'From ' + QUOTENAME(@PartnerServer) + '.master.sys.server_principals P' + CHAR(10) +
		'Inner Join ' + QUOTENAME(@PartnerServer) + '.master.sys.server_permissions SP' + CHAR(10) +
		CHAR(9) + 'On SP.grantee_principal_id = P.principal_id' + CHAR(10) +
		'Left Join ' + QUOTENAME(@PartnerServer) + '.master.sys.server_principals SubP' + CHAR(10) +
		CHAR(9) + 'On SubP.principal_id = SP.major_id And SP.class = 101' + CHAR(10) +
		'Left Join ' + QUOTENAME(@PartnerServer) + '.master.sys.endpoints SubEP' + CHAR(10) +
		CHAR(9) + 'On SubEP.endpoint_id = SP.major_id And SP.class = 105' + CHAR(10) +
		'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
		'And P.name <> ''sa''' + CHAR(10) +
		'And P.name Not Like ''##%''' + CHAR(10) +
		'And CharIndex(''' + @Machine + '\'', P.name) = 0;'

Insert Into @Perms (LoginName, PermState, PermName, Class, ClassDesc, MajorID, SubLoginName, SubEndPointName)
Exec sp_executesql @SQL;

Select @MaxID = Max(LoginID), @CurrID = 1
From @Logins;

While @CurrID <= @MaxID
  Begin
	Select @LoginName = Name,
		@IsDisabled = IsDisabled,
		@Type = [Type],
		@SID = [SID],
		@PasswordHash = PasswordHash
	From @Logins
	Where LoginID = @CurrID;
	
	If Not Exists (Select 1 From sys.server_principals
				Where name = @LoginName)
	  Begin
		Set @SQL = 'Create Login ' + quotename(@LoginName)
		If @Type In ('U', 'G')
		  Begin
			Set @SQL = @SQL + ' From Windows;'
		  End
		Else
		  Begin
			Set @PasswordHashString = '0x' +
				Cast('' As XML).value('xs:hexBinary(sql:variable("@PasswordHash"))', 'nvarchar(300)');
			
			Set @SQL = @SQL + ' With Password = ' + @PasswordHashString + ' HASHED, ';
			
			Set @SIDString = '0x' +
				Cast('' As XML).value('xs:hexBinary(sql:variable("@SID"))', 'nvarchar(100)');
			Set @SQL = @SQL + 'SID = ' + @SIDString + ';';
		  End

		If @Debug = 0
		  Begin
			Begin Try
				Exec sp_executesql @SQL;
			End Try
			Begin Catch
				Set @ErrNumber = ERROR_NUMBER();
				Set @ErrSeverity = ERROR_SEVERITY();
				Set @ErrState = ERROR_STATE();
				Set @ErrProcedure = ERROR_PROCEDURE();
				Set @ErrLine = ERROR_LINE();
				Set @ErrMsg = ERROR_MESSAGE();
				RaisError(@ErrMsg, 1, 1);
			End Catch
		  End
		Else
		  Begin
			Print @SQL;
		  End
		
		If @IsDisabled = 1
		  Begin
			Set @SQL = 'Alter Login ' + quotename(@LoginName) + ' Disable;'
			If @Debug = 0
			  Begin
				Begin Try
					Exec sp_executesql @SQL;
				End Try
				Begin Catch
					Set @ErrNumber = ERROR_NUMBER();
					Set @ErrSeverity = ERROR_SEVERITY();
					Set @ErrState = ERROR_STATE();
					Set @ErrProcedure = ERROR_PROCEDURE();
					Set @ErrLine = ERROR_LINE();
					Set @ErrMsg = ERROR_MESSAGE();
					RaisError(@ErrMsg, 1, 1);
				End Catch
			  End
			Else
			  Begin
				Print @SQL;
			  End
		  End
		End
	Set @CurrID = @CurrID + 1;
  End

Select @MaxID = Max(RoleID), @CurrID = 1
From @Roles;

While @CurrID <= @MaxID
  Begin
	Select @LoginName = LoginName,
		@RoleName = RoleName
	From @Roles
	Where RoleID = @CurrID;

	If Not Exists (Select 1 From sys.server_role_members RM
				Inner Join sys.server_principals RoleP
					On RoleP.principal_id = RM.role_principal_id
				Inner Join sys.server_principals LoginP
					On LoginP.principal_id = RM.member_principal_id
				Where LoginP.type In ('U', 'G', 'S')
				And RoleP.type = 'R'
				And RoleP.name = @RoleName
				And LoginP.name = @LoginName)
	  Begin
		If @Debug = 0
		  Begin
			Exec sp_addsrvrolemember @rolename = @RoleName,
							@loginame = @LoginName;
		  End
		Else
		  Begin
			Print 'Exec sp_addsrvrolemember @rolename = ''' + @RoleName + ''',';
			Print '		@loginame = ''' + @LoginName + ''';';
		  End
	  End

	Set @CurrID = @CurrID + 1;
  End

Select @MaxID = Max(PermID), @CurrID = 1
From @Perms;

While @CurrID <= @MaxID
  Begin
	Select @PermState = PermState,
		@PermName = PermName,
		@Class = Class,
		@LoginName = LoginName,
		@MajorID = MajorID,
		@SQL = PermState + space(1) + PermName + SPACE(1) +
			Case Class When 101 Then 'On Login::' + QUOTENAME(SubLoginName)
					When 105 Then 'On ' + ClassDesc + '::' + QUOTENAME(SubEndPointName)
					Else '' End +
			' To ' + QUOTENAME(LoginName) + ';'
	From @Perms
	Where PermID = @CurrID;
	
	If Not Exists (Select 1 From sys.server_principals P
				Inner Join sys.server_permissions SP On SP.grantee_principal_id = P.principal_id
				Where SP.state_desc = @PermState
				And SP.permission_name = @PermName
				And SP.class = @Class
				And P.name = @LoginName
				And SP.major_id = @MajorID)
	  Begin
		If @Debug = 0
		  Begin
			Begin Try
				Exec sp_executesql @SQL;
			End Try
			Begin Catch
				Set @ErrNumber = ERROR_NUMBER();
				Set @ErrSeverity = ERROR_SEVERITY();
				Set @ErrState = ERROR_STATE();
				Set @ErrProcedure = ERROR_PROCEDURE();
				Set @ErrLine = ERROR_LINE();
				Set @ErrMsg = ERROR_MESSAGE();
				RaisError(@ErrMsg, 1, 1);
			End Catch
		  End
		Else
		  Begin
			Print @SQL;
		  End
	  End

	Set @CurrID = @CurrID + 1;
  End

Set NoCount Off;

Ich werde das Skript in Zukunft öfter nutzen, da es für mich und meinem Zwecke der Login Migration sehr gut funktioniert hat.
Vielen Dank an den SQLSoldier

SQL Server – Security Bulletin MS15-058

Es war wieder Patch Tuesday (14. Juli 2015):

Der SQL Server hatte seit letztem August kein Sicherheitsupdate erhalten, nun war es soweit, der Download wurde auf den Downloadserver freigegeben.
Beide Fixes für GDR und QFE wurde veröffentlicht, weitere Details siehe Security Bulletin MS15-058.

Dieses Security Bulletin MS15-058 wurde veröffentlicht, um die Sicherheitsanfälligkeit in der Ausführung von Remote-Code zu beseitigen (für Details dazu finden Sie unter KB #3065718).Wenn Sie eine der folgenden Versionen verwenden, sollten Sie diesen Patch zeitnah installieren:

  • SQL Server 2014 SP1 – unaffected, but there is a GDR for a wrong results bug
  • SQL Server 2014 RTM – affected
  • SQL Server 2012 SP2 – affected
  • SQL Server 2012 SP1 – affected
  • SQL Server 2012 RTM – likely affected but you need to move to SP1 or SP2 for the fix
  • SQL Server 2008 R2 SP3 – affected
  • SQL Server 2008 R2 SP2 – affected
  • SQL Server 2008 R2 SP1 – likely affected but you need to move to SP2 or SP3 for the fix
  • SQL Server 2008 R2 RTM – likely affected but you need to move to SP2 or SP3 for the fix
  • SQL Server 2008 SP4 – affected
  • SQL Server 2008 SP3 – affected
  • SQL Server 2008 SP2 – likely affected but you need to move to SP3 or SP4 for the fix
  • SQL Server 2008 SP1 – likely affected but you need to move to SP3 or SP4 for the fix
  • SQL Server 2008 RTM – likely affected but you need to move to SP3 or SP4 for the fix

Sollten Sie unsicher sein, ob und welchen Patch Sie installieren sollten, hat Ihnen Aaron Bertrand auch eine Matrix zur Verdeutlichung erstellt.

Unter Umständen sind auch ältere Versionen von dem Bug / Loch betroffen, allerdings wird für diese kein Fix mehr (über öffentliche Server) zur Verfügung gestellt.