Unterschied zwischen Primary Key und Clustered Index
Mai 20th
Link: http://db-berater.blogspot.de/2013/05/unterschied-zwischen-primary-key-und.html
Letzte Woche wurde ich beauftragt, die Ursachen für eine schlechte Performance innerhalb einer Datenbank zu analysieren und gegebenenfalls Hinweise zu geben, wie man die Wartezeiten im Frontend verkürzen kann. Bei der Prüfung der Ausführungspläne und Indexstrukturen ist aufgefallen, dass fast 10% der Relationen HEAPS sind und in vielen Abfragen mit anderen Relationen über JOINS verwendet wurden. Meine erste Empfehlung war demzufolge – basierend auf den Ausführungsplänen – die Implementierung und Verwendung von Clustered Indexes. Da die Anwendung nicht “in House” entwickelt worden ist, wurde sich mit der Bitte um Prüfung und Zusendung von Skripten an den Hersteller gewandt. Das nachfolgende Script soll stellvertretend für den Lösungsansatz des Herstellers dienen. Bei der im Beispiel benannten Relation handelt es sich um eine simple Relation für die Speicherung historischer Daten ohne Fremdschlüssel-Referenzen.
- CREATE CLUSTERED INDEX ix_relation_id ON dbo.tbl_relation(id);
- DELETE dbo.relation WHERE id IN (SELECT id FROM dbo.relation GROUP BY id HAVING COUNT(id) > 1);
- DROP INDEX ix_relation ON dbo.tbl_relation;
- ALTER TABLE dbo.tbl_relation ADD CONSTRAINT p_relation_id PRIMARY KEY (id);
Als ich das Script von meinem Auftraggeber erhalten habe, musste ich erst mal tief Luft holen und mich fragen, welcher “Experte” solche Skripte an seine Kunden verschickt. Zunächst wird ein Clustered Index auf dem Attribut [Id] der Relation [dbo].[tbl_relation] erstellt (Zeile 1). Anschließend werden ALLE Datensätze, die eine redundante [Id] besitzen gelöscht (Zeile 2). Nachdem die Daten gelöscht wurden, wird der Clustered Index wieder entfernt und durch einen PRIMARY KEY ersetzt.
Der brisanteste Teil in diesem Script ist sicherlich die Zeile 2; sollen doch tatsächlich Daten aus einem Produktionssystem gelöscht werden ohne diese im Vorfeld in einer Staging-Tabelle zu analysieren. Des Weiteren ist aufgefallen, dass dem Programmierer scheinbar der Unterschied zwischen einem Clustered Index und der Einschränkung eines PRIMARY KEY nicht bekannt ist. Die Intention des Programmierers wird beim Lesen der obigen Skriptzeilen relativ schnell klar; zunächst wird ein “Index” erzeugt, um schnell die redundanten Datensätze zu löschen. Anschließend soll mittels PRIMARY KEY erneut ein Clustered Index erzeugt werden, um unsere Anforderungen zu erfüllen.
Was ist ein Primary Key
Um mitzuteilen, welchen der Schlüsselkandidaten man zur Identifikation eines Datensatzes (Tupel) in einer Relation bevorzugt, wird aus allen Schlüsselkandidaten der Primärschlüssel ausgewählt. Der Primärschlüssel wird üblicherweise so ausgewählt, dass er möglichst klein ist, das heißt möglichst wenige Attribute umfasst bzw. einen möglichst simplen Datentyp hat. Ein Primärschlüssel ist KEIN Index ist sondern eine Einschränkung(CONSTRAINT). Ein Primärschlüssel besitzt die nachfolgenden Besonderheiten:
- Eine Relation kann nur einen PRIMARY KEY besitzen
- In den Attributen des PRIMARY KEY sind keine NULL-Werte zugelassen
- Die Attribute des PRIMARY KEY dürfen nicht redundant sein (doppelte Schlüsselwerte!)
- Die Einschränkung PRIMARY KEY erzwingt immer die Definition eines EINDEUTIGEN Index und nicht zwingend einen CLUSTERED INDEX!
- Ein PRIMARY KEY ist NICHT zwingend für DRI (deklarative referenzielle Integrität)
Die nachfolgenden Beispiele soll die Besonderheiten eines PRIMARY KEY verdeutlichen:
CREATE TABLE dbo.master_table
(
Id int NULL PRIMARY KEY,
col1 char(20) NULL
);
GO
Der Aufruf schlägt fehl, da das Attribut [Id] NULL-Werte zulässt während der nächste Aufruf im nächsten Beispiel funktioniert, da die Einschränkung NOT NULL für das Attribut [Id] verwendet wird.
Meldung 8111, Ebene 16, Status 1, Zeile 2 Eine PRIMARY KEY-Einschränkung kann für eine Spalte in der master_table-Tabelle, die NULL zulässt, nicht definiert werden.
CREATE TABLE dbo.master_table
(
Id int NOT NULL PRIMARY KEY,
col1 char(20) NULL
);
GO
Sofern für die Relation nicht explizit ein Clustered Index definiert wird, erzeugt SQL Server automatisch für den PRIMARY KEY einen clustered Index. Beweis liefert die Untersuchung der Indexe und Einschränkungen für die zuvor erstellte Relation
SELECT OBJECT_NAME(parent_object_id) AS table_name,
k.name AS constraint_name,
k.type AS constraint_type,
k.type_desc AS constraint_type_desc,
unique_index_id,
is_system_named,
i.name AS index_name,
i.type_desc AS index_type
FROM sys.key_constraints k LEFT JOIN sys.indexes i
ON (
k.parent_object_id = i.object_id AND
k.unique_index_id = i.index_id
)
WHERE parent_object_id = OBJECT_ID('dbo.master_table', 'U');
Das DMO sys.key_constraints enthält jeweils eine Zeile für jedes Objekt, dass entweder eine PRIMARY KEY oder eine UNIQUE Einschränkung besitzt. Die obige Abbildung zeigt, dass der PRIMARY KEY eindeutig ist und der Name der Einschränkung automatisch durch SQL Server erstellt wurde. Handelt es sich um eine eindeutige Einschränkung (UNIQUE), kann der entsprechende UNIQUE Index über die [index_id] der zugehörigen Relation ermittelt werden.
Da eine Einschränkung auch durch individuelle Namensgebung erstellt werden kann, führt das nachfolgende Beispiel ebenfalls zur Erstellung des PRIMARY KEY mit dem Unterschied, dass der Name durch den Ersteller vordefiniert wird.
CREATE TABLE dbo.master_table
(
Id int NOT NULL,
col1 char(20) NULL,
CONSTRAINT pk_master_table_id PRIMARY KEY (Id)
);
GO
Da nun der Name des PRIMARY KEY selbst festgelegt worden ist, ist das Attribut [is_system_named] nun 0. Weitere Einstellungen haben sich nicht geändert. Das ein PRIMARY KEY nicht zwingend ein Clustered Index ist, zeigt das nächste Beispiel sehr deutlich:
CREATE TABLE dbo.master_table
(
Id int NOT NULL,
col1 char(20) NULL,
CONSTRAINT pk_master_table_id PRIMARY KEY NONCLUSTERED (id)
);
GO
CREATE CLUSTERED INDEX ix_master_table_col1 ON dbo.master_table (col1);
GO
Unabhängig von der Existenz eines Clustered Index kann der PRIMARY KEY definiert werden. Da ein PRIMARY KEY nur dann als Clustered Index angelegt wird, wenn noch kein Clustered Index existiert, musste in dem obigen Beispiel der PRIMARY KEY explizit mit der Option NONCLUSTERED definiert werden um eine automatische Erstellung des Clustered Index zu verhindern. Ein Blick auf die Einschränkungen und die vorhandenen Indexe zeigt das Ergebnis.
Wozu wird ein Primary Key verwendet?
Der Sinn und Zweck eines PRIMARY KEY ergibt sich aus den Eigenschaften der Einschränkung selbst. Da ein PRIMARY KEY keine NULL-Werte enthalten darf und eindeutig sein muss, eignet er sich für die Durchsetzung von DRI (Deklarative Referenzielle Integrität). Unter Referentieller Integrität versteht man Bedingungen, die zur Sicherung der Datenintegrität bei Nutzung relationaler Datenbanken beitragen können. Nach der RI-Regel dürfen Datensätze (über ihre Fremdschlüssel) nur auf existierende Datensätze verweisen.
Danach besteht die Referenzielle Integrität grundsätzlich aus zwei Teilen:
- Ein neuer Datensatz mit einem Fremdschlüssel kann nur dann in einer Tabelle eingefügt werden, wenn in der referenzierten Tabelle ein Datensatz mit entsprechendem Wert im Primärschlüssel oder einem eindeutigen Alternativschlüssel existiert.
- Eine Datensatzlöschung oder Änderung des Schlüssels in einem Primär-Datensatz ist nur möglich, wenn zu diesem Datensatz keine abhängigen Datensätze in Beziehung stehen.
SQL Server benötigt für Referenzielle Integrität nicht zwingend einen PRIMARY KEY; vielmehr sind nur die Eigenschaften eines PRIMARY KEY für SQL Server von Relevanz für die Durchsetzung von Referenzieller Integrität. Die beiden nachfolgenden Skriptbeispiele sollen diesen Zusammenhang verdeutlichen. Für die Definition eines Fremdschlüssels ist zwingend erforderlich, dass das Primärattribut der Masterrelation weder NULL-Werte besitzt noch mehrfach vorkommen darf. Das nachfolgende Script wird fehlschlagen, da SQL Server nicht in der Lage ist, eindeutige Schlüsselwerte in [master_table] sicher zu stellen:
CREATE TABLE dbo.master_table
(
Id int NOT NULL,
col1 char(20) NULL
);
GO
CREATE TABLE dbo.detail_table
(
Master_id int NOT NULL,
detail_id int NOT NULL,
col1 char(20) NOT NULL,
CONSTRAINT fk_master_table_id FOREIGN KEY (master_id)
REFERENCES dbo.master_table(Id)
ON DELETE CASCADE
);
Wird das obige Script ausgeführt, wird der nachfolgende Fehler ausgelöst. Die Fehlerbeschreibung ist selbsterklärend:
Meldung 1776, Ebene 16, Status 0, Zeile 2 In der dbo.master_table-Tabelle, auf die verwiesen wird, befinden sich keine primären oder Kandidatenschlüssel, die mit der verweisenden Spaltenliste im fk_master_table_id-Fremdschlüssel übereinstimmen.
Für eine Fremdschlüsselbeziehung wird ENTWEDER ein PRIMARY KEY oder ein UNIQUE NOT NULL Index benötigt. Statt also eines PRIMARY KEY kann auch eine UNIQUE-Einschränkung verwendet werden. Ändert man das Script zur Erstellung der Relationen wie folgt um, kann die Einschränkung problemlos erstellt werden:
CREATE TABLE dbo.master_table
(
Id int NOT NULL,
col1 char(20) NULL,
CONSTRAINT ux_master_table_id UNIQUE (Id)
);
GO
CREATE TABLE dbo.detail_table
(
Master_id int NOT NULL,
detail_id int NOT NULL,
col1 char(20) NOT NULL,
CONSTRAINT fk_master_table_id FOREIGN KEY (master_id)
REFERENCES dbo.master_table(Id)
ON DELETE CASCADE
);
Das Script läuft fehlerfrei durch und die Fremdschlüssel-Einschränkung wird erstellt, da für das Fremdschlüssel-Attribut [Id] die Einschränkungen “NOT NULL” und “UNIQUE” vorhanden sind. Es bedarf keiner weiteren Einschränkungen mehr. Schaut man sich anschließend die in der Relation [dbo].[master_table] befindlichen Indexe an, stellt man fest, dass die Relation selbst immer noch ein HEAP ist und keinen clustered Index besitzt.
SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID('dbo.master_table', 'U');
Fazit
Die Definition eines PRIMARY KEY für eine Relation hat NICHTS mit der Definition und der Funktionalität eines Clustered Index zu tun. Beide Verfahren sind unabhängig voneinander zu betrachten und verfolgen ganz verschiedene Ansätze. Während ein PRIMARY KEY eine Eigenschaft einer Relation ist, ist der Clustered Index die Relation selbst. Während ein Clustered Index ausschließlich für die physikalische Ordnung der Datensätze verantwortlich ist, dient der PRIMARY KEY der Durchsetzung von klar vordefinierten Vorgaben für das Schlüsselattribut eines Datensatzes (Eindeutigkeit und NOT NULLABLE).
Herzlichen Dank für’s Lesen
| PRIMARY KEY | http://msdn.microsoft.com/de-de/library/ms191236.aspx |
| CLUSTERED INDEX | http://msdn.microsoft.com/de-de/library/ms177443.aspx |
| Referentielle Integrität | http://de.wikipedia.org/wiki/Referentielle_Integrit%C3%A4t |
| sys.key_constraints | http://msdn.microsoft.com/de-de/library/ms174321.aspx |
| sys.indexes | http://msdn.microsoft.com/de-de/library/ms173760.aspx |
Flexible Parameterübergabe als Filterkriterien für dynamisches SQL in Stored Procedures
Apr 25th
Link: http://db-berater.blogspot.de/2013/02/cte-tabellenvariablen-und-temporare.html#more
Während der Besprechung zu einer Projekterweiterung wurde unter anderem ein Problem besprochen, dass sehr häufig anzutreffen ist – Konkatenation eines SQL-String “am Client” und Versand und Ausführung am SQL Server, um die Daten zu ermitteln. Ich habe vorgeschlagen, die komplette Suchroutine in eine Stored Procedure auszulagern. Dieser Stored Procedure werden dann nur noch die Parameter übergeben und die Konkatenation findet dann in der Prozedur statt. Das komplette SQL-Statement wird dann innerhalb der Stored Procedure ausgeführt und die Daten an den Client zurück geliefert. Die Bedenken von SQL-Injection habe ich widerlegt, indem ich argumentiert habe, dass trotz Variabilität in der Parameterübergabe ausschließlich mit sp_executeSQL und expliziter Parameterübergabe gearbeitet wird. Die Herausforderung war nicht ganz einfach aber ich habe eine funktionierende Lösung entwickeln können, die mehrere Vorteile besitzt.
Full story »Indexoptimierung = Reduktion von I/O
Apr 17th
Link: http://db-berater.blogspot.de/2013/04/indexoptimierung-reduktion-von-io.html
Während ich den vorherigen Artikel “Clustered Index vs. Non Clustered Index” geschrieben habe, habe ich ein paar interessante Beobachtungen gemacht, die es wert sind, etwas genauer unter die Lupe genommen zu werden. Vielmals höre ich aus Bemerkungen in Vorträgen oder Unterhaltungen mit Kollegen, wie wenig Beachtung bei der Indexierung der Vergleich des I/O bei der Umsetzung differenzierter Indexstrategien findet. Wenn ich mit einem Auftrag betraut werde, eine Abfrage zu optimieren, führe ich zunächst die Abfrage im Original aus und lege die daraus resultierenden I/O-Werte und den Ausführungsplan als "Baseline” fest. Anschließend beginne ich mit der Optimierung. Der nachfolgende Artikel soll deutlich machen, dass nicht immer nur der Ausführungsplan im Mittelpunkt stehen sollte sondern – und gerade – das I/O der Gratmesser für eine optimierte Indexstrategie ist.
Ausgangssituation
Gegeben ist zunächst ein HEAP ohne weitere Indexe mit der folgenden Struktur:
CREATE TABLE dbo.tbl_Members_Date
(
SId int NOT NULL,
FirstName char(80) NOT NULL,
LastName char(80) NOT NULL,
MemberSince date NOT NULL
)
Diese Tabelle hat insgesamt 40.417 Datensätze mit einer festen Größe von 167 Bytes / Datensatz. Anschließend wurde die folgende Abfrage mit unterschiedlichen Indexstrategien auf diese Relation ausgeführt:
SELECT YEAR(MemberSince) AS MemberYear,
COUNT_BIG(SId) AS Members
FROM dbo.tbl_Members_Date
GROUP BY
YEAR(MemberSince)
ORDER BY
COUNT_BIG(SId) DESC
Ausführung der Abfrage als HEAP
Die Relation besitzt aktuell noch keinen Index (HEAP) und die physikalische Gegebenheit der Relation (kann mittels sys.dm_db_index_physical_stats ermittelt werden) sieht wie folgt aus:
Die obige Abfrage ergab den folgenden Ausführungsplan und I/O:
Worktable-Tabelle. Scananzahl 0, logische Lesevorgänge 0,...
tbl_Members_Date-Tabelle. Scananzahl 1, logische Lesevorgänge 883, ...
Wie aus dem Ausführungsplan deutlich erkennbar ist, handelt es sich um einen “Table Scan”. Betrachtet man die vorherige Auswertung der physikalischen Struktur, kann man deutlich erkennen, dass jede Datenseite des Heaps gelesen werden musste (versteht sich von selbst – es gibt ja auch keine Einschränkungen).
Ausführung der Abfrage mit einem CLUSTERED INDEX auf [MemberSince]
Im nächsten Versuch wird ein Clustered Index erstellt, dessen Key das Attribut [MemberSince] ist. Anschließend wird die Abfrage erneut ausgeführt.
CREATE CLUSTERED INDEX cix_tbl_MemberSince_MemberSince ON dbo.tbl_Members_Date (MemberSince);
Das Ergebnis ist – zunächst – verblüffend. Statt besser zu werden, generiert diese Abfrage ein höheres IO!
Worktable-Tabelle. Scananzahl 0, logische Lesevorgänge 0, ...
tbl_Members_Date-Tabelle. Scananzahl 1, logische Lesevorgänge 903, ...
Es mussten 20 Pages mehr gelesen werden, um die gleiche Auswertung zu erhalten. Der Grund für dieses seltsame Verhalten ist in den unterschiedlichen Strukturen von HEAPS und CLUSTERED INDEXES zu finden. Ein HEAP besitzt eine sehr flache Struktur – nämlich einen Root-Level und anschließend unmittelbar die Leaf-Levels für die Speicherung der Daten. Ein Clustered Index hingegen hat eine deutlich komplexere Struktur, da bei zunehmendem Datenvolumen die Clustered Keys in B-Trees gespeichert werden. Die nachfolgende Abbildung zeigt den Unterschied sehr deutlich.
Hat der Heap – wie oben abgebildet – nur eine Ebene (Leaf-Level) muss der Clustered Index bereits 6 Seiten mehr für die Root-Page und eine Ebene (B-Tree) bereit halten. Die eigentlichen Daten wiederum werden erst im Level 0 (Leaf-Level) gespeichert. Hierbei werden 896 Seiten für diese Speicherung benötigt. Insgesamt sind es also 896 + 5 + 1 = 902 (+1 Page IAM) Seiten. Warum sind im Leaf-Level mehr Pages vorhanden als bei einem HEAP? Es werden doch identische Daten gespeichert!Die Erklärung hierfür ist relativ schnell gefunden, wenn man sich die Datenstrukturen der Leaf-Level einmal etwas genauer anschaut. Die nachfolgenden Abbildungen zeigen eine Page aus dem Leaf-Level eines HEAP und des CLUSTERED INDEX.
Data Page in HEAP
Data Page in CLUSTERED INDEX
Vollkommen transparent fügt SQL Server JEDEM Index, der nicht UNIQUE ist ein internes Attribut [UNIQUIFIER] hinzu, um einen Datensatz eindeutig zu kennzeichnen. Dieser [UNIQUIFIER] hat eine unangenehme Eigenschaft… – er benötigt Platz. Grundsätzlich wird der UNIQUIFIER immer dann mit einem Wert besetzt, wenn das Schlüsselattribut eines Index redundant ist. In einem HEAP werden solche [UNIQUIFIER] nicht verwendet! (Danke an Andreas Wolter, der mich auf diesen Fehler hingewiesen hat).
Bei dem anschließend gesetzten Clustered Index auf das Attribut [MemberSince] zeigt die obige Data Page bereits einen 17. ten redundanten Eintrag. Der UNIQUIFIER beginnt intern immer bei 0. hierbei sein ein wichtiger Hinweis darauf gestattet, dass ein UNIQUIFIER mit dem Wert 0 eine physikalische Länge von 0 hat – es wird kein INT für diesen Wert gespeichert. Hingegen ist jedoch zu erkennen, dass der UNIQUIFIER immer 4 Bytes belegt, wenn der Wert größer 0 ist. Das erklärt den unterschiedlichen Platzbedarf des CLUSTERED INDEX im Verhältnis zum HEAP.
Ausführung der Abfrage mit einem dedizierten Index auf das Attribut [SId]
Der dritte Test verwendet eine Abfragestrategie, bei der ein Clustered Index auf das Attribut [SId] angewendet wird. Da dieses Attribut eindeutig ist, wird ein UNIQUE Index verwendet. Des Weiteren wird ein Non Clustered Index auf das Attribut [MemberSince] gelegt. Durch diese Strategie wird für den Clustered Index die geforderte Eindeutigkeit gewährleistet, die die Speicherung eines UNIQUIFIERS vermeidet. Da jedoch MemberSince für die Abfrage benötigt wird, wird anschließend ein zweiter Non Clustered Index für dieses Attribut verwendet. Weitere Attribute müssen weder diesem Index als Schlüsselattribut hinzugefügt werden noch muss die SId (wird ja ebenfalls im der Abfrage benötigt) separat hinzugefügt werden, da ja der Clustered Key immer in jedem Non Clustered Index enthalten sein muss.
Nachdem die bestehenden Indexe vollständig gelöscht worden sind, können die zwei nachfolgenden Index angelegt werden:
CREATE UNIQUE CLUSTERED INDEX cix_tbl_MemberSince_SId ON dbo.tbl_Members_Date (SId);
CREATE INDEX ix_tbl_MemberSince_MemberSince ON dbo.tbl_Members_Date (MemberSince);
Ein Blick auf die Indexstatistiken zeigt, dass der Clustered Index nicht wesentlich weniger Volumen besitzt als der zuvor auf dem Attribut [MemberSince] verwendete Clustered Index.
Der Unterschied kommt zu Stande, da – wie bereits oben erwähnt – für die Datensätze kein UNIQUEIFIER mehr mit gespeichert werden muss. Ca. 10 Seiten für die obige Struktur mag erahnen, welches Datenvolumen eingespart werden kann, wenn es um wirklich große Datenmengen geht. Keine Behauptung ohne Beweise – ein Blick auf eine Datenseite zeigt den Unterschied deutlich. Man kann auf der Abbildung deutlich erkennen, dass ein UNIQUIFIER auf der Datenseite fehlt; er wird ja auch nicht mehr benötigt, da per Definition für den Clustered Index definiert wurde, dass er eindeutig ist.
Interessant ist aber ein Blick auf den zweiten – für die Abfrage entscheidenden – Index, der ausschließlich das Attribut [MemberSince] abdeckt. Dieser Index ist – im Verhältnis zu den zuvor untersuchten Indexen – sehr klein.
Wow – dieser Index verwendet insgesamt nur 66 Pages. Das ist dem Umstand geschuldet, dass das Attribut des Index (MemberSince) lediglich 3 Bytes belegt. Hinzurechnen muss man noch den Clustered Key (4 Bytes) + Overhead für die Speicherung der Daten (Slot Array, …). Dadurch, dass nun ca. 620 Informationen ([MemberSince], [SId]) auf eine Datenseite passen, können die 40.000 Datensätze bequem auf 65 Indexseiten gespeichert werden. Und genau das macht sich extrem bemerkbar in der Abfrage.
Das I/O ist erwartungsgemäß gemäß der Größe des Index recht klein:
Worktable-Tabelle. Scananzahl 0, logische Lesevorgänge 0, …
tbl_Members_Date-Tabelle. Scananzahl 1, logische Lesevorgänge 67, …
Insgesamt konnte so durch geschickte Indexierung die Performance um über 90% gesteigert werden!
Verwendung einer INDEXED View
Die ganze oben aufgeführten Indexvarianten sind ein sehr guter Ansatz, das I/O signifikant zu verbessern. Die bester aller Varianten – unter der Voraussetzung der obigen Anforderungen – ist jedoch ungeschlagen die Verwendung einer INDEXED View. Mehr zu den Anforderungen an eine Indexed View finden Sie im Artikel “Verwendung von “Indexed views” zur Verbesserung der Performance”. Von daher hier nur kurz die Erstellung, Indexstruktur und Auswertung…
CREATE VIEW dbo.view_Member_Statistics
WITH SCHEMABINDING
AS
SELECT YEAR(MemberSince) AS MemberYear,
COUNT_BIG(*) AS Members
FROM dbo.tbl_Members_Date
GROUP BY
YEAR(MemberSince) GO
CREATE UNIQUE CLUSTERED INDEX cix_view_Member_Statistics ON dbo.view_Member_Statistics (MemberYear);
Die Indexstatistiken sind beeindruckend.
Entsprechend gestaltet sich dann auch Ausführungsplan und I/O-Messung für die Ausführung der View:
view_Member_Statistics-Tabelle. Scananzahl 1, logische Lesevorgänge 2, …
Beeindruckend! Von ursprünglich 903 –> 883 –> 67 –> 2 I/O’s. Man kann sich vorstellen, wie glücklich ein Kunden sein wird, wenn er solche Zahlen präsentiert bekommt.
Fazit
Indexoptimierung muss immer von mehreren Seiten betrachtet werden. Es reicht nicht aus, den Ausführungsplan im Blick zu haben und zu versuchen, z. B. einen Index Scan durch einen Index Seek zu ersetzen oder einen Nested Loop durch einen Hash Join, … Indexoptimierung muss meines Erachtens zunächst das I/O im Auge behalten. Erst, wenn die Schwachstellen beim I/O eliminiert sind, sollte man sich um den Ausführungsplan kümmern. Diese Strategie ist meines Erachtens sogar elementar, da bei Reduktion des I/O Änderungen in der Ausführung einer Abfrage nicht auszuschließen sind.
Herzlichen Dank fürs Lesen
Clustered Index vs. NonClustered Index
Apr 14th
Link: http://db-berater.blogspot.de/2013/02/cte-tabellenvariablen-und-temporare.html#more
Heute habe ich mit einem sehr geschätzten Freund und Kollegen (Bernd Jungbluth) eine interessante Diskussion im Rahmen meines Vortrags zu Indexstrategien auf der SNEK II in Nürnberg geführt. Die Aufgaben-/Fragestellung war recht simpel. Es ging darum, ob ein Clustered Index auf einem Datumsattribut performanter sei als ein Clustered Index auf einem INT-Attribut und einem zusätzlichen Index auf dem besagten Datumsattribut. Allgemeine Nachteile eines Clustered Index auf einem Datumsattribut (Fragmentierung / Größe) sollen als Pro / Contra Argumente hier nicht beleuchtet werden.
Full story »Inside sys.dm_db_index_physical_stats
Mär 29th
Link: http://db-berater.blogspot.de/2013/03/inside-sysdmdbindexphysicalstats.html
Ich habe in dieser Woche einen Fall zu untersuchen gehabt, der ein System für mehrere Stunden (teilweise sogar Tage) komplett lahm gelegt hat. Die Analyse hat gezeigt, dass (unter anderem) regelmäßige Index- und Statistikaktualisierungen durchgeführt wurden. Was ich dann herausgefunden habe, mag man kaum glauben. In diesem konkreten Beispiel wurden wirklich ALLE Fehler gemacht, die man in Verbindung mit sys.dm_db_index_phyical_stats und dessen Anwendungsspektrum überhaupt machen kann.
Full story »














