NEU: SQL Server Code Name “Denali” CTP3 Produkt Dokumentation

Der SQL Server Code Name “Denali” Community Technology Preview 3 (CTP3) Product Guide v1 ist Sammlung von unterschiedlichen Ressourcen und beinhaltet jede Menge brauchbarer und wichtiger Dokumentation, Demos und Beispiele für und zur neuen SQL Server “Denali” CTP3 Version.

Zwecks besserer Organisation steht er als einzelener Link zum Download zur Verfügung: http://go.microsoft.com/fwlink/?LinkID=225814.

Der Product Guide v1 enthält:

  • 14 Product Datasheets
  • 8 PowerPoint Presentations
  • 5 Technical White Papers
  • 13 Hands-On Lab Preview Documents
  • 6 Click-Through Demonstrations
  • 13 Self-Running Demonstrations
  • 26 Links to On-Line References
  • 44 Links to On-Line Videos including 26 Presentations from North America TechEd 2011

Wichtig: Das SQL Server Team ist immer Dankbar für Feedback von Kunden und Partnern. Über diesen Link haben alle die SQL Server “Denali” gerade testen die Möglichkeit innerhalb der nächsten 60 Tage Feedback direkt ins Product Team nach Redmond zu geben – das Product Team kann innerhalb dieser Zeit noch sinnvolles Feedback direkt in die finale SQL Server nNext Version einarbeiten!

Viel Spaß beim Testen und Lesen,

Martin Pöckl

SQL Server “Denali” CTP 3 verfügbar!

Ab sofort kann man hier die öffentliche Community Technology Preview Version 3 des nächsten SQL Server Generation Codename “Denali” herunterladen! Diese Version hat bereits die sogenannte “Production Quality”, kann also für reale Testszenarien sowie Upgrade-Tests verwendet werden.

Die Hauptinvestitionen und daher Neuerungen in SQL Server “Denali” lassen sich unter den folgenden drei großen Punkten zusammenfassen:

  • “Mission Critical Plarform” bedeutet, dass SQL Server “Denali” höchstmögliche Performance und bis zu 100% Verfügbarkeit für die größten geschäftskritischen Anwendungen in Unternehmen bietet. Und das bei gleichzeitig niedrigen Betriebskosten – nachgewiesener Maßen die niedrigsten im Datenbankumfeld. Neu sind in diesem Bereich unter anderem die Hochverfügbarkeitslösung “AlwaysOn” oder hochperformante Data Warehouse Abfragen mit den “Columnstore Indizes”.
  • “Breakthrough Insights” soll die Marktführerschaft im BI-Bereich (siehe Gartner Magic Quadrant BI Platform, Jan. 2011) ausbauen. Dazu gehört die interaktive, webbasierte Berichtserstellung und Datenanalyse mit Project “Crescent”, die neue serverseitige In-memory BI Technologie (das sogenannte “BI Sematic Model”) oder die Sicherung der Datenqualität mit Data Quality Services.
  • “Cloud on your Terms” bedeutet schließlich, dass der Kunde freie Wahl hat, inwieweit er in die Cloud geht oder auch nicht: von der klassischen On-Premise Lösung über Private Cloud bis zur Public Cloud mit SQL Azure ist alles auf der Basis von SQL Server Technologie unterstützt. Mit “Juneau” und den Verbesserungen bei den DAC Paketen ist auch eine einheitliche Entwicklung über alle Szenarien möglich.

Gegenüber der ersten SQL Server “Denali” CTP von November 2010 gibt es eine Vielzahl von Neuerungen, die mein Kollege Stefen Krause hervorragend zusammengefasst hat: http://blogs.technet.com/b/sqlteamgermany/archive/2011/07/12/sql-server-denali-ctp3-ist-verf-252-gbar.aspx

Alles in allem gibt es eine Vielzahl an großen und kleinen Neuerungen, aber besonders die neue Hochverfügbarkeitstechnologie, die neuen bahnbrechenden BI Features und die teilweise massiv gesteigerte Performance sollten Grund genug sein SQL Server “Denali” zu testen.

Im Rahmen der SQL Server User Group Österreich werden wir im Herbst auf Basis der CTP 3 einen ein-tätigen, technischen “What’s new” Workshop mit Milos Radivojevic von den Solid Quality Mentors bei freiem Eintritt veranstalten. Im November folgt die lokale Microsoft Österreich SQL “Denali” Launch Veranstaltung. Die finale SQL Server vNext Release wird dann Anfang 2012 für Partner und Kunden kommerziell und zum testen verfügbar sein. Stay tuned for further details!

Viel Spaß beim Testen!
Martin Pöckl
martin.poeckl@microsoft.com

Microsoft Business Intelligence Award 2011

Ab sofort ist es möglich, für den Microsoft Business Intelligence Award 2011 einzureichen. Der Award wird von Microsoft Österreich gemeinsam mit der Computerwelt ausgeschrieben, um innovative Lösungen zu prämieren, die neue Einsichten durch BI vermitteln und Türen zu neuen Einsatzgebieten mit BI aufstoßen.

Einreichen kann man jede Lösung, bei der Business Intelligence Technologien innovativ zum Einsatz kommen. Typischerweise unter Verwendung von SharePoint, SQL Server, oder Dynamics oder von Lösungen der Microsoft Partner. Dem Gewinner winkt die Teilnahme an einer Business Intelligence Konferenz von Microsoft im Frühjahr 2012 in den USA, um sich dort mit den Experten in diesem Bereich auszutauschen. Jede Einreichung wird mit einem Jahresabo der Computerwelt belohnt.

Einreichen kann man bis zum 2.September 2011.
Weitere Informationen unter www.microsoft.at/bi-award.

Viel Glück!
Martin Pöckl
martin.poeckl@microsoft.com

Nächstes Treffen 21.06.: „Deadlock Probleme in SQL Server 2008 – erkennen und lösen“

Im Rahmen unseres nächsten SQL User Group Treffens am 21.06.2011 wird uns Milos Radivojevic mehr zum Thema “Wie erkenne und löse ich Deadlocks in SQL Server” erzählen.

Wenn Zeit bleibt werden wir uns am Ende auch kurz dem Thema “Transactions and Error Handling in SQL Server” widmen.

Hinweis: wir wollen ca. 20 Minuten am Ende die Möglichkeit geben SQL spezifische Fragen zu stellen. Bitte Eure Frage vorab an sqlserverug@live.at schicken, Milos wird die Fragen soweit möglich beantworten.

Treffpunkt am 21.06. ist wie immer um 18:00 bei Microsoft Österreich, Euro Platz 3, 1120 Wien! Anmeldung bitte formlos per E-Mail unter sqlserverug@live.at.

Wir freuen uns schon auf Euer Kommen!

Advanced SQL Server Performance Troubleshooting Workshop in Wien

Für alle die schon immer in die Tiefen von SQL Server Tuning und Troubleshooting eintauchen wollten veranstaltet Klaus Aschenbrenner im September (26.-28.) einen dreitägigen “Advanced SQL Server Performance Troubleshooting Workshop” in Wien.

Details, Kosten und Anmeldung: http://www.csharp.at/Registration_AT.aspx

Martin Pöckl
martin.poeckl@microsoft.com

Nächstes Treffen 26.05.: „Partitionieren von Tabellen anhand eines Praxisbeispiels“

Vielen Dank für euere Themenvorschläge!! Die Gewinner der T-Shirts bzw. der Tassen werden von uns schriftlich benachrichtigt.

Wir haben uns für das Thema: „Partitionieren von Tabellen anhand eines Praxisbeispiels“ entschieden. Speaker wird Georg Gamsjäger sein.

Agenda:
– Ausgangssituation
– Grundlagen der Partitionierung im Überblick
– Warum ist die Partitionierung in diesem Beispiel so ideal
– Performanceverbesserungen feststellen
– Automatisierte Verwaltung der Partition

Treffpunkt ist wie immer um 18:00 bei Microsoft Österreich, Euro Platz 3, 1120 Wien! Anmeldung bitte unter sqlserverug@live.at

Wir freuen uns schon auf Euer Kommen!

Substring vs. LIKE

Wenn eine TSQL-Aufgabe in der WHERE Klausel einer Abfrage mit der SUBSTRING Funktion oder dem LIKE Operator implementiert werden kann, soll man sich für die Variante mit dem LIKE Operator entscheiden. Entweder ist diese Option besser oder beide Varianten performieren gleich gut.

Eine SUBSTRING Variante ist nie besser als der LIKE-Operator. Deshalb wird es empfohlen (aufgrund der Performance), den LIKE Operator zu verwenden. Außerdem, ist die SUBSTRING-Option fehlerfreundlich und nicht intuitiv.

Das ist eine Ableitung einer generellen Empfehlung, dass ein Funktionsaufruf in der WHERE Klausel, wo eine Tabellenspalte als Funktionsargument verwendet wird, durch einen entsprechenden Operator ersetzt werden soll.

Code-Beispiel

Als Beispiel nehmen wir die Tabelle Person.Contact aus der AdventureWorks Datenbank. Falls ein Index auf die Spalte LastName nicht existiert, legen wir einen an:

IF NOT EXISTS(SELECT 1 FROM sysindexes WHERE name='IX_Contact_LastName')
CREATE NONCLUSTERED INDEX IX_Contact_LastName ON Person.Contact (LastName)

Jetzt sind wir sicher, dass der entsprechende Index existiert und wir können zwei Varianten einer Abfrage probieren.

Abfrage 1 (low selectivity)

Zunächst holen wir alle Kontakten mit Nachnamen, die mit A anfangen. Hier sind die Abfragen:

SELECT * FROM Person.Contact WHERE SUBSTRING(LastName, 1, 1)='A'
GO
SELECT * FROM Person.Contact WHERE LastName LIKE 'A%'

Werfen wir nun einen Blick auf die Ausführungsinformationen:

Beide Abfragen sind logisch äquivalent und liefern 911 Zeilen. Das sind ungefähr 4,5% aller Zeilen, aber mit allen Spalten. Deshalb hat der SQL Server geschätzt, dass die Verwendung des Indizes IX_Contact_LastName und entsprechende Key-Lookups teuer sind und sich für das Scannen der ganzen Tabelle entschieden. Die Abfrage ist nicht genug „selektiv“ für eine Indexverwendung und aus diesem Grund ist es egal, wie die WHERE Klausel implementiert ist.

Abfrage 2 (high selectivity)

Wiederholen wir jetzt diese Aufgabe mit der Änderung, dass der Nachname mit Acho beginnen soll. Hier sind die biden angepassten Abfragen:

SELECT * FROM Person.Contact WHERE SUBSTRING(LastName, 1, 4)='Acho'
GO
SELECT * FROM Person.Contact WHERE LastName LIKE 'Acho%'

Selbstverständlich sind die Abfragen wieder logisch äquivalent. Diesmal liefern sie nur eine Zeile zurück – diese Abfrage ist sehr „selektiv“. Das ist ein Zeichen für den SQL Server Optimizer, dass eine Indexverwendung sinnvoll wäre. Er hat entschieden den Index IX_Contact_LastName zu verwenden. Betrachten wir die Ausführungsdetails:

Wir sehen, dass in beiden Fällen der Index verwendet wird, aber nur die LIKE-Variante verwendet den Index-Seek Operator. In der ersten Abfrage wird die SUBSTRING Funktion für jede äußere Zeile evaluiert und deshalb musste der SQL Server Optimizer den Index-Scan Operator verwenden.

Die LIKE-Variante limitiert den Optimizer nicht und er konnte den besten Plan generieren. Nur fünf Seiten hat der SQL Server lesen müssen, um das Ergebnis liefern zu können. In der ersten Variante waren 60 Seiten dafür notwendig.

Fazit

Verwenden Sie immer LIKE anstatt SUBSTRING. Eine Abfrage mit dem SUBSTRING-Funktionsaufruf performiert nie besser als eine logisch äquivalente Abfrage mit dem LIKE-Operator.

IN vs. BETWEEN

Wenn eine TSQL-Aufgabe in der WHERE Klausel einer Abfrage mit den IN oder BETWEEN Operator implementiert werden kann, soll man sich für die Variante mit dem BETWEEN Operator entscheiden.

Entweder ist diese Option besser oder beide Varianten performieren gleich gut. Der Unterschied ist nicht so groß, aber der Vorteil liegt auf der Seite des BETWEEN-Operators.

Code-Beispiel

Als Beispiel nehmen wir die AdventureWorks Datenbank und die Tabelle SalesOrderHeader aus dem Schema Sales. Wir sollen Order-Details für die Order 43663 und 43664 zurückliefern. Hier sind zwei Abfragen, die jeweils eine Liste mit diesen zwei Orders generieren:

SELECT * FROM Sales.SalesOrderHeader WHERE SalesOrderID IN (43663,43664)
GO
SELECT * FROM Sales.SalesOrderHeader WHERE SalesOrderID >= 43663 AND SalesOrderID <= 43664

Die Ausführungspläne sind identisch:

In den Statistikinformationen sieht man aber, dass in der ersten Abfrage sechs, in der zweiten hingegen nur drei logical reads gemacht wurden:

Analyse

In der Statistik für die erste Abfrage können wir sehen, dass die Clustered Index Seek-Operation zweimal (Scan count 2) durchgeführt wurde. Warum zweimal? Weil der Optimizer eine IN Abfrage in eine entsprechende OR Anfrage konvertiert hat. Eine Index Seek-Operation wird für jedes OR-Teil durchgeführt. Schauen wir das genauer an:

SELECT * FROM Sales.SalesOrderHeader WHERE SalesOrderID IN (43663,43664)
GO
SELECT * FROM Sales.SalesOrderHeader WHERE SalesOrderID=43663 OR SalesOrderID=43664

Mit Hilfe der Ausführungsinformationen können wir herausfinden, dass beide Abfragen mit dem absolut identischen Ausführungsplan und derselben Anzahl an gelesenen Seiten beendet worden sind.

Fazit

Der BETWEEN oder Range-Operator ist ein bisschen besser als IN. Sehr oft spielt dieser Unterschied keine Rolle, aber es ist nicht schlecht zu wissen, wie der Optimizer mit dem IN Operator umgeht. Außerdem gibt es Situationen, wo jeder Leservorgang wichtig ist und dort kann dieser Unterschied sinnvoll sein

Unique-Constraints und Performance

Ein Unique-Constraint wird in einer Tabelle definiert, wenn in einer Spalte nur eindeutige Werte (inklusive NULL) möglich sind. Das ist der einzige Constraint, der durch eine Index-Erstellung umgesetzt wird. Um einen Unique-Constraint zu erstellen, muss man einen UNIQUE Index anlegen. Constraints sind in erster Linie eine Garantie für Datenintegrität. Man vergisst aber oft, dass diese auch den SQL Server Optimizer bei der Ausführungsplan-Generierung unterstützen.

Code-Beispiel

Als Beispiel erstellen wir eine Tabelle (dbo.OrderTest). Die Tabelle hat nur drei Spalten, die für unser Problem ausreichen:

--Tabelle OrderTest
IF OBJECT_ID('dbo.OrderTest') IS NOT NULL DROP TABLE dbo.OrderTest
GO
CREATE TABLE dbo.OrderTest(
order_id int NOT NULL,
order_date date NOT NULL,
status_id tinyint NOT NULL
CONSTRAINT PK_OrderTest PRIMARY KEY CLUSTERED(order_id)
)
GO

Mit diesem Skript füllen wir die Tabelle mit Order-Details. Nach der Ausführung befinden sich in der Tabelle eine Million Orders, mit dem Datum zwischen dem 1.1.2007 und 31.12.2010 und einer Zahl zwischen 1 und 5 als Status ID.

Aufgabe

Unsere Aufgabe ist einfach: Alle Order-Details anzeigen, wobei die Status ID durch die entsprechenden Bezeichnung ersetzt werden soll.

Diese zusätzliche Anforderung wird mit einer sogenannten Lookup-Tabelle umgesetzt. Erstellen wir nun die Lookup-Tabelle und füllen wir sie mit fünf Einträgen:

--Tabelle StatusLookup anlegen
IF OBJECT_ID('dbo.StatusLookup') IS NOT NULL
DROP TABLE dbo.StatusLookup
GO
CREATE TABLE dbo.StatusLookup(
id tinyint not null,
text varchar(20) not null)
GO
--Tabelle StatusLookup füllen
INSERT INTO dbo.StatusLookup(id, text)
VALUES(1, 'eingelangt'),(2, 'in Bearbeitung'),(3, 'Rückgabe'),(4, 'geschlossen'),(5, 'gelöscht')
GO

Die Tabelle dbo.StatusLookup ist eine ganz einfache Tabelle mit nur fünf Einträgen. Deshalb ist es üblich, dass eine solche Tabelle als Heap Tabelle (ohne Clustered Index) umgesetzt wird.

Ergebnis

Jetzt haben wir beide Tabellen und können die gewünschte Abfrage erstellen:


SELECT o.order_id,o.order_date,
(SELECT s.text FROM dbo.StatusLookup s WHERE s.id=o.status_id) status
FROM dbo.OrderTest o

Wir listen die ID und das Datum des Orders aus und fügen die Statusbezeichnung mit einer Subquery hinzu. Auf meinem Notebook ist die Ausführungszeit für diese Abfrage 10.9 Sekunden. Hier sind die Ausführungsinformationen bzw. der Ausführungsplan:

Ergebnisanalyse

Was kann man hier sehen? Die Tabelle dbo.StatusLookup wurde 1.000.000 mal gescannt! Auf jede Zeile aus der OrdeTest Tabelle wurden drei Operatoren angewandt (Table Scan, Stream Aggregate und Assert). Warum?

Eine Subquery kann nur einen skalaren Wert liefern. Deshalb muss der SQL Server überprüfen, ob die innere Abfrage für jede äußere Zeile nur einen Datensatz zurückliefert. Falls es mehrere Datensätze gibt, wird ein Fehler ausgelöst. Mit den Operatoren Table Scan und Stream Aggregate wird die Anzahl von inneren Datensätzen festgelegt und dann der Assert-Operator überprüft dann, ob diese Anzahl größer als 1 ist. Wie gesagt, wird das für jede Zeile durchgeführt – d.h. 1 Mio Ausführungen!

Diese Überprüfung limitiert den SQL Optimizer bei der Auswahl der Join-Strategie und somit bleibt ihm nur Nested Loops Join als einzige Option. Diese Option ist aber ineffizient, wenn die äußere Tabelle zu groß ist. Die Tabelle dbo.StatusLookup enthält nur fünf Zeilen, ohne ID-Duplikate. Aber Duplikate sind nicht explizit verboten. Daher muss immer überprüft werden, ob es zwei Zeilen mit gleicher ID gibt. Bei der Tabellenerstellung haben wir nicht gesagt, dass ein Duplikat nicht möglich ist.

So, was können wir hier tun? Dem SQL Server Optimizer sagen, dass er diese Überprüfung nicht machen muss! Das sagen wir mit einem Unique Constraint. Erstellen wir noch eine Lookup-Tabelle:

Unique Constraint als Performanceverbesserungsfaktor

IF OBJECT_ID('dbo.StatusLookup2') IS NOT NULL DROP TABLE dbo.StatusLookup2
GO
CREATE TABLE dbo.StatusLookup2(
id tinyint not null,
text varchar(20) not null)
GO
CREATE UNIQUE INDEX ix_StatusLookup2_id on dbo.StatusLookup2(id)
go
INSERT INTO dbo.StatusLookup2(id, text)
VALUES(1, 'eingelangt'),(2, 'in Bearbeitung'),(3, 'Rückgabe'),(4, 'geschlossen'),(5, 'gelöscht')
GO

Diesmal haben wir einen Unique Index hinzugefügt und damit die Werte für die Spalte id beschränkt. Führen wir unsere Abfrage erneut aus – diesmal mit der neuen Lookup-Tabelle:

SELECT o.order_id,o.order_date,
(SELECT s.text FROM dbo.StatusLookup2 s WHERE s.id=o.status_id) status
FROM dbo.OrderTest o

Schauen wir jetzt die Ausführungsinfo an:

Analyse

Die Ausführungszeit ist um 2 Sekunden reduziert und die CPU Zeit von 9.079 auf 749 ms! Anhand des Plans können wir sehen, dass der Optimizer aufgrund des hinzugefügten Unique Constraint nicht mehr Duplikatten in der Lookup-Tabelle überprüft und dass er finden konnte, dass diese Subquery-Abfrage äquivalent zu einem Left Join ist. Deshalb hat er einen optimalen Plan generiert, bei dem die Tabelle dbo.StatusLookup2 nur einmal gelesen werden musste! Wenn wir diese zwei Pläne im SQL Server Management Studio direkt miteinander vergleichen, sehen wir, dass die Version mit dem Unique Constraint nur 8% der gesamten Ressourcen verwendet – d.h. ganz grob gesagt ist dieser Plan mehr als 10 mal besser als die Nested Loop Join Variante.

So, eine Kleinigkeit, die man oft vergißt, da Lookup-Tabellen klein sind und Indizierung dort keine Rolle spielt.

Nun kann man sich fragen, warum die Ausführungszeit mit dem zweiten Plan “nur” zwei Sekunden besser ist, wenn der Plan selbst von SSMS als 10-mal besser bezeichnet wurde?

Abfrageperformance Vergleichen

Die Ergebnisliste wird zunächst generiert und dann noch dem Client (SSMS) gesendet. Die gesamte Zeit ist in diesem Fall keine gute Auswahl für einen Performance-Vergleich, da dem Client 1 Mio Zeilen_gesendet werden sollen. Wenn wir hingegen die CPU-Zeiten vergleichen (9.079 ms vs. 749 ms), bekommen wir ein realistisches Bild. Wenn wir aber die zweite Zeitkomponente annullieren möchten, können wir die Abfragen im SSMS so ausführen, dass die Ergebnisse nicht dem Client gesendet werden. Das können wir mit der Option “Discards results after execution” im Screen “Query/Query Options/Results/Grid” einstellen.

Wenn wir beide Abfragen mit dieser Option ausführen, bekommen wir für die Ausführungszeit 3.613 ms bzw. 573 ms.

Fazit

Eine Lookup-Tabelle kann freilich als Heap-Tabelle implementiert werden, aber dann darf nicht auf ein Unique Constraint auf die ID-Spalte vergessen werden. Und ganz generell: Den SQL Server mit allen relevanten Informationen füttern! Richtige und relevante Informationen können nur zur Generierung eines besseren Ausführungplans führen!

Den Beispiel-Code können Sie hier herunterladen.