Glorf.it

Glorf IT

Bedenkliches aus dem IT-Alltag

3. November 2012 um 17:19

Lesetipp: Guter Artikel über InMemory-Datenbanken

Bei Hype-Themen passiert es schnell, dass jeder dabei sein will. So behauptet doch aktuell jeder DB-Hersteller auch auf dem Gebiet "InMemory-Datenbanken" punkten zu können. Der Begriff ist nicht normiert, daher kann jeder darunter verstehen, was er will: den Meisten reicht es schon, wenn die Daten irgendwann mal zum größten Teil im Hauptspeicher stehen. Das ist freilich Blödsinn, denn nur die Systeme, die den Overhead herkömmlicher I/O-basierter Systeme einsparen, sind echt für InMemory konzipiert. Nur solchen würde ich den Stempel InMemory verpassen, wenn ich etwas zu sagen hätte.

Bei Heise fand ich den recht guten Artikel In-Memory Computing als Treiber neuartiger Geschäftsanwendungen, der das Thema aus SAP-Sicht beleuchtet.

14. März 2011 um 21:23

Performance-Tipps

Derzeit ist wieder Performance-Saison, daher konnte ich nicht umhin in diese Performance-Dokumente rein zu schauen, die gerade wieder durch das Internet geistern:

8. August 2008 um 17:51

Bessere Performance durch Datenkompression in Datenbanken?

Bei TecChannel.de einen Artikel zu Datenbanken zu finden, ist schon eher ungewöhnlich. Der Artikel "Datenkompression in SQL-Datenbanken" ist etwa auf Level 100 bis 200 und somit ein ganz guter Einstieg in das Thema. Weil heutzutage das Lesen und Schreiben von Daten tatsächlich noch in fast allen Fällen der Flaschenhals ist, ist das alte Thema immer noch relevant.

Folgende Aspekte werde hier unter dem Stichwort "Komprimierung" genannt, die dahin abzielen, dass auf eine Datenseite möglichst viele Datensätze passen:

  • Verwendung numerischer Typen anstelle von Zeichenketten
  • Verwendung von Unicode nur wo es wirklich nötig ist (SQL-Server speichert Unicode-Zeichen das als UTF-16): Der Platzbedarf sei typischerweise um 40 bis 60 Prozent höher.
  • Verwendung von numerischen Werten mit variablen Feldgröße (geht nur mit der SQL Server 2008 Enterprise Edition)
  • Seiten- oder Tabellen-Kompression bei vielen doppelten Werten nutzen (auch das geht nur mit der teuren SQL Server 2008 Enterprise Edition). Einen Performancevergleich findet man hier. Das beeindruckt.

Wer richtig große Datenbanken betreut und sich die Enterprise-Edition leisten kann, sollte sich die neuen Features dazu mal ansehen. Aber auch im Kleinen lohnt es sich Platz zu sparen.

9. Mai 2008 um 18:50

SQL-Server-2005: PIVOT auf JOINs

Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Das beschrieb ich im Artikel "SQL-Server-2005: schnelles Pivot?". Diese Serie soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenhänge helfen. Mit ein paar Kniffen kann man dann die Ausführung erheblich beschleunigen. Der Quellcode zu diesem letzten Artikel steht übrigens hier.

Performance von "gejointen" Daten

Wenn die Basistabelle des PIVOTs aus einer Derived-Table besteht, die mittels Join die Daten aus mehreren Tabellen sammelt, dann hängt es von relativ vielen Faktoren ab, ob das PIVOT schnell ist. Das gleiche gilt natürlich auch, wenn der PIVOT auf eine View oder eine Inline-Table-Valued-Function durchgeführt wird.

Man kann es aber auf den Nenner bringen: Kann der Optimizer das PIVOT so begrenzen, dass die Gruppierung allein auf den Werten einer Tabelle basiert, die mittels Clustered-Index gelesen werden können, dann ist es schnell. In den anderen Fällen muss man mit mehr oder weniger starken Performanceeinbußen rechnen.

Als Beispiel möchte ich einen relativ gängigen Sonderfall darstellen: Das Beispiel des ersten Artikels wird so erweitert, dass die Spaltenüberschriften in einer Translation-Table TranTab stehen:
Translation-Table

In der Tabelle OpenSchema steht dann der Verweis auf den Namen des Attributes:
Tabelle Openschema mit Übersetzungstabelle

Um das gewünschte Ergebnis zu bekommen, muss man PIVOT auf eine Derived-Table ausführen:

SELECT ID, "Typ", "Datum", "Anzahl", "Dings", "Bums"
FROM (SELECT os.ID, tt.Attribute, os."Value"
FROM OpenSchema as os
JOIN TransTab as tt
ON os.AttId=tt.AttId) as dt
PIVOT
(
Max("Value")
FOR Attribute IN ("Typ", "Datum", "Anzahl", "Dings", "Bums")
) AS pvt
ORDER BY ID

Die Performance verschlechtert sich dadurch dramatisch. Im Vergleich zum reinen Lesen der Daten fast um Faktor 4. Das liegt am ungünstigen Zugriffsplan:
Zugriffsplan

Wenn man einen PIVOT ohne den Join durchführt, dann erhält man die gewohnte, gute Performance:
SELECT ID, "1" as "Typ", "2" as "Datum", "3" as "Anzahl", "4" as "Dings", "5" as "Bums"
FROM (SELECT os.ID, os.AttId as Attribute, os."Value"
FROM OpenSchema as os ) as dt
PIVOT
(
Max("Value")
FOR Attribute IN ("1", "2", "3", "4", "5")
) AS pvt
ORDER BY ID

Leider muss man dazu die Spaltenamen "hart" codieren. Das ist aber genau das, was man vermeiden wollte als man die Translation-Table einführte. Um auch mit flexiblen Spaltennamen zu einer guten Performance zu kommen, muss man im Batch zunächst die Spalten ermitteln, das PIVOT-Statement dann dynamisch zusammensetzen und ausführen:

DECLARE @sqlcmd nvarchar(1000),
@selectList nvarchar(1000),
@pivotList nvarchar(1000);

SELECT
– zunächst die Spalten aus der SELECT-Liste ermitteln
@selectList = STUFF(
(SELECT N', '+QUOTENAME(AttId)+N' AS '

+QUOTENAME(Attribute) AS [text()]
FROM TransTab
ORDER BY AttId
FOR XML PATH('')), 1, 1, N''),
– dann die Liste der Spalten ermitteln
@pivotList = STUFF(
(SELECT N', '+QUOTENAME(AttId) AS [text()]
FROM TransTab
Order BY AttId
FOR XML PATH('')), 1, 1, N'');

SET @sqlcmd = N'SELECT ID, '+@selectList +N'
FROM (SELECT os.ID, os.AttId as Attribute, os."Value"
FROM OpenSchema as os) as O
PIVOT (Max("Value") FOR Attribute IN ('+@pivotList+N')) as P
ORDER BY Id;'

EXEC sp_executesql @sqlcmd;

Das ausgeführte Statement sieht dann so aus:
SELECT ID, [1] AS [Typ], [2] AS [DATUM], [3] AS [Anzahl], [4] AS [Dings], [5] AS [Bums]
FROM (SELECT os.ID, os.AttId as Attribute, os."Value"
FROM OpenSchema as os) as O
PIVOT (Max("Value") FOR Attribute IN ( [1], [2], [3], [4], [5])) as P
ORDER BY Id;

Die Gesamtperformance dieses Beispiels ist wieder sehr gut.
Insgesamt ergeben sich mit Pivot eine Menge neuer Möglichkeiten. Wenn dabei die Performance wichtig ist, dann muss man sich jedoch genau überlegen, wie man das Statement auch wirklich schnell ausführen kann.

Diese Reihe schrieb ich letzten Oktober als Reaktion auf den Aufruf im SQL-PASS-Newsletter. Möglicherweise erscheint der Artikel noch dort. Ich veröffentliche ihn jetzt aber lieber mal bevor er mit dem bald erscheinenden SQL-Server-2008 möglicherweise veraltet… 😉
7. Mai 2008 um 18:58

SQL-Server-2005: PIVOT und Performance

Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Das beschrieb ich im Artikel "SQL-Server-2005: schnelles Pivot?". Diese Serie soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenhänge helfen. Mit ein paar Kniffen kann man dann die Ausführung erheblich beschleunigen. Der Quellcode zu diesem vorletzten Artikel steht übrigens hier.

Performance

Um ein Gefühl dafür zu bekommen, wird die im ersten Artikel beschriebene Tabelle "OpenSchema" mit 1,9 Mio Datensätzen gefüllt und ausgelesen. Interessant ist, wie viel "Overhead" ein PIVOT ausmacht. Wenn man ein SELECT auf die ganze Tabelle mit einem PIVOT im gleichen Batch ausführt und dazu mit "Strg+M" den tatsächlichen Zugriffsplan anzeigen lässt, dann bekommt man neben den Zugriffsplänen auch den geschätzten, prozentualen Anteil der Statements im Batch.

SELECT ID, Attribute, "Value"
FROM OpenSchema

SELECT ID, attr1 as Typ, attr2 as Datum, attr3 as Anzahl, attr4 as Dings, attr5 as Bums
FROM OpenSchema
PIVOT
(
Max("Value")
FOR Attribute IN ("attr1", "attr2", "attr3", "attr4", "attr5")
) AS pvt
ORDER BY ID

Liegt ein Clustered-Index auf der Spalte ID, dann ist das Verhältnis 44% zu 56%. Das bedeutet, der Optimizer schätzt etwa 22% Aufschlag für den PIVOT gegenüber dem einfachen Auslesen. Das deckt sich mit dem Zugriffsplan des PIVOT: Hier benötigt das einfache Lesen auch 78%:

Das ändert sich allerdings dramatisch, wenn kein geeigneter Index vorhanden ist. Dann ist das Verhältnis im Batch 1% zu 99%.

Da für die Gruppierung bereits anhand der ID sortiert werden muss, hat das nachfolgende „ORDER BY ID“ tatsächlich keinerlei Auswirkungen auf den Zugriffsplan. Das sieht allerdings anders aus, falls man nach anderen Kriterien sortieren lässt. Das wird aber nur benötigt, wenn man Joins im PIVOT verwendet. dazu komme ich im letzten Artikel.

Zusammenfassend kann man sagen, dass ein PIVOT immer dann "schnell" ist, wenn ein Clustered Index auf den Gruppierungsfeldern liegt. Ein normaler Index hilft hier nicht weiter. Wie schnell er tatsächlich ist, hängt – wie immer – von der darunter liegenden Hardware ab…

Diese Reihe schrieb ich letzten Oktober als Reaktion auf den Aufruf im SQL-PASS-Newsletter. Möglicherweise erscheint der Artikel noch dort. Ich veröffentliche ihn jetzt aber lieber mal bevor er mit dem bald erscheinenden SQL-Server-2008 möglicherweise veraltet… 😉
5. Mai 2008 um 18:57

SQL-Server-2005: Störer beim PIVOT eliminieren

Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Das beschrieb ich im Artikel "SQL-Server-2005: schnelles Pivot?". Diese Serien soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenhänge helfen. Mit ein paar Kniffen kann man dann die Ausführung erheblich beschleunigen. Das kommt im nächsten Artikel. Der Quellcode zu diesem Artikel steht übrigens hier.

Eliminierung von Störern

Wenn in der Tabelle weitere Spalten enthalten sind, dann kann das mit PIVOT zu unerwarteten Ergebnissen führen. Wenn die Tabelle OpenSchema bspw. auch noch die Spalte „Stoerer“ enthält:
Tabelle Openschema mit Störer

Wenn man nun einfach das PIVOT aus dem letzten Artikel einfach verwendet, dann führt das zu komischen Ergebnissen.

SELECT ID,
attr1 as Typ,
attr2 as Datum,
attr3 as Anzahl,
attr4 as Dings,
attr5 as Bums
FROM OpenSchema
PIVOT (
MAX("Value")
FOR Attribute IN ("attr1", "attr2", "attr3", "attr4", "attr5")
) AS pvt
ORDER BY ID

Unerwartetes Ergebnis

Das liegt an der impliziten Gruppierung aller Felder aus der Basistabelle. Es kann also passieren, dass durch eine unbedachte Tabellenerweiterung einige SELECTs ein völlig anderes Ergebnis liefern. Das ist nicht im Geiste von SQL, ist unangenehm und soll vermieden werden. Daher rate ich dazu PIVOT-Statements immer so zu formulieren, dass sie nicht auf der Basistabelle arbeiten, sondern einer Derived-Table:

SELECT ID,
attr1 as Typ,
attr2 as Datum,
attr3 as Anzahl,
attr4 as Dings,
attr5 as Bums
FROM (
SELECT ID, Attribute, Value
FROM OpenSchema) as dt
PIVOT (
MAX("Value")
FOR Attribute IN ("attr1", "attr2", "attr3", "attr4", "attr5")
) AS pvt
ORDER BY ID

Das liefert nicht nur in diesem Fall das erwartete Ergebnis, sondern auch nach weiteren Tabellenänderungen. Der Zugriffsplan ist entspricht dem ersten Artikel.

Diese Reihe schrieb ich letzten Oktober als Reaktion auf den Aufruf im SQL-PASS-Newsletter. Möglicherweise erscheint der Artikel noch dort. Ich veröffentliche ihn jetzt aber lieber mal bevor er mit dem bald erscheinenden SQL-Server-2008 möglicherweise veraltet… 😉
3. Mai 2008 um 18:51

SQL-Server-2005: schnelles Pivot?

Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Mit ein paar Kniffen kann man dann die Ausführung erheblich beschleunigen. Diese Serien soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenhänge helfen. Der Quellcode zu diesem Artikel steht übrigens hier.

Wie wird PIVOT ausgeführt?

Angenommen die Tabelle „OpenSchema“ besteht aus drei Spalten: einer ID, einem Attributbezeichner und einer Wert-Spalte. Das sähe etwa so aus:
Tabelle Openschema

Diese Tabelle ist übrigens einem Beispiel von Itzik Ben-Gan (siehe www.sql.co.il) entnommen, weil er das PIVOT-Statement am besten erklären kann. Ehre wem Ehre gebührt.

Um die Ergebnisse dieser "Kennziffer-Wert-Tabelle" in "relationaler" Form zu präsentieren, müssen die Ergebnisse pivotiert werden:
gewünschtes Ergebnis

Vor dem SQL-Server-2005 war dazu ein recht kompliziert aussehendes Konstrukt notwendig: Man musste pro Ergebnis eine Aggregatfunktion auswerten, dass intern mittels CASE nur die gewünschten Ergebnisse auswertet:

SELECT ID,
MAX(CASE WHEN "Attribute" = 'attr1' THEN "VALUE" END) as "Typ",
MAX(CASE WHEN "Attribute" = 'attr2' THEN "VALUE" END) as "Datum",
MAX(CASE WHEN "Attribute" = 'attr3' THEN "VALUE" END) as "Anzahl",
MAX(CASE WHEN "Attribute" = 'attr4' THEN "VALUE" END) as "Dings",
MAX(CASE WHEN "Attribute" = 'attr5' THEN "VALUE" END) as "Bums"
FROM OpenSchema
GROUP by ID
ORDER BY ID

In dieser Form stellten es Rozenshtein, Abranovich und Birger schon 1992 im "SQL Server Forum 12/1992" vor. Damals gab es nur das CASE-Konstrukt noch nicht, deswegen verwendeten sie verschiedene Tricks, um ein CASE auf mathematische Weise zu erreichen. Der Vorteil bei dieser alten Schreibweise ist, dass man die Gruppierung explizit angeben kann.

Pivotieren mit Pivot

Im SQL-Server-2005 führte Microsoft bekanntlich das PIVOT-Konstrukt ein, um das Ganze zu vereinfachen. Im PIVOT-Statement wird die Gruppierung hingegen implizit durchgeführt: Es wird nach allen Spalten der Tabelle gruppiert, die nicht in den Klammern des PIVOT genannt werden. Unten werden "Value" und "Attribute" genannt, also wird nach allen verbleibenden gruppiert: hier nur "ID".

SELECT ID,
attr1 as Typ,
attr2 as Datum,
attr3 as Anzahl,
attr4 as Dings,
attr5 as Bums
FROM OpenSchema
PIVOT (
MAX("Value")
FOR Attribute IN ("attr1", "attr2", "attr3", "attr4", "attr5")
) AS pvt
ORDER BY ID

Das PIVOT-Konstrukt ist genauso implementiert, wie es bereits oben beschrieben wurde. Das schließe ich daraus, dass in beiden Fällen der gleiche Zugriffsplan verwendet wird:
Zugriffsplan

Natürlich passiert es öfters, dass der Optimizer einen semantisch äquivalenten Zugriffsplan verwendet, wenn er effizienter ist. Das ist hier jedoch unwahrscheinlich.
Die "ältere" Form hilft mir zu verstehen, wann ein PIVOT schnell ausgeführt werden kann und wann er einfach langsam sein muss. Sie hat zudem den Vorteil, dass man pro Spalte entscheiden kann, welche Aggregatfunktion verwendet werden soll. Beim neuen PIVOT wird für alle die gleiche genutzt.

Diese Reihe schrieb ich letzten Oktober als Reaktion auf den Aufruf im SQL-PASS-Newsletter. Möglicherweise erscheint der Artikel noch dort. Ich veröffentliche ihn jetzt aber lieber mal bevor er mit dem bald erscheinenden SQL-Server-2008 veraltet… 😉
15. Februar 2008 um 18:20

Ergebnisse vom Profiler und PerfMon zusammenführen

Alle, die nicht auf den erstklassigen Performance-Workshop des PSS-Teams am 14.4. gehen können, empfehle ich den Artikel "Integrating Profiler and PerfMon Log Files" auf SQLTeam.com.

Darin wird beschrieben, wie man die Ergebnisse des PerfMon in den Profiler laden kann, um sich eine kombinierte Darstellung ansehen zu können. Dadurch wird es bspw. möglich Infos zur Festplatten Nutzung oder der CPU-Nutzung mit den Traces des Profilers (also z.B. mit den SQL-Befehlen und deren Zeiten) zu kombinieren. Ohne diesen irren Trick ist man immer auf einem Auge blind…

12. August 2007 um 12:20

beliebte Performance-Fallen

Einer meiner Jobs ist die Analyse von Performanceproblemen bei unseren Anwendungen. Deswegen habe ich hier einfach mal ein paar typische Performance-Fallen bei datenbanknutzenden Anwendungen geschrieben. Natürlich ist es schwierig pauschale Ratschläge abzugeben. Es besteht immer die Gefahr platt oder arrogant zu wirken. So ist es nicht gemeint.
Das alles läuft unter dem Motto: Gefahren, die man kennt, kann man umschiffen…

* Nur die wirklich notwendigen Datensätze anfordern
Werden in der Anwendung beispielsweise Cursor verwendet (gerne bei ODBC oder MFC), dann werden oft nur die ersten 30 Datensätze lesen (mehr passt nicht auf den Bildschirm, der Rest wird dann nur im Bedarfsfalls gelesen (z.B. in einem virtuellen List-Control). Je nachdem welchen Cursor-Typ man verwendet und wie komplex das Statement ist, muss der SQL-Server aber dennoch alle vom Datensatz betroffenen Datensätze lesen, z.B. 10000, obwohl in der Regel nur die aktuellesten 20 tatsächlich von Kunden tatsächlich werden. Das gilt häufig dann, wenn der SQL-Server die Datensätze sortiert liefern soll, aber kein entsprechender Index da ist.

* Immer alle benötigen Daten auf einen Schlag anfordern
Ich sehe immer wieder, dass eine Reihe von Datensätzen aus einer Tabelle gelesen wird. Sagen wir mal die Daten einer Rechnung. Dann wird pro Rechnung nachgelesen wie der verantwortliche Sachbearbeiter – von dem ja nur die ID in dem Datensatz steckt – heißt. Dann wird der Name des Kunden (aus dem Gleichen Grund) pro Rechnung nachgelesen. Das sollte man bitte auf keinen Fall machen. Damit belegt man nur, dass der Chef das Geld oder die Zeit für eine SQL-Schulung nicht ausgeben wollte. Und kauft damit eine schlechte Performance ein.
Statt dessen verwendet man einen Join, um die beiden Namen zusammen mit den Rechnungsinformationen zu lesen. Das ist einfach und effizient.

* die Objektorientierung nicht um seiner selbst Willen durchziehen
Leider passiert es sonst regelmäßig, dass die aufgerufene Klasse, die ja meist nicht weiß in welchem Kontext sie gerufen wird, sich unsinnig verhält.
Beispielsweise soll der Status von allen Bestellungen eines Kunden auf "fakturiert" gesetzt werden, wenn die Rechnung dafür raus ging. Die beste Performance bekommt man wenn man nur ein einziges UPDATE dafür absetzt. Wenn man aber (vermeintlich) "objektorientiert" vorgeht, dann werden in einer Schleife alle Bestellungen durch genudelt, und pro Bestellung ein UPDATE abgesetzt. Das kostet unglaublich Performance und ist so als ob man jede Morgen für jedes einzelne Brötchen wieder einzeln zum Bäcker fährt. Dabei könnte man doch mit einer Fuhre alle 10 Brötchen mitbringen.

* Schleifen
Das gilt natürlich nicht nur für die objekt-orientierte Vorgehensweise: Das gilt für alle Programmiersprachen und Architekturen. Deswegen nenne ich es noch als eigenen Punkt: Jede Schleife kostet unnötig Zeit. Mehr Zeit als man denkt.

* keine oder unsinnige Clustered-Indexe
Den Primärschlüssel zum Clustered-Index zu machen ist nach meiner Erfahrung meistens suboptimal und lohnt sich nur bei zusammengesetzten Primärschlüsseln. Wenn man eine ID verwendet, dann sollte man Clustered-Index lieber auf ein oder mehrere fachliche Felder legen, die unter Umständen sogar Fremdschlüssel irgendwohin sind. Dadurch erreicht man, dass fachlich zusammengehörige Felder auf der gleichen oder wenigstens auf benachbarten Datenseiten liegen.
Der Clustered-Index sollte übrigens nicht zu "breit" sein, weil am SQL-Server-2005 die anderen Index nicht mehr auf die Datenseiten verweisen, sondern auf den Eintrag im Clustered-Index…

Früher hatte ich auch noch eine ganze Palette an Tricks zur Mikro-Optimierung auf Lager. Aber der Optimizer des SQL-Servers ist jetzt so clever geworden, dass die meisten zum alten Eisen gehören. Außerdem habe ich echt die Erfahrung gemacht, dass die dicken Brocken so viel Zeit verschlingen, dass sich die Mikrogeschichten (wie z.B. Parametrisierung) erst sehr spät rentieren…

18. Mai 2007 um 20:23

Trigger – wie viele Sätze sind betroffen?

Gestern las ich bei coreworxx den Artikel "UPDATE Trigger – schon mal reingefallen?" und wollte auch gleich einen Kommentar schreiben. Weil ich dazu aber einen Google-Account einrichten müsste, antworte ich lieber hier und setze einen Trackback…
Schade, weil ich jetzt erst mal den Kontext herstellen muss, damit man nicht erst den einen Artikel lesen muss, um diesen zu verstehen. Übrigens liebe ich es Artikel über SQL-Themen zu lesen: also bitte nicht abschrecken lassen…

Andreas Gümbel beschreibt in dem Artikel, wie man mittels der Tabellen INSERTED und DELETED mitbekommt wie die Werte des geänderten Datensatzes vor und nach der Änderung kommt. Hier eines seiner Beispiele:

CREATE TRIGGER [TRIGGER_NAME_Update] ON [dbo].[myTableName]
FOR UPDATE
AS
DECLARE @myInsertId int
SELECT @myInsertId=myTableId FROM INSERTED
[weitere Verarbeitung …]

Das ist völlig korrekt beschrieben.
Aber dennoch würde ich von diesem Statement abraten. Es geht strikt davon aus, dann immer nur ein einziger Datensatz geändert wird. Um die optimale Performance zu erreichen werden in Datenbanksystemen aber immer möglichst alle betroffenen Datensätze mit einem Schwupps geändert.
Ich merke immer wieder, dass es gerade altgedienten Programmierern schwer fällt, dass man jetzt nicht mehr mühsam alles über eine Schleife machen muss: In SQL macht man das mit einem einzigen Befehl. Hier mein Lieblingsbeispiel: Alle Mitarbeiter der Datenbankbetreuung bekommen eine 10%ige Gehaltserhöhung…

UPDATE Stammdaten.Mitarbeiter
SET Gehalt = Gehalt * 0.1
WHERE team='Datenbanktechnologie'

Das Statement dauert fast genau so lange wie der Update eines einzelnen Datensatzes. Deswegen macht man eher selten einen Update auf einen Satz. Und selbst wenn man jetzt fest überzeugt ist, dass man niemals mehrere Sätze gleichzeitig wird. Sollte man das dennoch vorsehen, weil sich meiner Erfahrung nach sehr schnell die Randbedingungen ändern und "Puff" knallt der Trigger mitten im laufenden Betrieb an die Wand.
Das ist übrigens eine Tuningmaßnahme, die ich immer wieder bei Performanceanalysen empfehlen muss: Mengenupdates anstelle von Schleifen. In der ersten Runde geht es meist auf gar keinen Fall, weil man ja in dem jeweiligen Objekt keine Infos darüber hat, welche anderen Objekte sonst noch betroffen sind. Wenn der Druck von Seiten der Kunden steigt, geht es meistens doch…

Daher: Im Trigger selber bitte immer die Verarbeitung von mehreren Datensätzen vorsehen.

24. April 2007 um 17:56

SQL-Server: Wer sind heute meine 5 Langläufer?

Im Artikel Dynamic Management Views and Functions" auf databasejournal.com fand ich folgende sehr nützlich Abfrage zur Ermittlung der durchschnittlichen CPU-Zeit der 5 längsten Abfragen:

SELECT TOP 5 total_worker_time/execution_count 'Avg CPU Time',
SUBSTRING(st.text, (qs.statement_start_offset/2)+1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(st.text)
ELSE qs.statement_end_offset
END – qs.statement_start_offset)/2) + 1)statement_text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
ORDER BY total_worker_time/execution_count DESC;

Risiken und Nebenwirkungen: In den "Dynamic-Management-Views" sind nur die Infos seit dem letzten Start des Dienstes enthalten…

PS: Wer wissen will, was aktuell gerade läuft, findet die Antwort mit folgender Abfrage von Urs Gehrig:

SELECT sp.spid [SPID], sp.last_batch [Executed at], st.text [SQL]
FROM sys.sysprocesses sp
OUTER APPLY fn_get_sql(sp.sql_handle) st
WHERE st.text IS NOT NULL

4. April 2007 um 18:43

SQL Server Wait Events

Bei SimpleTalk.com beschreibt Mario Broodbakker in dem Artikel "SQL Server Wait Events: Taking the Guesswork out of Performance Profiling" wie man am SQL-Server anhand der Warteschlangen Performancebremsen ausmachen kann. Der Artikel ist gut zu lesen. Allerdings stößt mir etwas säuerlich auf, dass nicht deutlich genug rauskommt, dass diese Idee Tom Davidson schon vor Jahren beschrieben und eingesetzt hat.

Ich finde den originalen Artikel "SQL Server 2005 Waits and Queues" von Davidson immer noch etwas besser. Am besten ist es vermutlich beide zu lesen, weil es doch eine Menge Holz auf einmal ist. Das Dokument "Troubleshooting Performance Problems in SQL Server 2005" wird hingegen erwähnt. Auch dort stehen wirklich nützliche Dinge drin, aber der in diesem Fall relevante Teil könnte gerne länger sein. Den Artikel würde ich für später empfehlen.

Wenn man den Post von Broodbakker und den Original-Artikel gelesen hat, dann findet man die weiteren Tipps von Tom Davidson zu dem Thema sicher auch nützlich: sie stehen im Artikel "Performance and Tuning Blue Prints".

|