Modus pro Gruppe ermitteln

Posted on Jan 27, 2006 von in SQL Server

Vor einiger Zeit kam eine interessante Frage in der Newsgroup auf. Gesucht wurde der am häufigsten vorkommende Preis pro Artikelgruppe. Also der Modus je Artikelgruppe. Gegeben ist das folgende Ausgangsszenario:

CREATE TABLE #t (ArtikelGruppe CHAR(2), Preis DECIMAL(8,2))
INSERT INTO #t VALUES('SP', 1.2)
INSERT INTO #t VALUES('SP', 1.2)
INSERT INTO #t VALUES('SP', 2.1)
INSERT INTO #t VALUES('GR', 2.2)
INSERT INTO #t VALUES('GR', 2.3)
INSERT INTO #t VALUES('GR', 2.3)
INSERT INTO #t VALUES('GR', 2.3)

Gemäß der Aufgabenstellung soll nun

ArtikelGruppe Preis      Anzahl      
------------- ---------- -----------
SP 1.20 2
GR 2.30 3

(2 row(s) affected)

als Ergebnis erscheinen. Doch diese an und für sich scheinbar einfache Frage kann sich jedoch zu einem mittelschweren Brainteaser entwickeln. Den am häufigsten vorkommenden Preis über sämtliche Artikel kann man noch relativ leicht und intuitiv bestimmen mittels:

SELECT TOP 1 WITH TIES ArtikelGruppe, Preis, COUNT(*) Anzahl
FROM #t
GROUP BY ArtikelGruppe, Preis
ORDER BY Anzahl DESC

ArtikelGruppe Preis Anzahl
------------- ---------- -----------
GR 2.30 3

(1 row(s) affected)

Doch wie kommt nun die Gruppierung nach Artikelgruppen hier ins Spiel? Christoph Muthmann kam zu folgendem Lösungsansatz:

--Christoph's Ansatz
SELECT ArtikelGruppe, Preis, COUNT(*) AS Anzahl
FROM #t a
GROUP BY artikelgruppe, preis
HAVING COUNT(*) >=
(SELECT MAX(Anzahl)
FROM
(SELECT artikelgruppe, preis, COUNT(*) AS Anzahl
FROM #t b
WHERE b.artikelgruppe = a.artikelgruppe
GROUP BY artikelgruppe, preis) c)

ArtikelGruppe Preis Anzahl
------------- ---------- -----------
SP 1.20 2
GR 2.30 3

(2 row(s) affected)

während mein ursprünglicher Ansatz folgendermaßen aussah:

--Mein ursprünglicher Ansatz
SELECT ArtikelGruppe,Preis, COUNT(*) Anzahl
FROM #t a
GROUP BY ArtikelGruppe,Preis
HAVING NOT EXISTS
(SELECT *
FROM #t b
WHERE b.ArtikelGruppe = a.ArtikelGruppe
GROUP BY b.Preis
HAVING COUNT(b.ArtikelGruppe) > COUNT(a.ArtikelGruppe))

ArtikelGruppe Preis Anzahl
------------- ---------- -----------
SP 1.20 2
GR 2.30 3

(2 row(s) affected)

Vergleicht man nun die Ausführungspläne wird man bei meinem Ansatz einen "Lazy Spool" entdecken. Für gewöhnlich ist dies ein schlechtes Omen und man sollte versuchen, die Abfrage so umzuformulieren, daß dieser logische Operator nicht mehr auftaucht. Da ich aber Christoph's Ansatz sowieso für eleganter halte, formuliere ich lieber diesen ein bißchen um, um einen deutlich schlankeren und damit wahrscheinlich effektiveren Ausführungsplan zu erhalten:

SELECT ArtikelGruppe, Preis, COUNT(*) AS Anzahl
FROM #t t
GROUP BY ArtikelGruppe, Preis
HAVING COUNT(*) =
(SELECT MAX(Anzahl)
FROM
(SELECT ArtikelGruppe, Preis, COUNT(*) AS Anzahl
FROM #t
GROUP BY ArtikelGruppe, Preis) x
WHERE ArtikelGruppe = t.ArtikelGruppe)

ArtikelGruppe Preis Anzahl
------------- ---------- -----------
SP 1.20 2
GR 2.30 3

(2 row(s) affected)

Christoph hat dann die drei obigen Statements mal an einem repräsentativen Datenbestand ausprobiert. Die Tabelle hat 250.000 Zeilen und umgesetzt auf das Beispiel 8 Artikelgruppen mit 358 verschiedenen Preisen. Ohne Verwendung von Indexes kommen dabei folgende Laufzeiten heraus:

  • Christoph's Ansatz: 5.390 Millisekunden
  • Mein Ansatz: 19.033 Millisekunden
  • Neuer Ansatz: 436 Millisekunden

Erstellt man jetzt einen Index auf (ArtikelGruppe, Preis) kommt es zu folgenden Laufzeiten:

  • Christoph's Ansatz: 153 Millisekunden
  • Mein Ansatz: 500 Millisekunden
  • Neuer Ansatz: 110 Millisekunden

Beeindruckend kann man erkennen, wie stark manche Abfragen durch geeignete Indexes beschleunigt werden können.

Der Vollständigkeithalber kann man alternativ zu MAX() zwar auch diverse TOP Varianten einsetzen:

SELECT artikelgruppe, preis, COUNT(*) AS Anzahl
FROM #t a
GROUP BY artikelgruppe, preis
HAVING COUNT(*) =
(SELECT TOP 1 COUNT(*)
FROM #t
WHERE a.ArtikelGruppe = ArtikelGruppe
GROUP BY ArtikelGruppe, Preis
ORDER BY COUNT(*) DESC)

SELECT ArtikelGruppe, Preis, COUNT(*) AS Anzahl
FROM #t t
GROUP BY ArtikelGruppe, Preis
HAVING COUNT(*) =
(SELECT TOP 1 Anzahl
FROM
(SELECT TOP 100 PERCENT ArtikelGruppe, Preis, COUNT(*) AS Anzahl
FROM #t
GROUP BY ArtikelGruppe, Preis
ORDER BY Anzahl DESC) x
WHERE ArtikelGruppe = t.ArtikelGruppe)

die ebenfalls das gewünschte Ergebnis bringen, aber auch keine entscheidende Verbesserung im Vergleich zu den beiden in der Newsgroup geposteten Statements bzgl. der Ausführung bringen.

Yield to maturity

Posted on Jan 25, 2006 von in SQL Server

Wie in einem der letzten Beiträge dieser Kategorie angekündigt, möchte ich hier auf der Website meine Implementierung der Berechnung der Yield to maturity vorstellen. Andere Begriffe für Yield to maturity sind auch arithmetische Rendite oder Effektivzins. Sämtliche Begriffe sind durchaus gebräuchlich und werden verwendet, um ein und diesselbe Sache zu beschreiben. Und so werden auch in diesem Beitrag diese Begriffe synonym verwendet.
Einige Prämissen gleich vorweg: Wir betrachten hier in diesem Beitrag nur den einfachsten Fall der Effektivzinsberechnung und beschränken uns auf die Diskussion eines festverzinslichen Wertpapieres mit jährlicher Zinszahlung ohne eingebettete Derivate mit einem Rückzahlungskurs von 100%. Aus Gründen der Vereinfachung liegen die erwarteten Cash Flows bereits vor. In der Praxis würde man diese wahrscheinlich erst zur Laufzeit generieren. Auch beschränken wir uns auf die am deutschen Kapitalmarkt mittlerweile eher unübliche 30/360 Zinsmethode. Die Erweiterung für die diversen Tageskonventionen überlasse ich dem geneigten Leser, dem ich mit dem hier vorgestellten Skript schon genug Basisarbeit abgenommen habe. ;-)

Das fundamentale Prinzip jeglicher Bewertung ist, daß der Wert einer Kapitalanlage dem Present Value (oder Barwert) der erwarteten Zahlungsströme entspricht. Dabei spielt es keinerlei Rolle, welcher Art diese Kapitalanlage ist. Von daher setzt sich jeder Valuierungsprozeß aus folgenden 3 Schritten zusammen:

  1. Bestimmung der (erwarteten) Cash Flows.
  2. Festlegung eines angemessenenen Diskontsatzes (bzw. angemessener Diskontsätze).
  3. Berechnung des Present Values der Zahlungsströme aus 1. anhand der Werte aus 2.

So, zunächst einmal hört sich das nicht weiter schwer an. Ist es eigentlich auch nicht. Aber man muß sich verdeutlichen, daß dies ein iterativer Prozeß ist, der nur bedingt mit dem mengenbasierten Ansatz relationaler Datenbanken zu vereinbaren ist. Man berechnet die Rendite jedes einzelnen Wertpapieres indem man solange eine Schleife durchläuft, bis das Ergebnis hinreichend genau ist. Jawohl, hinreichend genau. Das Ganze ist ein Trial-and-Error Prinzip.

Genug der Worte. Ein Beispiel: Gegeben sei ein Wertpapier mit einem jährlichen Zinskupon von 5,00% und einer Endfälligkeit in genau 4 Jahren. Der Preis für das Papier beträgt 95,92. Frage: Welche Yield-to-maturity hat dieses Papier? Antwort: ca.6,1824% Lösung: Gemäß unserem obigen Schema, benötigen wir zuerst die Cash Flows.

CREATE TABLE Cash_Flows 
(
cashflow DECIMAL(8,2),
valuta DATETIME
)
INSERT INTO Cash_Flows VALUES (-95.92,'20060115')
INSERT INTO Cash_Flows VALUES (5,'20070115')
INSERT INTO Cash_Flows VALUES (5,'20080115')
INSERT INTO Cash_Flows VALUES (5,'20090115')
INSERT INTO Cash_Flows VALUES (105,'20100115')

So, am Anfang steht ein negativer Cash Flow (d.h. eine Auslage), da wir diesen Preis bezahlen müssen, um das Papier zu erwerben. Danach erhalten wir 4 Jahre lang einmal pro Jahr die Kuponzahlung iHv 5 und zusätzlich im letzten Jahr den Nominalbetrag (= 5 + 100 = 105). Den eigentlichen Code wird man zweckmäßigerweise in eine UDF packen. Diese könnte folgendermaßen aussehen:

CREATE FUNCTION yield_to_maturity(@issue_date SMALLDATETIME)
RETURNS DECIMAL(15,14)
AS
BEGIN
DECLARE @ytm_tmp FLOAT
DECLARE @ytm FLOAT
DECLARE @pv_tmp FLOAT
DECLARE @pv FLOAT

SET @ytm_tmp = 0
SET @ytm = 0.1
SELECT @pv_tmp = SUM(cashflow) FROM Cash_Flows
SET @pv =
(SELECT SUM(cashflow/POWER(1.0+@ytm,(DATEDIFF(month,@issue_date, valuta)* 30 + DAY(valuta)-DAY(@issue_date)
-
CASE
WHEN(@issue_date)>=30 AND DAY(valuta) = 31
THEN 1
ELSE 0
END)/ 360.0 ))
FROM Cash_Flows)

WHILE ABS(@pv) >= 0.000001
BEGIN
DECLARE @t FLOAT
SET @t = @ytm_tmp
SET @ytm_tmp = @ytm
SET @ytm = @ytm + (@t-@ytm)*@pv/(@pv-@pv_tmp)
SET @pv_tmp = @pv
SET @pv =
(SELECT SUM(cashflow/POWER(1.0+@ytm,(DATEDIFF(month,@issue_date, valuta) * 30 + DAY(valuta)-DAY(@issue_date)
-
CASE
WHEN(@issue_date)>=30 AND DAY(valuta) = 31
THEN 1
ELSE 0
END) /360.0))
FROM Cash_Flows)
END
RETURN @ytm
END
GO

Da das Ganze ein Näherungsverfahren ist, hat man einen gewissen Spielraum bei der Wahl der oberen Grenze bei der Initialisierung der Variablen. 0,1 (=10%) ist idR ein guter Startwert. Danach läuft man solange durch die WHILE Schleife, bis die Abbruchbedingung erfüllt ist. Und hier erkennt man dann auch sehr schön den iterativen Ansatz dieses Verfahrens, für das dann andere Programmiersprachen wie C++ oder VB wesentlich besser geeignet sind. Aufgerufen wird die UDF dann mit

SELECT dbo.yield_to_maturity('20060115')

-----------------
.06182374295818

Rechnet man unser Beispiel mit einer spezialisierten Software nach wird man unter Umständen ab irgendwelchen Nachkommastellen Abweichungen bemerken. In der Regel betrifft dies die 6.te bis 8.te Nachkommastelle. Aber die fallen dann auch nicht mehr wirklich dramatisch ins Gewicht. Differenzen ab der 6.ten Nachkommastelle ignoriere ich einfach. Obwohl ich nicht wirklich weiß, woran dies liegt. Ich schiebe dies dann gerne auf die Implementierung des FLOAT Datentypen in SQL Server.

< rant mode on >
So, zum Schluß ein Wort in eigener Sache: Direkt in den Tagen nachdem ich in diesem Beitrag angekündigt hatte, eine T-SQL basierte Yield-to-maturity Formel zu veröffentlichen, habe ich gleich mehrere Mails von Leuten erhalten, die entweder genau wissen wollten, wann es denn soweit sei oder ob diese oder jene Besonderheit eingebaut sei und ob ich auch beabsichtige, noch weitere Renditemaße zu veröffentlichen. Ich habe zwar geahnt, daß diese Formel für viele vielleicht interessant sein könnte, war aber vollkommen von der teilweisen Dreistigkeit dieser Mails überrascht.
Ich möchte deshalb an dieser Stelle eines ganz klar stellen:

  • Ich habe lange gezögert, ob ich nun diesen Beitrag veröffentlichen soll oder nicht, aber nun mache ich meine damals publizierte Ankündigung wahr. Nicht mehr, aber auch nicht weniger. Versprochen ist versprochen ... usw...
  • Ich habe meinen Originalalgorithmus aber nach diesen Mails absichtlich noch weiter verschlankt, damit diejenigen Leute, die es offensichtlich selber nicht fertigbringen, diese Formel abzubilden, wenigstens noch einiges an eigenem Gehirnschmalz investieren müssen, um die Funktion tatsächlich praxistauglich zu machen.
  • Ich werde keine weiteren Renditemaße veröffentlichen (was ich bis zu diesen Mails tatsächlich vorhatte).

Ich sehe nicht ein, daß ich hier über das hinaus, was ich eh schon mache, kostenlos Wissen vermittle, womit andere Leute potentiell eine Menge Geld verdienen können. Ich habe kein Problem damit, hier Algorithmen zu veröffentlichen, die Anderen Zeit, Nerven und Geld sparen. Ganz im Gegenteil! Aber diese Mails gaben mir das Gefühl, nur ein Ideenlieferant zu sein, den man ausnutzen kann. Aus diesem Grund schiebe ich dem einen Riegel vor, und behalte mein Wissen in diesem Bereich ab sofort für mich.

Sorry, aber mal wieder haben einige schwarze Schafe der Gesamtheit die Suppe versalzen!
< rant mode off >

Cryptography in the Database

Posted on Jan 19, 2006 von in Vermischtes

Hin und wieder stößt man in Online Communities auf Fragen wie "Kann ich Daten vor dem Datenbankadministrator verbergen?" oder "Wie kann ich verhindern, daß Systemadministratoren meine Daten lesen?" Und oft ist die Antwort darauf: "Einen Datenbankadministrator kann man nicht daran hindern, die Daten einer Datenbank einzusehen. Warum auch? Schließlich braucht der Administrator vollen Zugriff im Falle eines Falles. Und wenn man schon seinen Administratoren nicht mehr trauen kann, hat man wahrscheinlich noch ganz andere Probleme." oder "Wenn schon, dann sollten die Daten im Front-End verschlüsselt werden und danach in der Datenbank gespeichert werden. Datenbankverschlüsselung allein ist schwach."

Datensicherheit ist teilweise ein sträflich vernachlässigtes Thema. Zumindest was die Sicherheit anbetrifft, die nach der Firewall und Intrusion Detection Systemen kommt und sich unternehmensintern abspielt. Medienwirksamer sind zweifelsohne die spektakulären Nachrichten darüber, daß irgendjemand in irgendein Netzwerk eingedrungen ist und dort Schaden angerichtet hat. Nachrichten über "interne Hacker" gerade naturgemäß selten nach außen und an die Medien.
Der interne "Angreifer" hat den Vorteil, daß er sich bereits hinter dem äußeren Wall befindet, Zugriff auf das Netzwerk hat und auch mehr oder weniger ausgeprägte Kenntnisse über die Netzwerktopologie besitzt. Gerade als Datenbankadministrator ist man hier in einer sehr priviligierten Position. Nicht nur hat man bereits den Zugriff auf sämtliche in der Datenbank gespeicherten Daten, sondern als Admin ist man auch in der Lage, die Spuren einer solchen eventuellen Attacke zu verwischen. Kryptographie kann helfen, dieses latente Sicherheitsrisiko zu minimieren. Doch Datenbank-Kryptographie alleine ist nicht genug. Um ein Höchstmaß an Sicherheit zu erzielen muß man eine komplette kryptographische Infrastruktur errichten.

Damit ist im Groben der Inhalt dieses Buches wiedergegeben. Auf ca. 270 Seiten entwickelt der Autor solch eine kryptographische Infrastruktur bzw. gibt genügend Ideen, um eine solche in seiner eigenen IT-Landschaft zu erstellen oder Produkte von Drittanbietern auf ihre Zweckmäßigkeit einschätzen zu können.

Der Autor leitet Symantec's IT Applikations- und Datenbanksicherheits-Programm und sorgt in dieser Position dafür, daß die internen Systeme von Symantec sicher sind. Die Beispiele werden anhand von MySQL als Datenbanksystem und Java als Programmiersprache präsentiert, aber diese sollten ohne größere Schwierigkeiten auch auf andere Datenbanksysteme und Sprachen übertragbar sein. Der komplette Source Code seines Kryptographie Projektes ist downloadbar von der Webseite des Verlages.

Lesenswert ist dieses Buch vor allem für Leute, die an Projekten oder in Umgebungen arbeiten, für die Sicherheit die oberste Priorität ist. Seien es nun Systemarchitekten, Systemanalysten, Entwickler oder auch Risikomanager, die jedoch zumindest über Grundkenntnisse in der Kryptographie verfügen sollten.

Cryptography in the Database
Kevin Kenan
2005 Addison-Wesley
ISBN 0-321-32073-5

Pro SQL Server 2005

Posted on Dez 29, 2005 von in Vermischtes

Was erwartet man von einem Buch, daß vom einem Direktor des SQL Server Produkt Teams zusammen mit 2 SQL Server MVP's (Most Valuable Professionals) und anderen Datenbank Veteranen geschrieben wurde?

Ganze Geschichte »

JOIN Hints

Posted on Dez 26, 2005 von in SQL Server

JOIN Hints können in einer Abfrage verwendet werden, um den JOIN Typ festzulegen, den der Query Optimiser für den Ausführungsplan verwenden soll: Folgende JOIN Optionen stehen zur Verfügung:
- Loop
- Merge
- Hash

Die Syntax für einen JOIN Hint ist (am Beispiel eines INNER JOINs):

FROM tabelle_1 INNER (LOOP | MERGE | HASH) JOIN tabelle_2

Hier ist ein Beispiel:

FORM header_tabelle INNER LOOP JOIN detail_tabelle

Wie man sehen kann, wird der JOIN Hint zwischen der Angabe des JOIN Typs (hier der INNER) und dem JOIN Schlüsselwort. Nur ein JOIN Hint kann zu einer Zeit pro JOIN in einer Abfrage verwendet werden. Ferner können JOIN Hints nur verwenden werden zusammen mit der ANSI JOIN Syntax, nicht mit der älteren Microsoft JOIN Syntax.

Die obige Syntax ist nicht die einzige Möglichkeit, um einen JOIN Hint in einer Abfrage zu verwenden. Man kann auch die OPTION Klausel verwenden. Diese bewirkt, daß der Hint während der gesamten Abfrage verwendet wird. Es können mehrere Hints in der OPTION Klausel auftauchen, jeder Hint jedoch nur genau 1x. Es gibt nur eine OPTION Klausel pro Abfrage.

Hier ist ein Beispiel der Verwendung der OPTION Klausel:
OPTION (INNER) oder OPTION (MERGE) oder OPTION (HASH)

Welche Methode SQL Server verwendet, um Tabellen zu joinen, hängt von zahlreichen Einzelfaktoren ab. In der Mehrzahl der Fälle wird man aber einen Nested Loop Join beobachten. Bevor man nun aber hingeht, und versucht, den JOIN durch Einsatz eines Hints zu beeinflußen, sollte man immer erst die zugrundeliegende Abfrage und die Indexes auf den zugrundeliegenden Tabellen betrachten und versuchen, diese zu optimieren, bevor man SQL Server durch den Hint in seinem Spielraum einengt und die Verarbeitungsmethode starr vorgibt. Unter Umständen kann man durch den Einsatz des Hints die Performance in den Keller ziehen, wenn z.B. unter geänderten Rahmenbedingungen der Hint nicht mehr angebracht ist; SQL Server jedoch gezwungen ist, diesen zu beachten. Von daher sollte man stets vorher unter realistischen Bedingungen testen, ob der Hint das bringt, was man erwartet.

*****

Murachs ASP.NET 2.0 Upgraders Guide VB Edition

Posted on Dez 26, 2005 von in Vermischtes

Vielleicht haben Sie unsere Rezension von Murach's ASP.NET 2.0 Upgrader's Guide C# Edition gelesen und interessant gefunden. Da Sie aber VB Entwickler sind, haben Sie sich gefragt, ob es etwas ähnlich für Ihre Programmiersprache gibt. Nun, diese Suche hat ein Ende. Hier ist es.

Ganze Geschichte »

Trigger

Posted on Dez 19, 2005 von in SQL Server

Tips und Tricks, die die Performance von Trigger erhöhen können:

Die Zeit, die ein Trigger zur Ausführung benötigt, ist eine Funktion der Anzahl der innerhalb des Triggers referenzierten Tabellen und der Anzahl der vom Trigger Code betroffenen Zeilen. Daher sollte man stets bestrebt sein, die Anzahl der im Trigger involvierten Tabellen und die Anzahl der betroffenen Zeilen zu minimieren.

Zusätzlich sollte der Code eines Triggers auf ein Minimum reduziert werden, um Overhead zu vermeiden. Dies ist wichtig, da Trigger typischerweise durch INSERT's, UPDATE's und DELETE's ausgelöst werden, die in OLTP Applikation häufig vorkommen. Je mehr Code in einem Trigger ausgeführt werden muß, umso langsamer ist jedes INSERT, UPDATE und DELETE, das stattfindet.

*****

Falls der Trigger eine WHERE Klausel beinhaltet, sollte man nicht vergessen, einen geeigneten Index zu erstellen, der verwendet werden kann. WHERE Klauseln, die sich in Triggern verstecken, werden leicht übersehen, aber wie jedes andere SELECT Statement können auch sie signifikant durch einen geeigneten Index beschleunigt werden.
Ein Weg um dies zu testen ist, den Trigger Code im Query Analyzer laufen zu lassen und den daraus resultierenden Ausführungsplan zu untersuchen. Dadurch kann man sehr schnell feststellen, ob man einen geeigneten Index hinzufügen muß oder nicht.

*****

Hat man ein INSERT, UPDATE oder DELETE Statement daß länger zu benötigen scheint als angemessen, sollte man überprüfen, ob ein Trigger auf der beteiligten Tabelle definiert ist. Das Performance Problem, das man beobachtet, könnte sehr wohl auf den Trigger zurückzuführen zu sein, nicht auf das DML Statement selber.
Man sollte nicht vergessen, Trigger Code genauso zu optimieren wie man es mit jedem anderen Code auch macht. Aufgrund der Tatsache, daß Trigger Code im Hintergrund läuft, scheinen viele Leute zu vergessen, daß der Code existiert, und potentiell für Performance Probleme verantwortlich sein kann.
Man kann Profiler und Query Analyzer verwenden, um herauszufinden, wie die Trigger in einer Datenbank arbeiten.

*****

Verwenden Sie keinen Trigger, um referenzielle Integrität sicherzustellen, wenn Sie die Option haben, auf die in SQL Server eingebauten Funktionen zur Sicherstellung referenzieller Integrität zurückzugreifen. Die Verwendung der hoch optimierten eingebauten referenziellen Integrität ist deutlich schneller als die Verwendung eines Triggers und interpretiertem Code, um die gleiche Aufgabe zu erfüllen.

*****

Wenn Sie die Wahl haben zwischen einem Trigger und einer CHECK Constraint, um Regeln oder Standards in der Datenbank sicherzustellen, sollte man sich generell für die CHECK Constraint entscheiden, da sie schneller sind als Trigger, wenn sie die gleiche Aufgabe erfüllen sollen

*****

Man sollte es vermeiden, einen Trigger zurückrollen zu müssen aufgrund des verursachten Overheads. Anstelle den Trigger ein Problem finden zu lassen und evtl. eine Transaktion zurückrollen zu müssen, sollte man den Fehler vorher versuchen abzufangen, falls das aufgrund des involvierten Codes möglich ist. Einen Fehler abzufangen (bevor der Trigger ausgelöst wird), verbraucht wesentlich weniger Server Resourcen als den Trigger zurückrollen zu müssen.

*****

Manchmal erscheint es aus Performancegrunden angebracht, denormalisierten Daten zu halten. Beispielsweise mag man aggregierte ( so z.B. kumulierte Daten) in einer Tabelle halten, da es einfach zu zeitaufwendig ist, sie on-the-fly innerhalb des SELECT Statements zu generieren. Ein Weg, diese denormalisierten Daten zu pflegen, ist die Verwendung von Triggern. Beispielsweise könnte ein Trigger immer dann ausgelöst werden, wenn ein neuer Verkauf zur Verkaufstabelle hinzugefügt wird und den Gesamtwert dieses Verkaufs zu einer Gesamtverkaufstabelle hinzufügen.

*****

Der Code, der in einem UPDATE Trigger enthalten ist, wird jedesmal ausgeführt, wenn seine zugrundeliegende Tabelle upgedatet wird. In den meisten UPDATE Triggern, betrifft der Code des Triggers nur einige wenige Spalten, nicht alle. Darum ist es nicht sinnvoll (und eine Verschwendung von SQL Server Resourcen) den gesamte Code des Triggers auszuführen, auch wenn die Spalten, an denen man ursprünglich interessiert war, überhaupt nicht aktualisiert worden sind. Mit anderen Worten, auch wenn eine Spalte aktualisiert wird, an der man nicht interessiert ist, wird der UPDATE Trigger ausgelöst und der Code ausgeführt.
Um nun die unnötige Ausführung von Code in einem UPDATE Trigger zu vermeiden, kann man eine der beiden folgenden Funktionen einsetzen: UPDATE() (verfügbar in SQL Server 2000) und COLUMNS_UPDATED() (verfügbar in SQL Server 7.0 und 2000).
Jede dieser beiden Funktionen kann verwenden werden, um zu testen, ob eine bestimmte Spalte sich verändert hat oder nicht. Daher kann man nun Code in seinem Trigger schreiben, der genau dies überprüft und falls sich diese Spalte nicht verändert hat, der Code halt nicht ausgeführt wird. Dies reduziert die Arbeit, die der Trigger ausführen muß und verbessert die allgemeine Performance der Datenbank.
Die UPDATE() Funktion wird verwendet, um die Veränderung einer einzelnen Spalte zu einer Zeit zu überprüfen. Die COLUMNS_UPDATED Funktion kann verwendet werden, um gleichzeitig mehrere Spalten zu überprüfen.

*****

Falls Sie kaskadierende referentielle Integrität (zum Beispiel kaskadierende DELETEs) in SQL Server 2000 Datenbanken, verwenden Sie kaskadierende referentielle Integritäts Constraints anstelle von Triggern, die die kaskadierenden DELETEs ausführen, da Constraints wesentlich effizienter sind und dadurch die Performance verbessern können. Falls man ältere (7.0 oder früher) Applikationen hat, die man auf SQL Server 2000 portiert hat und die Trigger benutzen, um kaskadierende DELETEs, sollte man in Erwägung ziehen, die Trigger, falls möglich zu entfernen und kaskadierende referentielle Integrität zu verwenden.

*****

Während INSTEAD OF trigger technisch identisch mit AFTER Triggern sind, liegt der Hauptgrund für die Verwendung von INSEAD OF Trigger darin, daß man mit ihnen bestimmte View Typen aktualisieren kann. Was bedeutet dies nun im Hinblick auf Performance? Unter der Annahme, daß die meisten, der von Ihnen geschriebenen Trigger, nur selten Transaktionen zurückrollen, kann man getrost weiterhin AFTER Trigger verwenden. Der Overhead eines INSTEAD OF Triggers ist größer als der eines AFTER Trigers. Ist es hingegen an der Regel, daß ROLLBACKs durchgeführt werden (mehr als in 50% aller Fälle), dann kann ein INSTEAD OF Trigger die bessere Alternative sein, da das ROLLBACK eines INSTEAD OF Triggers weniger Overhead verursacht als das eines AFTER Triggers. Also sollte man die meiste Zeit konventionelle AFTER Trigger verwenden, und sich die INSTEAD OF Trigger sparen, um damit Views upzudaten.

*****

SQL Server 2000 ermöglicht in gewissem Maße, Kontrolle über die Reihenfolge in der Trigger auszuüben. In gewissem Maße heißt, daß man angeben kann, welcher Trigger als Erster und welcher als Letzter ausgeführt wird. hat man jedoch mehr als zwei Trigger für eine Tabelle definiert, hat man keine Kontrolle über die Reihenfolge, in der die restlichen ausgeführt wird.
So, wie nun kann die Selektion der Reihenfolge, in der die Trigger ausgeführt werden, dabei helfen, die Performance einer Applikation zu verbessern? Um Trigger Performance zu optimieren, sollte man denjenigen Trigger, bei dem ein ROLLBACK am wahrscheinlichsten (aus welchem Grund auch immer), als ersten auszuführenden Trigger spezifizieren. Auf diese Weise ist nur dieser eine Trigger betroffen, wenn ein ROLLBACK durchgeführt werden muß.
Mal angenommen, man hat drei Trigger auf einer Tabelle definiert, aber anstelle dem am wahrscheinlichsten als ersten zu definieren, sei dieser als letzter definiert. Wird in diesem Szenario nun ein ROLLBACK ausgelöst, dann müssen alle drei Trigger. Wäre dieser Trigger aber als Erster definiert worden, hätte nur dieser Trigger zurückgerollt werden müssen. Verringert man die Anzahl der Trigger, die zurückgerollt werden müssen, reduziert man SQL Server's Overhead und verbessert die Performance.

Data Strategy

Posted on Dez 15, 2005 von in Vermischtes

Man stelle sich man die folgende hypothetische Situation vor: Eine Firma existiert bereits seit Jahren und ist stetig über diesen Zeitraum gewachsen. Am Anfang war vielleicht ein UNIX System, ein Großrechner oder eine AS/400 auf der die ersten unternehmensweit vitalen Daten lagen. Über die Jahre hinweg tauchten andere Plattformen und Betriebssysteme wie Windows und/oder Linux, auf. Diese liefen dann auf den Rechnern der Anwender in den verschiedenen Abteilungen der Firma. Die Anwender in diesen Abteilungen adaptierten schnell die neuen Systeme und entwickelten ihre eigenen Lösungen für ihre jeweiligen Bedürfnisse. Die Anzahl der Schnittstellen zwischen den diversen System wuchs beständig und die Daten wurden redundant in zahlreichen Systemen in teilweise verschiedenen Aggregationsstufen gehalten. Da die Firma rasch wuchs, war nie genug Zeit vorhanden, die vorhandenen Systeme systematisch zu erfassen und zu dokumentieren. Niemand störte sich großartig an diesem Zustand. Falls die IT Abteilung überhaupt von diesen "Stealth" Systemen wußte, da nahezu in jeder Abteilung ein "Power User" in der Lage ist, eine Tabellenkalkulations- oder eine Desktop-Datenbank Lösung zu erstellen. So lange alles ohne Probleme läuft, stört sich niemand an diesem Chaos in der IT Infrastruktur. Aber wehe, wenn etwas mit einem Fehler abbricht. Im Zweifel haben die ursprünglichen Entwickler die Firma bereits vor Jahren verlassen und aufgrund der unzureichenden Dokumentation weiß niemand so ganz genau, was denn der Code eigentlich macht. Daher kommt es öfters mal vor, daß die Beseitigung eines Fehlers an ganz anderer Stelle nun zu einem neuen Fehler führt, da auch die Abhängigkeiten zwischen den einzelnen Systemen unbekannt sind. Die IT Abteilung ist kaum noch in der Lage, das Chaos unter Kontrolle zu halten und anstatt sich auf ihre eigentliche Aufgabe, der Optimierung von IT Prozessen, zu konzentrieren, spielt die Abteilung nur noch Feuerwehr und löscht einen Brand nach dem anderen. Wie...??? Das klingt nicht allzu hypothetisch? Das klingt vielleicht sogar bekannt?

Nun, ich glaube, daß es in der überwiegenden Mehrzahl aller Firmen mehr oder weniger so aussieht. Und genau hier setzt auch das vorliegende Buch an. Es befaßt sich nicht mit Datenbanken in dem Sinne, wie man durch Administration oder Entwicklung eines Datenbanksystems, eine Datenstrategie implementiert. Dieses Buch abstrahiert wesentlich stärker und betrachtet das Thema eher aus der Vogelperspektive. Es beschreibt die Vorteile, die eine homogene IT Infrastruktur bietet und die Vorteile, die die gesamte Firme hat, wenn man sich auf einige wenige Kerntechnologien beschränkt, um diese homogene Infrastruktur zu schaffen. Von daher sollte man auch keine spektakulären neuen Erkenntnisse erwarten. Es werden keine wirklich innovativen, tiefgreifenden Einblicke geboten, die man nicht sowieso haben sollte, sofern man als IT Mensch mit offenen Augen durch die Welt geht. Zugegebenermassen hilft es aber doch von Zeit zu Zeit immer mal wieder, darüber zu lesen.

Dieses Buch wird seine Leser polarisieren. Natürlich ist es aus der IT Perspektive heraus wünschenswert, sich auf einige wenige Kernsysteme konzentrieren zu können, um dadurch besseren allgemeinen Service leisten zu können. Dies aber in der Wirklichkeit zu erreichen ist gleichwohl äußerst schwer. Die Gründe dafür sind vielfältig. Sie beginnen mit der Nichtverfügbarkeit von IT System auf bestimmten Plattformen, bzw. falls sie verfügbar sind, bieten sie nicht die geforderte Funktionalität. Weiter geht's vielleicht damit, daß das Management nicht bereit ist, viel Geld für ein System auszugeben, welches die Kernsysteme unterstützt, wenn es auch andere Systeme gibt, die wesentlich weniger kosten, evtl. im Falle von Open Source, vielleicht sogar kostenlos sind, aber leider nur andere Technologien unterstützen. Darüberhinaus mag man vielleicht auch argumentieren, daß die Migration von System A auf System B eine nicht zu unterschätzende Aufgabe darstellt, die sehr zeit- und kostenintensiv ist und eine sorgfältige Vorbereitung erfordert. Zu guter Letzt gibt es auch gute Gründe für das Argument, daß eine Firma sich nicht 100% von einem einzigen Betriebssystem oder einem einzigen Datenbanksystem abhängig machen will und deshalb absichtlich mehrgleisig fährt. Es gibt noch viele weitere Gründe, die wahrscheinlich schon jeder mal gehört hat. Deshalb spare ich sie mir hier an dieser Stelle.

Es fällt mir nicht leicht, eine Empfehlung zu diesem Buch zu unterbreiten. Es ist sicherlich erstklassig geschrieben, gut lesbar und man merkt, daß die Autoren anerkannte Fachleute auf ihrem Gebiet sind. Der wahrscheinlich in den meisten Firmen vorherrschende Status Quo wird klar analysiert, aber die Schlußfolgerung, sich auf einige wenige Kernsysteme zu konzentrieren, ist nicht so einfach, wie in dem Buch beschrieben. Während es in der Theorie natürlich logisch und einleuchtend ist, ist die reale Welt myriadenfach komplexer in all ihren Facetten und so eine Entscheidung trifft man nicht so einfach, da sie eine Firma in ihrem Kern trifft.

Falls Sie IT Manager sind, der noch Argumente für seinen Kampf mit der Geschäftsführung sucht, werden Sie dieses Buch interessant und hilfreich finden. Falls Sie DBA oder Entwickler sind, machen Sie zwar nichts falsch, wenn Sie mal hin und wieder ein solches Buch lesen. Ich halte es aber nicht für eine notwendige Literatur, die Sie zwingend für Ihren Job brauchen. Und deshalb sollten Sie, wie eigentlich immer, sich dieses Buch vor einem Kauf genau ansehen.

Data Strategy
von Adelman, Moss und Abai
2005 Addison-Wesley
ISBN: 0-321-24099-5