Schmeling Datenbankentwicklung mit dem
Microsoft SQL Server 2005
Holger Schmeling
Microsoft
SQL Server 2005
Der Autor: Holger Schmeling, München
Alle in diesem Buch enthaltenen Informationen, Verfahren und Darstellungen wurden nach bestem Wissen zusammengestellt und mit Sorgfalt getestet. Dennoch sind Fehler nicht ganz auszuschließen. Aus diesem Grund sind die im vorliegenden Buch enthaltenen Informationen mit keiner Verpflichtung oder Garantie irgendeiner Art verbunden. Autoren und Verlag übernehmen infolgedessen keine juristische Verantwortung und werden keine daraus folgende oder sonstige Haftung übernehmen, die auf irgendeine Art aus der Benutzung dieser Informationen oder Teilen davon entsteht. Ebenso übernehmen Autoren und Verlag keine Gewähr dafür, dass beschriebene Verfahren usw. frei von Schutzrechten Dritter sind. Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Buch berechtigt deshalb auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.
Bibliografische Information Der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
Dieses Werk ist urheberrechtlich geschützt. Alle Rechte, auch die der Übersetzung, des Nachdruckes und der Vervielfältigung des Buches, oder Teilen daraus, vorbehalten. Kein Teil des Werkes darf ohne schriftliche Genehmigung des Verlages in irgendeiner Form (Fotokopie, Mikrofilm oder ein anderes Verfahren) auch nicht für Zwecke der Unterrichtsgestaltung reproduziert oder unter Verwendung elektronischer Systeme verarbeitet, vervielfältigt oder verbreitet werden.
© 2007 Carl Hanser Verlag München Lektorat: Fernando Schneider Sprachlektorat: Sandra Gottmann, Münster-Nienberge Herstellung: Monika Kraus Umschlagdesign: Marc Müller-Bremer, Rebranding, München Umschlaggestaltung: MCP Susanne Kraus GbR, Holzkirchen Datenbelichtung, Druck und Bindung: Kösel, Krugzell Ausstattung patentrechtlich geschützt. Kösel FD 351, Patent-Nr. 0748702 Printed in Germany ISBN-10: 3-446-22532-3 ISBN-13: 978-3-446-22532-9 www.hanser.de/computer
Für Gerda
Inhalt 1 1.1 1.2 1.3 1.4 1.5
Einleitung ................................................................................................................. 1 Was vermittelt dieses Buch?.................................................................................................... 1 Für wen ist dieses Buch? ......................................................................................................... 2 Wo finde ich was? ................................................................................................................... 3 Welche Voraussetzungen werden benötigt? ............................................................................ 5 Danksagung ............................................................................................................................. 6
2 2.1
SQL Server 2005-Architektur .................................................................................. 9 SQL Server 2005-Komponenten ............................................................................................. 9 2.1.1 Das SQL Server-Datenbankmodul........................................................................... 10 2.1.2 SQL Server Replication Services............................................................................. 12 2.1.3 SQL Server Notification Services............................................................................ 12 2.1.4 SQL Server Reporting Services ............................................................................... 12 2.1.5 SQL Server Analysis Services ................................................................................. 13 2.1.6 SQL Server Integration Services.............................................................................. 13 2.1.7 Verwaltungswerkzeuge............................................................................................ 14 2.1.8 Entwicklungswerkzeuge .......................................................................................... 14 2.1.9 Der SQL Server Agent............................................................................................. 14 SQL Server-Editionen ........................................................................................................... 15 Systemdatenbanken ............................................................................................................... 16 2.3.1 Die master-Datenbank ............................................................................................. 16 2.3.2 Die model-Datenbank .............................................................................................. 16 2.3.3 Die msdb-Datenbank ............................................................................................... 17 2.3.4 Die tempdb-Datenbank ............................................................................................ 17 2.3.5 Weitere Datenbanken............................................................................................... 17 Die Struktur einer SQL Server-Datenbank ............................................................................ 18 2.4.1 Datenbanken ............................................................................................................ 19 2.4.2 Datenbankobjekte .................................................................................................... 19 Zusammenfassung ................................................................................................................. 21
2.2 2.3
2.4
2.5
VII
Inhalt 3 3.1 3.2 3.3
3.4 3.5 3.6 3.7 3.8 3.9 4 4.1 4.2 4.3 4.4 4.5 4.6
4.7
4.8
4.9 4.10 4.11 4.12 4.13 4.14
VIII
SQL Server-Verwaltungswerkzeuge .................................................................... 23 SQL Server-Konfigurations-Manager ....................................................................................24 SQL Server-Oberflächenkonfiguration ..................................................................................25 SQL Server Management Studio............................................................................................27 3.3.1 Start und Anmeldung................................................................................................27 3.3.2 Bereiche des Hauptfensters.......................................................................................30 3.3.3 Datenbankprojekte erstellen .....................................................................................47 Business Intelligence Development Studio ............................................................................51 SQL Server Profiler................................................................................................................52 Datenbankmodul-Optimierungsratgeber ................................................................................58 Sqlcmd ...................................................................................................................................61 SQL Server-Onlinedokumentation.........................................................................................63 Zusammenfassung..................................................................................................................64 Transact SQL-Grundlagen.................................................................................... 65 Das Relationenmodell ............................................................................................................66 SQL mit System .....................................................................................................................67 T-SQL-Stapel (Batches) .........................................................................................................69 SQL-Datentypen ....................................................................................................................71 T-SQL-Ausdrücke und -Operatoren.......................................................................................76 Datenbanken verwalten ..........................................................................................................79 4.6.1 Datenbank erzeugen .................................................................................................79 4.6.2 Datenbank ändern.....................................................................................................82 4.6.3 Datenbank löschen....................................................................................................84 Schemas verwenden ...............................................................................................................84 4.7.1 Namensauflösung .....................................................................................................87 4.7.2 Synonyme .................................................................................................................89 Tabellen entwerfen .................................................................................................................90 4.8.1 Tabellen anlegen.......................................................................................................90 4.8.2 Tabellen ändern ........................................................................................................93 4.8.3 Identität/Primärschlüssel ..........................................................................................95 4.8.4 Weitere Einschränkungen.........................................................................................98 4.8.5 Berechnete Spalten .................................................................................................102 4.8.6 Ein Wiedersehen mit CREATE TABLE ................................................................103 4.8.7 Beziehungen festlegen............................................................................................104 4.8.8 Zusammenfassung zu Einschränkungen .................................................................107 4.8.9 Tabellen löschen .....................................................................................................107 4.8.10 Datenbankdiagramme.............................................................................................108 4.8.11 Temporäre Tabellen................................................................................................109 INSERT Daten einfügen ...................................................................................................110 SELECT Daten abfragen...................................................................................................111 4.10.1 Filtern: Projektion und Selektion............................................................................115 Informationen ausgeben mit PRINT.....................................................................................122 Ausführen von gespeicherten Prozeduren ............................................................................122 UPDATE Daten ändern.....................................................................................................123 DELETE Daten löschen ....................................................................................................125
Inhalt 4.15
4.16 4.17
4.18
4.19
4.20 4.21
4.22 5 5.1 5.2 5.3
5.4
SQL-Funktionen.................................................................................................................. 126 4.15.1 Systemfunktionen .................................................................................................. 127 4.15.2 Konfigurationsfunktionen ...................................................................................... 129 4.15.3 Metadatenfunktionen ............................................................................................. 130 4.15.4 Numerische Funktionen......................................................................................... 132 4.15.5 Funktionen zur Zeichenkettenbearbeitung............................................................. 136 4.15.6 Konvertierungsfunktionen ..................................................................................... 142 4.15.7 Datums- und Zeitfunktionen .................................................................................. 144 4.15.8 Sonstige Funktionen und Operatoren..................................................................... 149 Die Beispieldatenbank AdventureWorks............................................................................. 153 Sichten................................................................................................................................. 154 4.17.1 Ändern von Sichten ............................................................................................... 156 4.17.2 Aktualisierung von Daten ...................................................................................... 156 Daten einfügen mit SELECT............................................................................................... 158 4.18.1 INSERT und SELECT........................................................................................... 158 4.18.2 SELECT INTO ...................................................................................................... 158 Null...................................................................................................................................... 159 4.19.1 Dreiwertige Logik.................................................................................................. 160 4.19.2 Arithmetische Operationen .................................................................................... 163 4.19.3 Operationen mit Zeichenketten.............................................................................. 164 4.19.4 Hinweise für die Verwendung von NULL............................................................. 166 4.19.5 Spezielle Funktionen für NULL-Werte ................................................................. 167 Fehlerbehandlung in T-SQL................................................................................................ 169 Komplexe Abfragen mit SELECT....................................................................................... 170 4.21.1 Mengenoperationen mit SQL................................................................................. 170 4.21.2 Abfragen über mehrere Tabellen ........................................................................... 172 4.21.3 Aggregatfunktionen und GROUP BY ................................................................... 179 4.21.4 Rangfolgefunktionen ............................................................................................. 186 4.21.5 Korrelierende Unterabfragen ................................................................................. 191 4.21.6 Abgeleitete Tabellen .............................................................................................. 192 4.21.7 Allgemeine Tabellenausdrücke.............................................................................. 193 4.21.8 PIVOT und UNPIVOT .......................................................................................... 198 4.21.9 OUTPUT ............................................................................................................... 202 4.21.10 APPLY .................................................................................................................. 203 Zusammenfassung ............................................................................................................... 205 Transaktionen und Sperren ................................................................................ 207 Eigenschaften von Transaktionen........................................................................................ 207 Start und Ende einer Transaktion ........................................................................................ 210 5.2.1 Transaktionsmodi des SQL Servers....................................................................... 211 Sperren ................................................................................................................................ 213 5.3.1 Sperrbare Ressourcen ............................................................................................ 214 5.3.2 Sperrtypen.............................................................................................................. 217 5.3.3 Deadlocks .............................................................................................................. 219 Isolationsstufen.................................................................................................................... 224 5.4.1 READ UNCOMMITTED...................................................................................... 224
IX
Inhalt
5.5 5.6
5.7 5.8 6 6.1
6.2
6.3
6.4
6.5 7 7.1
7.2 7.3
X
5.4.2 READ COMMITTED ............................................................................................225 5.4.3 REPEATABLE READ...........................................................................................226 5.4.4 SNAPSHOT ...........................................................................................................227 5.4.5 SERIALIZABLE....................................................................................................228 5.4.6 Abschließende Betrachtungen zu Isolationsstufen..................................................228 Sperren explizit anfordern Sperrhinweise .........................................................................229 Das Transaktionsprotokoll ...................................................................................................232 5.6.1 Abschneiden des Protokolls....................................................................................233 5.6.2 Verhalten im Fehlerfall...........................................................................................234 Hinweise zur Verwendung von Transaktionen.....................................................................234 Zusammenfassung................................................................................................................236 Administration für Datenbankentwickler........................................................... 237 Datensicherung und -wiederherstellung ...............................................................................237 6.1.1 Wiederherstellungsmodelle ....................................................................................240 6.1.2 Prüfen der Konsistenz einer Datenbank..................................................................242 6.1.3 Vollständige Datenbanksicherung ..........................................................................242 6.1.4 Sicherung des Transaktionsprotokolls ....................................................................248 6.1.5 Sichern der Systemdatenbanken .............................................................................251 Wiederherstellung von Datenbanken....................................................................................252 6.2.1 Automatische Wiederherstellung bei Systemstart ..................................................255 6.2.2 Wiederherstellung der Systemdatenbanken ............................................................255 Zugriffskontrolle ..................................................................................................................256 6.3.1 SQL Server-Anmeldungen .....................................................................................257 6.3.2 Anmeldungen hinzufügen.......................................................................................259 6.3.3 Anmeldungen löschen ............................................................................................261 6.3.4 Berechtigungen vergeben .......................................................................................261 6.3.5 Vordefinierte Anmeldekonten ................................................................................262 6.3.6 Datenbankbenutzer .................................................................................................263 6.3.7 Datenbankrollen .....................................................................................................265 6.3.8 Rechte erteilen und entziehen .................................................................................266 Überwachung von SQL Server.............................................................................................270 6.4.1 Was kann überwacht werden? ................................................................................271 6.4.2 Überwachung mit dem SQL Server Management Studio.......................................272 6.4.3 Überwachung mit dem SQL Server Profiler...........................................................276 6.4.4 Überwachung mit dem Windows-Systemmonitor..................................................276 6.4.5 Verwendung von T-SQL zur Überwachung ...........................................................277 Zusammenfassung................................................................................................................286 T-SQL-Programmierung...................................................................................... 287 Variablen..............................................................................................................................287 7.1.1 Deklaration von Variablen......................................................................................287 7.1.2 Wertzuweisung an Variablen..................................................................................288 7.1.3 Besondere Variablentypen......................................................................................290 Dynamisches SQL................................................................................................................292 Ablaufsteuerung ...................................................................................................................293
Inhalt
7.4
7.5
7.6
7.7
7.8 8 8.1
8.2 8.3
8.4
7.3.1 Anweisungsblöcke ................................................................................................. 293 7.3.2 Bedingte Ausführung von Anweisungen ............................................................... 293 7.3.3 Wiederholte Ausführung von Anweisungen .......................................................... 294 Cursor.................................................................................................................................. 297 7.4.1 Cursor erzeugen ..................................................................................................... 297 7.4.2 Einen Cursor durchlaufen Daten abrufen............................................................ 300 7.4.3 Daten aktualisieren ................................................................................................ 305 7.4.4 Hinweise zur Verwendung von Cursorn ................................................................ 306 Gespeicherte Prozeduren ..................................................................................................... 306 7.5.1 Warum gespeicherte Prozeduren?.......................................................................... 307 7.5.2 Erstellung von gespeicherten Prozeduren .............................................................. 307 7.5.3 Verwenden von Parametern ................................................................................... 311 7.5.4 Der Rückgabewert einer gespeicherten Prozedur .................................................. 314 7.5.5 Die gespeicherte Systemprozedur sp_procoption .................................................. 314 7.5.6 EXECUTE und INSERT ....................................................................................... 316 7.5.7 Erweitertes Beispiel: PDF-Dateien einlesen .......................................................... 317 Benutzerdefinierte Funktionen ............................................................................................ 320 7.6.1 Skalarwertfunktionen............................................................................................. 320 7.6.2 Tabellenwertfunktionen ......................................................................................... 323 Trigger................................................................................................................................. 325 7.7.1 DML-Trigger ......................................................................................................... 326 7.7.2 Hinweise zur Verwendung von Triggern ............................................................... 332 7.7.3 DDL-Trigger.......................................................................................................... 333 Zusammenfassung ............................................................................................................... 338 XML-Integration ................................................................................................... 339 Tabellen im XML-Format darstellen: FOR XML ............................................................... 339 8.1.1 FOR XML RAW ................................................................................................... 341 8.1.2 FOR XML AUTO.................................................................................................. 342 8.1.3 FOR XML EXPLICIT........................................................................................... 344 8.1.4 FOR XML PATH .................................................................................................. 352 8.1.5 FOR XML-Optionen.............................................................................................. 355 8.1.6 Noch einmal FOR XML PATH............................................................................. 358 XML-Daten in Tabellenform abbilden: OPENXML........................................................... 367 Der XML-Datentyp ............................................................................................................. 372 8.3.1 Ungetypte XML-Daten .......................................................................................... 375 8.3.2 Getypte XML-Daten .............................................................................................. 376 8.3.3 query...................................................................................................................... 379 8.3.4 exist ....................................................................................................................... 382 8.3.5 value ...................................................................................................................... 383 8.3.6 nodes...................................................................................................................... 384 8.3.7 modify.................................................................................................................... 388 8.3.8 XML-Indizes ......................................................................................................... 391 Zusammenfassung ............................................................................................................... 395
XI
Inhalt 9 9.1
9.2
9.3 9.4 10 10.1 10.2 10.3 10.4 10.5 10.6
10.7 10.8 10.9 11 11.1 11.2 11.3
11.4 11.5
11.6
XII
SQL Server Service Broker................................................................................. 397 Architektur ...........................................................................................................................397 9.1.1 Service Oriented Architecture (SOA) .....................................................................398 9.1.2 Service Broker-Kommunikation.............................................................................399 9.1.3 Service Broker-Komponenten ................................................................................401 Erstellung von Anwendungen ..............................................................................................402 9.2.1 T-SQL-Kommandos für eine Service Broker-Kommunikation ..............................402 9.2.2 Eine Service Broker-Beispielanwendung ...............................................................403 9.2.3 Service Broker-Aktivierung....................................................................................417 Überwachung von Server- und Datenbankobjekten .............................................................418 Zusammenfassung................................................................................................................421 Datenbankentwicklung mit .NET ........................................................................ 423 SQL Server und die .NET CLR............................................................................................423 Sicherheitsaspekte ................................................................................................................425 Assemblys ............................................................................................................................426 Der Namensraum Microsoft.SqlServer.Server .....................................................................430 Datentypen der Namensraum System.Data.SqlTypes .......................................................431 Datenbankprojekte mit Visual Studio 2005 erstellen ...........................................................431 10.6.1 Erstellen eines Datenbankprojektes ........................................................................432 10.6.2 Erstellen von gespeicherten Prozeduren .................................................................434 10.6.3 Erstellen von benutzerdefinierten Funktionen ........................................................442 10.6.4 Erstellen benutzerdefinierter Typen........................................................................44 10.6.5 Erstellen von benutzerdefinierten Aggregaten........................................................45 10.6.6 Erstellen von Triggern ............................................................................................460 10.6.7 Debuggen von CLR-Objekten ................................................................................462 Noch einmal: Sicherheit .......................................................................................................46 T-SQL oder .NET?...............................................................................................................46 Zusammenfassung................................................................................................................46 Web Services mit dem SQL Server .................................................................... 469 Was sind Web Services? ......................................................................................................469 SOAP-Endpunkte.................................................................................................................470 11.2.1 Syntax zur Erstellung eines HTTP-Endpunktes......................................................471 Reservierung von HTTP-Namensräumen.............................................................................473 11.3.1 Implizite Reservierung ...........................................................................................474 11.3.2 Explizite Reservierung ...........................................................................................475 11.3.3 Löschen einer expliziten Reservierung...................................................................475 Aufbau der URL für einen Endpunkt ...................................................................................476 Einen Web Service erstellen und verwenden .......................................................................476 11.5.1 Den AdWrksServicePoint-Endpunkt erzeugen........................................................477 11.5.2 Sicherheitsaspekte ..................................................................................................478 11.5.3 Den Web Service verwenden..................................................................................481 Zusammenfassung................................................................................................................486
Inhalt 12 12.1 12.2 12.3 12.4
Optimierung von Abfragen ................................................................................. 487 Die Speicherverwaltung von SQL Server............................................................................ 488 Ausführung von Abfragen durch SQL Server ..................................................................... 489 Erstellen einer Testdatenbank.............................................................................................. 490 Verwenden von Indizes ....................................................................................................... 491 12.4.1 Tabellen ohne Index Heap .................................................................................. 491 12.4.2 Gruppierte Indizes ................................................................................................. 492 12.4.3 Nicht gruppierter Index auf einem Heap................................................................ 494 12.4.4 Nicht gruppierter Index auf einem gruppierten Index............................................ 495 12.4.5 Erzeugen von Indizes............................................................................................. 497 12.4.6 Löschen von Indizes .............................................................................................. 501 12.5 Messen der Leistung............................................................................................................ 501 12.5.1 Messen mit der Stoppuhr ....................................................................................... 502 12.5.2 Statistische Werte .................................................................................................. 503 12.5.3 Ausführungspläne.................................................................................................. 505 12.5.4 Der SQL Server Profiler ........................................................................................ 511 12.5.5 Windows-Systemmonitor ...................................................................................... 517 12.5.6 Verbindung von Ablaufverfolgung und Logfile .................................................... 518 12.6 Beispiele für die Optimierung ............................................................................................. 521 12.7 Weitere Hinweise für Indizes .............................................................................................. 528 12.7.1 Index-Selektivität................................................................................................... 528 12.7.2 Index-Fragmentierung ........................................................................................... 529 12.7.3 Fehlende Indizes .................................................................................................... 529 12.7.4 Nicht verwendete Indizes....................................................................................... 531 12.8 Der Datenbankmodul Optimierungsratgeber ....................................................................... 532 12.9 Tipps zur Abfrageoptimierung ............................................................................................ 535 12.10 Zusammenfassung ............................................................................................................... 538 13 13.1 13.2 13.3 13.4
13.5
Anwendungen mit den Notification Services erstellen .................................... 539 Einführung in SQL Server Notification Services ................................................................ 539 Architektur .......................................................................................................................... 540 Erstellen von Notification Services-Anwendungen............................................................. 543 Eine Notification Services-Beispielanwendung................................................................... 544 13.4.1 Instance Configuration File (ICF).......................................................................... 544 13.4.2 Application Definition File (ADF) ........................................................................ 547 13.4.3 Erzeugen und Ausführen der NS-Anwendung....................................................... 554 13.4.4 Überprüfen der erzeugten NS-Datenbanken und -Objekte..................................... 558 13.4.5 Erzeugen von Abonnements und Ereignissen........................................................ 559 Zusammenfassung ............................................................................................................... 572
Literatur ........................................................................................................................... 573 Register............................................................................................................................ 575
XIII
Vorwort
Mit dem SQL Server 2005 hat Microsoft die nächste Generation des SQL Server Systems herausgegeben, die sich von der Vorgängerversion in vielen Bereichen deutlich abhebt. Die Änderungen sind teilweise beträchtlich, man könnte gar versucht sein zu behaupten, dass es sich beim SQL Server 2005 eher um ein neues Produkt als um eine Weiterentwicklung der vorherigen Version(en) handelt. Die Erweiterungen erstrecken sich natürlich auch oder eben erst recht auf die Möglichkeiten und Mittel, die dem Datenbankentwickler nunmehr zur Verfügung stehen. Seien es die Sprache T-SQL, die Common Language Runtime Integration, neue Möglichkeiten durch die erweiterte XML-Integration oder die Einführung komplett neuer Module wie Service Broker oder Notification Services. All diese Bereiche sind mittlerweile umfangreich und interessant genug, um jeweils eigene Bücher zu füllen. Aber warum veröffentlicht man denn nun eigentlich noch ein Buch über die SQL Server 2005 Datenbankentwicklung, mögen Sie sich fragen. Nun, dieses Buch konzentriert sich nahezu ausschließlich auf die reine Datenbankentwicklung, also die Programmierung bzw. die Programmiermöglichkeiten des SQL Servers 2005 an sich, unter Verwendung von T-SQL und C#. Durch eine derartige Fokussierung kann in diesem Buch auch auf so interessante Bereiche wie z.B. die Erstellung von Anwendungen mit den Notifications Services, die Optimierung von Abfragen oder auch den Einsatz des Service-Brokers näher eingegangen werden, die in vergleichbaren Werken oft ausgelassen werden. Dagegen werden Sie hier kaum Erläuterungen zur Client-Entwicklung, also etwa zum Datenzugriff mittels ADO.NET, finden und wenn, dann sicherlich nur am Rande. Ich hoffe, dass dieses Buch mit der beschriebenen Fokussierung Ihnen nützlich sein wird, sowohl beim Einstieg in die Programmierung des SQL Servers 2005 als auch beim eventuellen Umstieg von der Vorgängerversion, und wünsche Ihnen viel Freude beim Lesen bzw.
XV
Vorwort Durcharbeiten. Möglicherweise möchten Sie aber auch einfach nur Anregungen für einen Vergleich der Leistungsfähigkeit des SQL Servers 2005 mit anderen Datenbanksystemen erhalten, was die Möglichkeiten der Datenbankentwicklung anbelangt. Auch dann werden Sie sicherlich nützliche Informationen finden. Abschließend noch eine Anmerkung. Alle an diesem Buch Beteiligten haben mit großer Akribie gearbeitet, um in diesem Buch bestmögliche Informationen zusammenzutragen. Sämtliche im Buch verwendeten Beispiele (und das sind eine Menge!) wurden deshalb sorgfältig entworfen und geprüft. Nichtsdestotrotz kann sich natürlich der eine oder andere (Druck-)Fehler eingeschlichen haben. Falls Sie solche Ungereimtheiten entdecken, zögern Sie bitte nicht, mich zu informieren. Auch bei auftretenden Fragen, Anregungen oder Korrekturvorschlägen würde ich mich über eine Kontaktaufnahme sehr freuen.
München, im April 2007 Holger Schmeling
[email protected]
XVI
1 Einleitung Als die Firma Microsoft im November des Jahres 2005 endlich die lang ersehnte neue Version des SQL Servers freigegeben hat, war ich sehr gespannt darauf, ob sich das mehrjährige Warten auf diese Version auch tatsächlich gelohnt hat. Immerhin ist der SQL Server 2005 mit einer Verspätung von einigen Jahren erschienen, was die Frage aufwirft, ob denn die neuen Features diese Verspätung wenigstens rechtfertigen. SQL Server 2005 enthält faszinierende und beeindruckende Erweiterungen, vor allem in den Bereichen Business Intelligence und Datenbank- bzw. Anwendungsentwicklung, aber auch wesentlich verbesserte Möglichkeiten für den Administrator. Insgesamt umfasst der SQL Server 2005 eine solche Vielzahl von Veränderungen bzw. Weiterentwicklungen gegenüber der Vorgängerversion, dass ich letztlich sogar ein wenig Verständnis für die nicht unerhebliche Verspätung seiner Auslieferung aufbringe. Aus meiner Sicht kann ich die Frage, ob sich das Warten gelohnt hat, also mit einem klaren Ja beantworten. SQL Server 2005 bietet eine sichere, zuverlässige und stabile Plattform für unternehmenskritische Anwendungen. Da Sie dieses Buch in der Hand halten, denken Sie zumindest über den Einsatz von SQL Server 2005 nach. Ich hoffe, dass Ihnen das Buch hierbei eine Hilfe und ein Wegweiser sein wird.
1.1
Was vermittelt dieses Buch? In diesem Buch werden vor allem die Möglichkeiten für die Datenbankentwicklung mit dem SQL Server 2005 ausgelotet. Hierbei wird natürlich ausführlich auf die neuen Features von SQL Server 2005 eingegangen. Das Buch beinhaltet jedoch letztlich alle Aspekte der Datenbankentwicklung. Sie können dieses Buch also verwenden, um in die Datenbankentwicklung mit SQL Server 2005 von Grund auf einzusteigen. Unabhängig von den Neuerungen im SQL Server 2005 ist die Sprache T-SQL nach wie vor die erste Wahl bei der Datenbankentwicklung mit SQL Server. Weite Teile des vorliegenden Buches konzentrieren sich daher auch auf diesen Aspekt, also die Verwendung von
1
1 Einleitung T-SQL. Darüber hinaus finden Sie hier selbstverständlich auch detaillierte Ausführungen über die .NET-Integration, die Erstellung von Web Services und die Möglichkeiten für die Verarbeitung von XML-Daten. Diese Thematiken werden jeweils in eigenen Kapiteln abgehandelt. Das Buch enthält auch eigenständige Kapitel zu den Themen SQL Server Service Broker und Notification Services. Ebenso finden Sie ein Kapitel, das sich mit der SQL ServerAdministration befasst. In diesen drei Kapiteln wird allerdings lediglich eine Einführung in die Thematik vermittelt. Anders geht es leider nicht, da der Umfang des Buches natürlich beschränkt ist. Die Kapitel sind jedoch inhaltlich so aufgebaut, dass Sie mit den dort präsentierten Informationen in die Lage versetzt werden, bei Bedarf tiefer in die jeweilige Thematik einzusteigen. Gleiches gilt auch für die Optimierung von Abfragen, die ebenfalls in einem eigenständigen Kapitel behandelt wird. Optimierung ist ein recht komplexes Thema, über das auch bereits eigene Bücher geschrieben wurden (und werden). Die an dieser Stelle zur Thematik Abfrageoptimierung dargebotenen Ideen sind dazu gedacht, Sie in die Materie einzuführen und Sie mit einer Art Framework für die Untersuchung von Abfragen und deren Optimierung auszustatten. Der Bereich Business Intelligence wird in diesem Buch nicht behandelt. Sie werden hier also keine Ausführungen zu SQL Server Integration Services, Analysis Services oder Reporting Services finden.
1.2
Für wen ist dieses Buch? Wie der Titel eindeutig verrät, wendet sich dieses Buch in erster Linie an Datenbankentwickler. Ich denke jedoch, dass sich für jeden, der einfach nur die Möglichkeiten von SQL Server 2005 ausloten möchte, ein Blick in das Buch lohnen wird. Vor allem SQL ServerAdministratoren sollten sich ebenso angesprochen fühlen. Sicherlich muss ein Administrator dieses Buch nicht unbedingt von Anfang bis Ende durcharbeiten. Der SQL Server 2005 ist jedoch kein reines Datenbank-Managementsystem mehr, sondern ebenso ein Anwendungsserver. Der Einsatz von SQL Server als Anwendungsserver stellt natürlich nicht nur eine Herausforderung für den Datenbankentwickler, sondern ebenso für den Administrator dar. Dies bezieht sich zum Beispiel auf die Möglichkeit, Web Services bereitstellen zu können, sowie auf die Komponenten Service Broker und Notification Services. Ein Administrator sollte in der Lage sein, Anwendungen, die unter Verwendung dieser Komponenten erstellt wurden, auch abzusichern. In den entsprechenden Kapiteln zu diesen Themen wird hierauf zum Teil näher eingegangen. Darüber hinaus finden Administratoren hier auch Grundlagen für die Optimierung.
2
1.3 Wo finde ich was?
1.3
Wo finde ich was? Das vorliegende Buch unterteilt sich in vier größere Abschnitte. Einführung Der erste Teil besteht aus den Kapiteln 2 und 3 und vermittelt Grundlegendes zur SQL Server-Architektur sowie zur Benutzung des Servers unter Verwendung der mitgelieferten Software für die Server-Verwaltung. Kapitel 2: SQL Server 2005-Architektur. In diesem Kapitel werden die SQL ServerKomponenten und deren Zusammenspiel behandelt. Kapitel 3: SQL Server-Verwaltungswerkzeuge. SQL Server 2005 wird mit einem komplett übererarbeiteten Satz an Verwaltungswerkzeugen ausgeliefert. Kapitel 3 erklärt die Verwendung dieser Werkzeuge mit Bezug auf Entwicklung und Administration. T-SQL-Grundlagen In diesem Teil des Buches werden sowohl die Grundlagen von T-SQL als auch die Verarbeitung von Transaktionen behandelt Kapitel 4: Transact SQL-Grundlagen. Dies ist das bei weitem umfangreichste Kapitel des Buches. Das Kapitel vermittelt eine ausführliche Einführung in die Verwendung der Sprache T-SQL. Kapitel 5: Transaktionen und Sperren. In diesem Kapitel erfahren Sie, wie SQL Server Transaktionen benutzt, um die Datenintegrität sicherzustellen, und wie Sperren verwendet werden, um parallele Zugriffe auf Server- und Datenbankressourcen zu ermöglichen. SQL Server-Administration Der dritte Teil liegt konzentriert sich auf die Administration. Kapitel 6: Administration für Datenbankentwickler. Grundkenntnisse der Administration sind auch für Datenbankentwickler interessant und erforderlich. Kapitel 6 vermittelt eine Einführung in diese Thematik. Kapitel 12: Optimierung von Abfragen. Für die Optimierung von Abfragen ist es erforderlich, die Abfrageleistung zu messen und langsame Abfragen, die überarbeitet werden sollten, herauszufinden. Welche Möglichkeiten Ihnen hierfür zur Verfügung stehen, ist unter anderem Gegenstand des Kapitels 12. Datenbankentwicklung Dieser Part ist natürlich der Schwerpunkt des Buches. Hier erfahren Sie alles über die Möglichkeiten für die Datenbankentwicklung mit dem SQL Server.
3
1 Einleitung Kapitel 7: T-SQL-Programmierung. Kapitel 7 erläutert weiterführende Konzepte für den Einsatz von T-SQL zur Datenbankprogrammierung. Hier wird erklärt, welche Sprachelemente T-SQL zu diesem Zweck zur Verfügung stellt. Das Kapitel erläutert auch die Entwicklung von gespeicherten Prozeduren, benutzerdefinierten Funktionen und Triggern. Kapitel 8: XML-Integration. Mit SQL Server 2005 wird ein nativer XML-Datentyp eingeführt. Die sich hierdurch ergebenden Möglichkeiten sind Gegenstand des Kapitels 8. Darüber hinaus erfahren Sie in diesem Kapitel, wie XML-Daten in ein Tabellenformat bzw. Tabellen in XML überführt werden können. Kapitel 9: SQL Server Service Broker. Der mit dem SQL Server 2005 eingeführte Service Broker ermöglicht eine asynchrone Datenverarbeitung. In Kapitel 9 erfahren Sie, wie Sie den Service Broker einsetzen können. Kapitel 10: Datenbankentwicklung mit .NET. Eine fantastische Neuigkeit in SQL Server 2005 ist die Möglichkeit, Datenbank- und Serverobjekte in einer .NETProgrammiersprache erstellen zu können. Kapitel 10 zeigt, wie es geht, und erklärt auch, was hierbei zu beachten ist. Kapitel 11: Web Services mit dem SQL Server. SQL Server 2005 erlaubt die Erstellung von nativen HTTP SOAP-Endpunkten und ist dadurch in der Lage, auf HTTP SOAP-Anforderungen zu antworten, ohne dass die Internet Information Services als Vermittler notwendig sind. Dies ermöglicht die Bereitstellung von Web Services direkt mit dem SQL Server. Kapitel 11 befasst sich mit dieser Thematik. Kapitel 12: Optimierung von Abfragen. Hier erfahren Sie, wie Sie als Datenbankentwickler Abfragen optimieren können. Kapitel 13: Anwendungen mit den Notification Services erstellen. Kapitel 13 befasst sich mit einer speziellen SQL Server-Komponente, den Notification Services. Die Notification Services bieten ein Anwendungs-Framework zur Erstellung von Applikationen für eine Ereignisüberwachung. In Kapitel 13 erhalten Sie hierzu eine Einführung. Das Kapitel 12 nimmt hier eine gewisse Sonderstellung ein. In diesem Kapitel wird untersucht, welche grundlegenden Möglichkeiten für die Optimierung von Abfragen SQL Server bietet. Sicher ist die Entwicklung performanter Abfragen in erster Linie Sache des Datenbankentwicklers. Das Kapitel befasst sich jedoch auch damit, auf welche Weise Abfragen, für die eine Optimierung lohnenswert oder erforderlich ist, gefunden werden können, wie man also die Abfrageleistung messen kann. Daher ist dieses Kapitel sicherlich auch für Administratoren interessant. Das Kapitel ist natürlich auch ein Beleg dafür, dass in vielen Fällen eine klare Abgrenzung zwischen Administrator und Datenbankentwickler schwierig ist.
4
1.4 Welche Voraussetzungen werden benötigt?
1.4
Welche Voraussetzungen werden benötigt? Persönliche Kenntnisse Zum Verständnis der in diesem Buch abgehandelten Thematiken sind grundlegende Kenntnisse relationaler Datenbanken nützlich, aber nicht unbedingt erforderlich. Wenn Sie wissen, wie Tabellen aufgebaut sind, so ist dies für ein Verständnis des Buchinhaltes absolut ausreichend. Falls Sie das Gefühl haben, in dieser Beziehung nähere Informationen zu benötigen, so finden Sie eine Vielzahl von Büchern, die sich mit dieser Thematik auseinandersetzen. Stellvertretend sei hier nur [STEI03] genannt. Sie benötigen weiterhin keinerlei SQL Server-Vorkenntnisse. Sicherlich ist es von Vorteil, wenn Sie bereits Gelegenheit hatten, Erfahrungen mit einer Vorgängerversion von SQL Server etwa SQL Server 2000 zu sammeln. Das Buch ist jedoch so aufgebaut, dass dies nicht erforderlich ist. Systemvoraussetzungen Microsoft empfiehlt einen Computer mit Pentium III 600 und 512 MB Hauptspeicher als Minimalvoraussetzung für die Installation des SQL Servers 2005. Für die lizenzkostenfrei verfügbare Express Edition des SQL Servers 2005 sollen sogar 192 MB als Hauptspeicher ausreichend sein. Das erscheint sicherlich etwas niedrig gegriffen. Ich kann dies jedoch bestätigen, da ich auf einem entsprechenden Computer (PIII 600, 512 MB) eine Installation der SQL Server 2005 Developer Edition im Einsatz hatte. Allerdings sollten Sie von solch einem System natürlich keine Wunder erwarten. Wenn möglich, verwenden Sie einen PC mit mehr Prozessor und vor allem mit mehr Hauptspeicher, erst dann bereitet die Arbeit mit SQL Server auch Freude. Damit Sie alle in diesem Buch verwendeten Beispiele nachvollziehen können, benötigen Sie zumindest die Standard Edition von SQL Server. Falls Sie diese Version nicht besitzen, so können Sie sich auf der Microsoft-Webseite auch eine 180-Tage-Testversion der Enterprise Edition von SQL Server herunterladen, in der dann alle SQL Server-Features zur Verfügung stehen. Die Verwendung der lizenzkostenfrei verfügbaren Express Edition ist für Experimente mit den in diesem Buch behandelten Thematiken nicht ausreichend. Insbesondere für die Beispiele in den Kapiteln 11, 12 und 13, aber auch für Teile der anderen Kapitel ist der Leistungsumfang der Express Edition zu eingeschränkt. (Kapitel 2 erklärt den Umfang der einzelnen Editionen etwas genauer.) Das Buch geht davon aus, dass eine SQL Server-Version bereits installiert ist, befasst sich also nicht mit der Installation des Servers. Um die Beispiele des Buches nachvollziehen zu können, müssen Sie außerdem lokale Administratorrechte besitzen oder zumindest für den SQL Server als Systemadministrator eingerichtet sein. In den Kapiteln 10, 11 und 13 werden C# Anwendungen bzw. -Assemblys mit Visual Studio 2005 erstellt. Wenn Sie die Beispiele in diesen Kapiteln nachvollziehen möchten, so
5
1 Einleitung benötigen Sie wenigstens die Visual Studio 2005 Express Edition. Diese Edition können Sie kostenlos im Internet beziehen. Mit der Visual Studio Express Edition sind allerdings nur lokale Verbindungen zu SQL Servern möglich. Sie benötigen dann also eine lokale Installation von SQL Server. Besser wäre es daher, wenn Sie Visual Studio 2005 wenigstens in der Standard Edition zur Verfügung haben. Die Beispieldatenbank AdventureWorks Zum Leistungsumfang aller SQL Server-Editionen gehört die AdventureWorksBeispieldatenbank. Diese Datenbank wird in der kompletten SQL Server-Dokumentation, aber auch in vielen Artikeln und Büchern zu SQL Server verwendet und ist sozusagen mittlerweile eine Art Standard für diese Zwecke. Auch in diesem Buch basieren viele Beispiele und Erklärungen auf der Datenbank AdventureWorks. Sie sollten also auf eine SQL Server-Installation mit dieser Datenbank Zugriff haben. Im vorliegenden Buch werden nicht alle Tabellen der Datenbank verwendet. In Kapitel 4 finden Sie ein Diagramm dieser Datenbank, das nur diejenigen Objekte der Datenbank darstellt, die in diesem Buch verwendet werden. Generell ist es eine gute Idee, wenn Sie sich das komplette Datenbankdiagramm dieser Datenbank einmal ansehen, um zum Beispiel auch für das Studium anderer Literatur zu SQL Server 2005 entsprechend gewappnet zu sein. Sie finden dieses Diagramm auf der Microsoft-Webseite unter dieser Adresse:
Zusätzlich wird das vollständige Datenbankdiagramm auch auf der Webseite zu diesem Buch zur Verfügung stehen.
1.5
Danksagung Ein Buch zu schreiben zumal als Erstautor ist eine echte Herausforderung! Es bedeutet zuerst einmal eine Menge Stress, gibt einem aber auch die Möglichkeit zu lernen. Der Lernprozess ist hierbei nicht nur auf das Fachliche beschränkt, sondern bezieht auch den eigentlichen Produktionsprozess ein. Das Buch, das Sie gerade in den Händen halten, ist letztlich das Resultat einer Teamarbeit. Sicherlich hat der Autor den größten Teil der Arbeit geleistet. Dafür steht ja dann auch sein Name auf dem Buchdeckel. Für mich war dies alles eine neue und ausgezeichnete Erfahrung. Wenn Sie ein Buch in die Hand nehmen, so können Sie in fast allen Fällen an dieser Stelle entsprechende Danksagungen lesen. So auch hier. Beim Lesen eines Buches werden Sie diesen Abschnitt in der Regel einfach übergehen. Aus meiner nun gesammelten Erfahrung kann Ich Ihnen aber versichern, dass diese Danksagungen allesamt durchaus ernst gemeint sind und nicht nur aus purer Höflichkeit geschrieben werden. Glauben Sie mir dies bitte!
6
1.5 Danksagung Ohne die Unterstützung einer Reihe weiterer Leute aus dem Verlag und auch dem persönlichen Umfeld wäre dieses Buch mit Sicherheit nicht möglich gewesen. In erster Linie möchte ich mich hier bei Herrn Fernando Schneider vom Hanser Verlag bedanken, der es mir überhaupt erst ermöglicht hat, dieses Buch in Angriff zu nehmen. Seine kompetente Beratung und sein Verständnis waren eine große Hilfe. Ein weiterer Dank gelten Frau Kraus vom Hanser Verlag sowie Frau Gottmann. Beide haben durch ihre Arbeit aus dem Manuskript überhaupt erst ein Buch geformt. Das letzte und zugleich wichtigste Dankeschön ist Angelika vorbehalten, die während der Zeit, die dieses Buch benötigt hat, auf vieles verzichten musste. Insbesondere ihre Unterstützung und ihre behutsame, aber doch irgendwie energische Art, mich anzutreiben, hat viel zum Gelingen dieses Buches beigetragen. Hierfür und auch für alles andere: Danke, Angelika.
7
2 SQL Server 2005-Architektur Der SQL Server 2005 ist inzwischen weit mehr als nur ein pures Datenbankmanagementsystem (DBMS). Sicherlich ist unbestreitbar, dass die Kernfunktionalität nach wie vor entscheidend durch das Datenbankmodul, das letztlich die DBMS-Funktionalität zur Verfügung stellt, bestimmt wird. Auch der Inhalt dieses Buches befasst sich weitgehend mit der Verwendung des Datenbankmoduls aus der Sicht des Datenbankentwicklers. Allerdings enthält der SQL Server auch eine Reihe weiterer Komponenten, die es ermöglichen, den SQL Server nicht nur als ein reines DBMS, sondern auch als Anwendungsserver zu betreiben. Die SQL Server-Komponenten werden im folgenden Kapitel, das sich mit der generellen Architektur des SQL Servers auseinandersetzt, zunächst vorgestellt. Im weiteren Verlauf dieses Buches werden Sie dann sehen, wie Sie einige dieser Komponenten als Datenbankentwickler einsetzen können.
2.1
SQL Server 2005-Komponenten Abbildung 2.1 zeigt die Komponenten des SQL Servers 2005. Die Abbildung verdeutlicht weiterhin die Integration von SQL Server in die Entwicklungsumgebung Visual Studio .NET und veranschaulicht, wie SQL Server-Anwendungen sich in das Gesamtsystem einfügen. Solche Anwendungen können natürlich von Ihnen selbst entwickelt werden. Auf der anderen Seite sind hierunter auch Standardanwendungen einzuordnen, wie zum Beispiel die Microsoft Office-Familie insbesondere Office 2007 oder auch der Microsoft SharePoint Portal Server. Wie bereits gesagt, wird sich dieses Buch in der Hauptsache mit den Möglichkeiten des Datenbankmoduls für die Verwaltung und Verarbeitung von relationalen Daten befassen. Es ist einfach so, dass die Datenbankentwicklung sich in der Hauptsache auf genau dieses Modul mit all seiner Komplexität konzentriert. Sie werden aber zum Beispiel auch erfahren, wie Sie Anwendungen mit den Notification Services erstellen und welche Möglichkeiten Ihnen Visual Studio .NET für die Entwicklung von Datenbankobjekten bietet.
9
2 SQL Server 2005-Architektur
Abbildung 2.1 Die Komponenten von SQL Server 2005
In den folgenden Abschnitten erhalten Sie eine Einführung in die einzelnen Bestandteile des SQL Servers.
2.1.1
Das SQL Server-Datenbankmodul
Das Datenbankmodul von SQL Server 2005 repräsentiert ein relationales Datenbankmanagementsystem (DBMS). Als ein solches System ist es verantwortlich für die Regelung aller Zugriffe auf die im System enthaltenen Datenbanken. Die relationalen Datenbanken bestehen hierbei im Wesentlichen aus Tabellen, die zueinander in Beziehung stehen. Das DBMS verarbeitet Anfragen an die Datenbanken entsprechend der vergebenen Zugriffsberechtigungen. Sie können sich das DBMS als eine Art Schutzschild um die eigentlichen Daten vorstellen. Es ist nicht möglich, dieses Schutzschild zu durchdringen. Alle Datenänderungs- und Datenleseprozesse müssen ihre Anforderungen sozusagen an die äußere Hülle richten und werden dann beantwortet oder auch abgelehnt. Durch diese Verfahrensweise werden letztlich die Datenintegrität und der Datenschutz gewährleistet. Das DBMS ist hierbei sogar in der Lage, die Daten bei einem Systemabsturz zu schützen. Die Verarbeitung von Daten wird innerhalb von Transaktionen durchgeführt. Transaktionen stellen sicher, dass alle Datenänderungen konsistent sind und dauerhaft gespeichert werden. Transaktionen sorgen auch dafür, dass mehrere Prozesse gleichzeitig auf identische Datenbereiche zugreifen können, wobei diese Prozesse innerhalb der Transaktionsverarbeitung voneinander isoliert werden. Hierdurch ermöglicht das DBMS eine parallele Datenverarbeitung. Transaktionen sind ein wesentliches Konzept von DBMS, aus diesem Grund ist dieser Thematik im vorliegenden Buch ein eigenes Kapitel (5) gewidmet.
10
2.1 SQL Server 2005-Komponenten Der Zugriff auf die Daten erfolgt über eine standardisierte Schnittstelle, unter Verwendung der Structured Query Language (SQL). Diese Abfragesprache muss jeder Datenbankentwickler beherrschen. Die Verwendung von SQL insbesondere des Microsoftspezifischen Dialektes T-SQL zieht sich daher durch alle Kapitel dieses Buches. Zusätzlich sind dieser Thematik zwei eigene Kapitel (4 und 7) gewidmet. Für die Speicherung aller Informationen, die das DBMS für die Sicherstellung der geschilderten Funktionalität benötigt, verwendet das DBMS ebenfalls Datenbanken. Diese Systemdatenbanken müssen in jeder SQL Server-Instanz existieren, damit das DBMS korrekt arbeiten kann. Das Datenbankmodul bietet die Möglichkeit, über sogenannte Systemkataloge aus diesen Systemdatenbanken Informationen über Server und Datenbanken zu erhalten. In Abschnitt 2.3 werden die Systemdatenbanken näher beleuchtet. In vielen Kapiteln dieses Buches werden Sie dann sehen, wie man die Systemkataloge abfragt. Der SQL Server-Dienst Jede SQL Server-Instanz enthält ein separates DBMS, das in einem eigenen WindowsDienst läuft. Sie können sich die SQL Server-Dienste sowie auch die Dienste der anderen SQL Server-Komponenten im Dienstmanager Ihres Computers ansehen (Abbildung 2.2).
Abbildung 2.2 Der SQL Server-Dienst der Standardinstanz
Der SQL Server Service Broker Der ebenfalls im Datenbankmodul enthaltene Service Broker ermöglicht die asynchrone Verarbeitung von Daten. Hierzu können Anfragen als Ereignisse in eine Warteschlange eingereiht und dann durch eine Anwendung abgearbeitet werden. Dies funktioniert übrigens auch über Servergrenzen hinweg, d. h., die Service Broker unterschiedlicher SQL
11
2 SQL Server 2005-Architektur Server-Instanzen können Nachrichten austauschen. Eine solche nachrichtenbasierte Datenverarbeitung kann zu einer besseren Server-Auslastung bzw. Lastverteilung beitragen. Das Einreihen von Ereignissen in eine Warteschlange benötigt zunächst nur minimale Ressourcen. Eine möglicherweise ressourcenintensive Abarbeitung dieser Ereignisse kann dann zu einem Zeitpunkt geringerer Server-Auslastung erfolgen. SQL Server ist sogar in der Lage, die Abarbeitung der Ereignisse innerhalb von Threads zu erledigen, deren Anzahl ja nach Zustand der Warteschlange automatisch erhöht oder erniedrigt wird. Der Service Broker ist Gegenstand eines eigenen Kapitels (9).
2.1.2
SQL Server Replication Services
Mit den Replication Services können verteilte SQL Server Datenbanken verwaltet werden, die eine dezentrale Verarbeitung von Daten ermöglichen. Über die Mechanismen der Replication Services ist es dann möglich, die dezentralen, an einer Replikation beteiligten Datenbestände wieder zu synchronisieren. Replication Services ermöglichen auch eine einfache Publikation von lokalen Datenänderungen für das Einspielen auf entfernten Servern bzw. Datenbanken. Hierzu müssen die entfernten Systeme nicht ständig verbunden sein, das Übertragen der Datenänderungen kann automatisch erfolgen, sobald ein System wieder online ist. Das klassische Einsatzgebiet hierfür sind sicherlich Architekturen, bei denen die Notebooks von Vertriebsmitarbeitern dezentral arbeiten und nach Verbindungsherstellung zur Zentrale automatisch auf den aktuellen Stand gebracht werden.
2.1.3
SQL Server Notification Services
Auch die Notification Services ermöglichen eine ereignisbasierte Verarbeitung von Daten. Mithilfe der Notification Services können zum Beispiel Datenänderungen überwacht und dann beim Auftreten von bestimmten Änderungen, die zuvor über ein Regelwerk definiert wurden, Benachrichtigungen an Abonnenten verschickt werden. Die Notification Services bieten ein Framework für die Entwicklung solcher Applikationen. Die Erstellung von Anwendungen mit den Notification Services wird in Kapitel 13 behandelt.
2.1.4
SQL Server Reporting Services
Die SQL Server Reporting Services sind eine serverbasierte Plattform, welche die Entwicklung und Verteilung von Berichten ermöglicht. Reporting Services stellen einen Server der mittleren Ebene zur Verfügung, der seinerseits die Internet Information Services benötigt. Über einen speziellen Berichtsmanager können Sie eine Vielzahl von Einstellungen vornehmen. Hierzu gehören zum Beispiel:
12
2.1 SQL Server 2005-Komponenten Berechtigungen. Sie können die Zugriffskontrolle für Elemente des Berichtsservers, wie zum Beispiel die Berichte selber, einstellen. Automatische Ausführungen. Ein Bericht kann automatisch zu einem bestimmten Zeitpunkt ausgeführt werden. Auslieferungen/Verteilung. Berichte können zum Beispiel auf einem Webserver bereitgestellt oder auch per E-Mail verteilt werden. Historie. Berichte können auch in Kopie auf dem Berichtsserver gespeichert werden. Auf diese Weise ist es möglich, einen Berichtsverlauf zu erzeugen. Erstellte Berichte können in einer Vielzahl von Formaten gespeichert und auch verteilt werden. Mögliche Formate sind zum Beispiel PDF, TIF, HTML, XML oder Microsoft Excel. Sie haben außerdem die Möglichkeit, den Benutzern des Systems das Generieren von Ad-hoc-Berichten auf der Basis zuvor definierter Berichtsmodelle zu gestatten.
2.1.5
SQL Server Analysis Services
Mit den Analysis Services können multidimensionale Datenbanken, OnLine Analytical Processing sowie Data Mining-Anwendungen erstellt und aktualisiert werden.
2.1.6
SQL Server Integration Services
Die Integration Services ermöglichen die Entwicklung von Extract-, Transform- und Load Paketen (ETL). Sie unterstützen die Erstellung von Datawarehouse-Anwendungen, für die eine unternehmensweite Datenintegration erforderlich ist. Integration Services Anwendungen sind in sogenannten Paketen organisiert. Innerhalb eines solchen Paketes wird in der Regel eine bestimmte Datentransformation erledigt. Hierbei können Daten aus einer Vielzahl von Datenquellen eingelesen und auch wieder an unterschiedliche Ziele ausgegeben werden. Zwischen dem Einlesen und dem Ausgeben von Daten enthält ein Paket Transformationsschritte, welche die empfangenen Daten letztlich in das erforderliche Zielformat überführen. Ein typischer Einsatzfall ist das Sammeln von Daten aus unterschiedlichen Quellen, wie zum Beispiel Excel-, Access- und Textdokumenten, sowie eventuell auch aus anderen Datenbanken und die Zusammenführung dieser Daten in einer zentralen Datenbank dem Datawarehouse. Die Komponenten Reporting Services, Analysis Services und Integration Services werden auch unter dem Begriff Business Intelligence (BI) eingeordnet. Die Thematik Business Intelligence ist relativ komplex und wird daher in der Literatur auch separat behandelt. Dieses Buch befasst sich nicht mit BI, sondern mit dem Thema Datenbankentwicklung. Falls Sie sich für die BI-Thematik interessieren, so finden Sie in [AZEV06], [SCHR06] oder [SCHU06] entsprechende Ausführungen.
13
2 SQL Server 2005-Architektur
2.1.7
Verwaltungswerkzeuge
Die Verwaltungswerkzeuge bestehen aus einer Handvoll leicht zu bedienender Programme, mit denen Sie die SQL Server-Komponenten konfigurieren und überwachen können. Das mit Abstand wichtigste Werkzeug ist sicherlich das SQL Server Management Studio, das im Verlaufe dieses Buches durchgängig zum Einsatz kommen wird. Eine gründliche Einführung in die Benutzung der einzelnen Werkzeuge sowie deren Verwendungszweck erfolgt im anschließenden Kapitel 3.
2.1.8
Entwicklungswerkzeuge
Für den Datenbankentwickler bietet der SQL Server 2005 die integrierte Entwicklungsumgebung Visual Studio 2005. Die Integration von SQL Server und Visual Studio ermöglicht die komfortable Erstellung von Anwendungen und Bibliotheken. Die integrierten Debugging-Möglichkeiten für T-SQL- und .NET-Quellcode vereinfachen die Fehlersuche ganz erheblich und können dazu beitragen, dass sich die Entwicklungszeiten verkürzen und die Fehlerquoten sinken. In Kapitel 10 werden Sie sehen, wie Visual Studio zur Erstellung von Datenbankobjekten verwendet werden kann. SQL Server kommt mit einer speziellen Edition von Visual Studio daher, dem sogenannten Business Intelligence Development Studio. Mit dieser Edition können Sie wie der Name schon verrät Business Intelligence-Anwendungen erstellen, also Reporting Services-, Analysis Services- und Integration Services-Projekte entwickeln. Das Business Intelligence Development Studio erlaubt jedoch nicht das Übersetzen von C#- oder .NETQuellcode. Für die in Kapitel 10 behandelte Datenbankprogrammierung mit .NET reicht das Business Intelligence Development Studio also nicht aus, hierfür benötigen Sie am besten die Professional Edition von Visual Studio. Im SQL Server Management Studio können Skriptsammlungen also T-SQL-Quellcode in Form von speziellen Projekten organisiert werden. Das Management Studio ermöglicht auch die Analyse von T-SQL-Befehlen in Bezug auf Ausführungsdauer und Ressourcenverbrauch. In gewisser Weise kann das Management Studio somit auch als ein Entwicklungswerkzeug angesehen werden.
2.1.9
Der SQL Server Agent
Der SQL Server Agent ist ein eigener Windows-Dienst. Jede SQL Server-Instanz bringt ihren eigenen Agenten mit, eine Ausnahme bildet hier lediglich die Express-Edition, für die kein Agent zur Verfügung steht. Der SQL Server Agent kann Ereignisse überwachen und beim Auftreten bestimmter Ereignisse Aktionen auslösen. Typischerweise wird ein zu überwachendes Ereignis das Erreichen eines bestimmten Zeitpunktes sein, an dem dann zum Beispiel eine (nächtliche) Datensicherung durchgeführt wird. Der Agent kann aber auch andere Ereignisse, wie zum Beispiel die Auslastung von Datenbanken oder das Windows-Ereignisprotokoll, überwachen und beim Überschreiten einer bestimmten Schwelle
14
2.2 SQL Server-Editionen bzw. dem Eintragen eines definierten Fehlers in das Ereignisprotokoll eine E-MailNachricht an bestimmte Personen, sogenannte Operatoren, verschicken. Der SQL Server Agent koordiniert auch die Replikation. In Kapitel 6 werden Sie sehen, wie der SQL Server Agent zum Erstellen von automatisierten Datensicherungen verwendet werden kann.
2.2
SQL Server-Editionen Die SQL Server-Produktfamilie ist in unterschiedlichen Editionen verfügbar. Diese Editionen unterscheiden sich bezüglich des angebotenen Leistungsumfangs und natürlich auch im Preis. Sie können aus den folgenden Editionen die für Sie passende auswählen: SQL Server Enterprise Edition Die Enterprise Edition bietet den kompletten Leistungsumfang der gesamten in SQL Server 2005 verfügbaren Features. SQL Server Standard Edition Die Standard Edition enthält gegenüber der Enterprise Edition Einschränkungen in Bezug auf die Skalierbarkeit, Verfügbarkeit und Business Intelligence. Ebenfalls nicht unterstützt wird die horizontale Partitionierung von Tabellen und Indizes. SQL Server Workgroup Edition Die Workgroup Edition enthält zusätzliche Einschränkungen gegenüber der Standard Edition. Die Notification Services stehen in dieser Edition nicht zur Verfügung. Ebenfalls nicht möglich ist die Erstellung von http-Endpunkten, also die Veröffentlichung von Web Services, wie dies in Kapitel 11 geschildert wird. SQL Server Developer Edition Die Developer Edition bietet den gleichen Leistungsumfang wie die Enterprise Edition. Der Einsatz dieser Edition ist aber nur für die Entwicklung gestattet. Das Lizenzmodell verbietet die Benutzung der Developer Edtion in Produktionsumgebungen. SQL Server Express Edition Die Express Edition können Sie lizenzkostenfrei benutzen. Diese Version hat gegenüber der Workgroup Edition weitere Einschränkungen. Insbesondere enthält diese SQL Server Edition nicht den SQL Server Agent. Außerdem ist die Größe einer einzelnen Datenbank auf 4 Gigabyte begrenzt.
15
2 SQL Server 2005-Architektur Der Leistungsumfang für die einzelnen Editionen kann sich im Laufe der Zeit allerdings verändern. Das ist vorrangig abhängig von der Lizenzpolitik, die ja durch eine Vielzahl von Faktoren beeinflusst werden kann. Dies betrifft insbesondere vielleicht dank MySQL die Express Edition, für die zum Beispiel ursprünglich weder das SQL Server Management Studio noch die Reporting Services verfügbar waren. Inzwischen können Sie für diese Edition das Service Pack 1 installieren, in dem nun sowohl die Reporting Services als auch das SQL Server Management Studio Express (ein spezielles Management Studio für die Express Edition) enthalten sind. Ich kann Ihnen also nur dazu raten, dass Sie sich die jeweils aktuellen und detaillierten Informationen zum Leistungsumfang der einzelnen Editionen von der Microsoft-Website holen. Dort finden Sie eine entsprechende Übersicht unter: http://www.microsoft.com/germany/sql/editionen/default.mspx
2.3
Systemdatenbanken Wie bereits weiter oben erwähnt, benutzt das Datenbankmodul für die permanente Speicherung von benötigten Informationen die sogenannten Systemdatenbanken der SQL Server-Instanz. Diese Systemdatenbanken werden für eine einwandfreie Funktion der SQL Server-Instanz benötigt, bei fehlenden oder fehlerhaften Systemdatenbanken ist eine uneingeschränkt korrekte Funktionalität nicht gewährleistet. SQL Server benötigt die folgenden Systemdatenbanken, die bei der Installation automatisch angelegt werden:
2.3.1
Die master-Datenbank
Die Datenbank master ist die zentrale Datenbank für die SQL Server-Instanz. In der master-Datenbank sind Informationen über alle im Server enthaltenen Objekte abgelegt. Hierzu zählen zum Beispiel Anmeldeinformationen, Sicherheitseinstellungen und Informationen zu Benutzerdatenbanken. Ohne diese Datenbank kann die SQL Server-Instanz nicht gestartet werden.
2.3.2
Die model-Datenbank
Die model-Datenbank ist eine Datenbank, die als Vorlage für die Erstellung neuer Datenbanken benutzt wird. Jede Datenbank, die in der SQL Server-Instanz angelegt wird, verwendet die model-Datenbank als Muster. Sie können die Datenbank model also so konfigurieren, dass jede erzeugte Datenbank bereits definierte Standardobjekte enthält. Typischerweise werden dies Benutzer oder Datentypen aber vielleicht auch bestimmte Tabellen oder gespeicherte Prozeduren sein.
16
2.3 Systemdatenbanken
2.3.3
Die msdb-Datenbank
In dieser Datenbank speichert vor allem der SQL Server Agent seine Objekte und Informationen. Hierzu zählen zum Beispiel Aufträge mit Ausführungsterminen, der Verlauf der Auftragsausführung und Benachrichtigungen. Die msdb enthält darüber hinaus auch Informationen über den Sicherungs- und Wiederherstellungsverlauf von Datenbanken.
2.3.4
Die tempdb-Datenbank
Die tempdb ist die Datenbank, in welcher der SQL Server temporäre Informationen ablegt. Sie können diese Datenbank mit dem TEMP-Verzeichnis von Windows vergleichen, in dem das Betriebssystem temporäre Informationen ablegt. Beim Start einer SQL ServerInstanz wird die tempdb stets mit der Standardgröße neu erzeugt. Während der Ausführung der Instanz wird die Größe der Datenbank dann dynamisch an die Erfordernisse angepasst. Die tempdb existiert wie alle anderen Systemdatenbanken auch pro SQL ServerInstanz einmal. Alle Prozesse, die auf die Instanz von SQL Server zugreifen, müssen sich also die tempdb teilen. Wenn nun diese Prozesse viele temporäre Objekte benötigen, so kann durch die gemeinsame Benutzung der Datenbank tempdb das Laufzeitverhalten leiden. Im Kapitel 5 werden wir hierauf noch einmal zurückkommen.
2.3.5
Weitere Datenbanken
Die einzelnen Komponenten von SQL Server nutzen ihrerseits ebenfalls die Möglichkeit, ihre Daten in einer Datenbank abzulegen. Außerdem können Sie bei der Installation der SQL Server-Instanz Beispieldatenbanken installieren. Diese Beispieldatenbanken sind die Grundlage für die meisten Codebeispiele in der SQL Server-Dokumentation und viele andere Diskussionen in Foren oder der Literatur zu SQL Server. ReportServer Die ReportServer-Datenbank ist die zentrale Datenbank für die SQL Server Reporting Services. Eine Reporting Services-Instanz speichert alle Informationen in dieser Datenbank. Hierzu zählen zum Beispiel Zugriffsberechtigungen, Berichtsdefinitionen, gespeicherte Berichte, Abonnements für die Berichtserstellung usw. ReportServer ist der Standardname für diese Datenbank, die Sie allerdings bei der Installation der Reporting Services auch anders benennen können. ReportServerTempDB Dies ist eine temporäre Datenbank für die SQL Server Reporting Services, ähnlich der tempdb für die SQL Server-Instanz. Diese Datenbank kann ebenfalls bei der Installation der Reporting Services umbenannt werden.
17
2 SQL Server 2005-Architektur AdventureWorks Die Datenbank AdventureWorks wurde bereits im einleitenden Kapitel 1 erwähnt. Diese Datenbank können Sie bei der SQL Server-Installation in einem separaten Schritt installieren. Für eine korrekte Funktion der SQL Server-Instanz wird die Datenbank AdventureWorks nicht benötigt. Auf einem Test- oder Entwicklungsserver ist die Installation dieser Datenbank allerdings zu empfehlen. Es sei an dieser Stelle nochmals daran erinnert, dass diese Datenbank die Grundlage für nahezu alle in diesem Buch verwendeten Beispiele bildet. AdventureWorksDW Auch die Datenbank AdventureWorksDW ist eine Beispieldatenbank, die Sie bei der SQL Server-Installation separat installieren müssen. Diese Datenbank ist die Basis für viele Beispiele zu den Analysis Services das DW steht hier für Datawarehouse. Im Prinzip finden Sie in der AdventureWorksDW eine stark denormalisierte Form der Datenbank AdventureWorks. Die AdventureWorksDW wird für dieses Buch nicht benötigt. Datenbanken für Notification Services-Instanzen Wenn Sie Notification Services Anwendungen erstellen, so wird jede dieser Anwendungen in einer Instanz von Notification Services ausgeführt. Eine solche Instanz kann mehrere Notification Services-Anwendungen ausführen. Jede Instanz von Notification Services besitzt eine eigene Datenbank, in der zum Beispiel Informationen über existierende Abonnenten und die Notification Services-Anwendungen, die in dieser Instanz ausgeführt werden, gespeichert werden. Datenbanken für Notification Services-Anwendungen Die einzelnen Notification Services-Anwendungen speichern ihre Informationen ebenfalls in einer zugehörigen Datenbank. Hierbei benutzt jede Anwendung ihre eigene Datenbank. In dieser Datenbank finden Sie zum Beispiel Informationen zu aufgetretenen Ereignissen und Abonnements.
2.4
Die Struktur einer SQL Server-Datenbank In diesem Abschnitt wird eine kurze Einführung in die generelle Struktur einer Datenbank im SQL Server präsentiert. Diese Einführung ist nicht umfassend, das Ziel ist lediglich, eine Grundlage für die weiteren Kapitel dieses Buches zu schaffen.
18
2.4 Die Struktur einer SQL Server-Datenbank
2.4.1
Datenbanken
Eine SQL Server-Datenbank ist aus logischer Sicht nichts anderes als einfach eine Sammlung von Datenbankobjekten, die in den folgenden Abschnitten gleich näher erläutert werden. Die SQL Server-Instanz speichert eine Datenbank in diversen Dateien des Betriebssystems dies ist sozusagen die physikalische Sicht auf die Datenbank. Dabei werden mindestens zwei Dateien benötigt, eine für die eigentlichen Daten hierin werden die erzeugten Datenbankobjekte abgelegt und eine zweite für die sogenannten Protokolleinträge. Wozu die Protokolleinträge nützlich sind, wird in Kapitel 5, das sich mit der Transaktionsverarbeitung des SQL Servers auseinandersetzt, behandelt. An dieser Stelle soll zunächst nur kurz ein Überblick über die zur Verfügung stehenden Datenbankobjekte gegeben werden.
2.4.2
Datenbankobjekte
Eine Datenbank ist natürlich nur von Nutzen, wenn sie in der Lage ist, Informationen zu speichern und diese Informationen auch wieder abgerufen werden können. Die Organisation dieser Datenspeicherung und des Abfragens von Informationen wird durch diverse Datenbankobjekte ermöglicht, die in diesem Abschnitt kurz vorgestellt werden sollen. In Kapitel 4 werden Sie dann erfahren, wie man Datenbankobjekte anlegt, ändert und auch löscht. Tabelle Das mit Abstand wichtigste Datenbankobjekt ist sicherlich die Tabelle, die letztlich die vom Benutzer eingegebenen Informationen speichert und über Abfragen auch ausgibt. Der Name Tabelle ist hierbei tatsächlich wörtlich zu nehmen, eine Datenbanktabelle besteht in der Tat aus Zeilen und Spalten. Für jede Tabelle muss bei der Erzeugung eine Struktur spezifiziert werden. Dadurch wird festgelegt, wie die einzelnen Zeilen der Tabelle aufgebaut sein müssen, also zum Beispiel wie viele Spalten die Tabelle enthält und wie die Datentypen dieser Spalten sind. Für die einzelnen Spalten können auch Einschränkungen angegeben werden, die insbesondere den Wertebereich einer Spalte begrenzen können. Jede Tabelle sollte außerdem über einen sogenannten Primärschlüssel verfügen, der eine Zeile innerhalb der Tabelle eindeutig identifiziert. Für eine Tabelle, die Kundendaten enthält, wird zum Beispiel die Kundennummer als Primärschlüssel dienen. Sicht Eine Sicht können Sie sich einfach wie eine virtuelle Tabelle vorstellen. Wie der Name schon andeutet, repräsentiert eine Sicht einen bestimmten Blick auf die eigentlichen Tabellendaten, die der Sicht zugrunde liegen. Durch die Sicht können Sie also aus Tabellen nur bestimmte Spalten oder Zeilen herausfiltern sowie Spalten im Ergebnis neu berechnen und/oder umbenennen.
19
2 SQL Server 2005-Architektur Schema Ein Schema ist letztlich nur ein Container, der andere Datenbankobjekte aufnehmen kann. Schemas werden vorrangig verwendet, um die Verwaltung von Datenbankobjekten und den Zugriff auf die im Schema enthaltenen Datenbankobjekte übersichtlicher zu gestalten. Gespeicherte Prozedur Gespeicherte Prozeduren ermöglichen die Programmierung des SQL Servers. Eine Prozedur enthält T-SQL Code, der einfach unter einem Namen, dem Prozedurnamen, in der Datenbank abgelegt werden kann. Eine Prozedur kann auch Parameter empfangen und verarbeiten. Benutzerdefinierte Funktion Auch benutzerdefinierte Funktionen ermöglichen die SQL Server-Programmierung. Wie Prozeduren, so können auch Funktionen Parameter entgegennehmen. SQL Server unterscheidet prinzipiell zwei Typen von benutzerdefinierten Funktionen: Skalarwertfunktionen. Dieser Funktionstyp gibt einen einfachen Parameter zurück. Tabellenwertfunktionen. Eine Tabellenwertfunktion gibt eine Tabelle als Ergebnis zurück. Trigger Trigger sind ebenfalls gespeicherte Prozeduren. Nur können Sie diese Prozeduren nicht direkt aufrufen. Die Ausführung eines Triggers ist immer an ein bestimmtes Ereignis gebunden, daher auch der Name. Beim Eintreten dieses Ereignisses wird der Trigger dann gefeuert. Trigger können zum Beispiel für Datenänderungsoperationen an Tabellen angelegt werden. So ist es möglich, immer dann, wenn Zeilen zu einer Tabelle hinzugefügt werden, den im Trigger enthaltenen T-SQL-Code auszuführen, der dann zum Beispiel zusätzliche Gültigkeitsprüfungen vornehmen oder Berechnungen durchführen kann. Benutzerdefinierter Datentyp Benutzerdefinierte Datentypen sind nichts anderes als Synonyme für SQL Server-eigene Datentypen. Durch die Verwendung benutzerdefinierter Datentypen können Sie für SQL Server-Datentypen Bezeichnungen aus Ihrer Anwendungsdomäne vergeben. Hierbei haben Sie auch die Möglichkeit, zusätzliche Attribute, wie zum Beispiel Genauigkeit, anzugeben. Dadurch könnten Sie in etwa einen eigenen Datentyp für E-Mail hinzufügen und spezifizieren, dass dieser eine maximale Länge von 80 Zeichen haben darf. Index Indizes können für Tabellen oder auch Sichten erzeugt werden. Über Indizes kann der Datenzugriff enorm beschleunigt werden, da bei der Verwendung von Indizes statt der an-
20
2.5 Zusammenfassung sonsten erforderlichen sequenziellen Suche die binäre Suche verwendet werden kann. In Kapitel 12 werden wir hierauf noch einmal zurückkommen. Benutzer Datenbankbenutzer ermöglichen die Verwaltung der Datenbanksicherheit. Insbesondere kann Benutzern der Zugriff auf Datenbankobjekte verwehrt oder gestattet werden. In Kapitel 6 wird darauf genauer eingegangen. Rolle Eine Rolle innerhalb einer Datenbank ist einfach ein Container, der Benutzer aufnehmen kann. Rollen können ebenfalls Berechtigungen erteilt oder entzogen werden, was natürlich entsprechende Auswirkungen auf die in der Rolle enthaltenen Benutzer hat. Dadurch erleichtern Rollen die Verwaltung von Zugriffsberechtigungen,
2.5
Zusammenfassung In diesem Kapitel haben Sie die Architektur des SQL Servers kennengelernt. Sie wissen nun, aus welchen Komponenten sich der SQL Server zusammensetzt und welche Rolle die einzelnen Komponenten übernehmen bzw. welche Aufgaben diese Komponenten erfüllen. Sie haben gesehen, welchen unterschiedlichen Leistungsumfang die verschiedenen SQL Server-Editionen bieten. Außerdem kennen Sie die Systemdatenbanken des SQL Servers. Zum Abschluss dieses Kapitels haben Sie schließlich noch eine Einführung in den generellen Aufbau von SQL Server-Datenbanken erhalten und erfahren, welche Datenbankobjekte in Datenbanken enthalten sind und welche Funktion diese Datenbankobjekte erfüllen.
21
3 SQL Server-Verwaltungswerkzeuge Bei der Installation des SQL Servers wird in der Programmgruppe Microsoft SQL Server 2005 eine Reihe von Programmen abgelegt, mit denen eine komfortable Verwaltung aller SQL Server-Komponenten möglich ist. Zusätzlich stehen Ihnen auch Werkzeuge zur Verfügung, mit denen Sie den SQL Server überwachen und konfigurieren sowie Business Intelligence-Projekte entwickeln können. Hierfür können Sie die in der folgenden Übersicht enthaltenen und kurz beschriebenen Instrumente verwenden: SQL Server-Konfigurations-Manager. Ein Werkzeug für die Konfiguration der SQL Server-Dienste und Netzwerk-Optionen. SQL Server-Oberflächenkonfiguration. Ein Programm zum Aktivieren oder Deaktivieren von SQL Server Features. SQL Server Management Studio. Die Verwaltungszentrale für alle SQL ServerKomponenten. Der enthaltene Abfrageeditor ermöglicht das komfortable Erstellen und Testen von T-SQL-Abfragen. Business Intelligence Development Studio. Die Entwicklungsumgebung für Business Intelligence-Projekte. Datenbankmodul-Optimierungsratgeber. Ein Ratgeber, der Abfragen analysiert und Vorschläge für physikalische Datenbankmodifikationen unterbereitet. SQL Server Profiler. Ein Programm zum Überwachen und Protokollieren der SQL Server-Aktivitäten. Sqlcmd. Ein Kommandozeilenprogramm für die Ausführung von Abfragen. SQL Server-Online-Dokumentation. Die umfassende Dokumentation für alle SQL Server-Komponenten. Nicht alle dieser Instrumente sind für den Datenbankentwickler gleichermaßen interessant, einige der genannten Programme werden zum Beispiel hauptsächlich für die SQL ServerAdministration benötigt. Schaden kann es jedoch nicht, wenn auch Sie als Datenbankentwickler die zur Verfügung stehenden Werkzeuge zumindest kennen wenn auch vielleicht nicht zu 100 % beherrschen. Im Rest dieses Kapitels wird daher auch für einige der Programme aus der obigen Auflistung nur eine Einführung erfolgen. Der Fokus in diesem Ka-
23
3 SQL Server-Verwaltungswerkzeuge pitel liegt eindeutig auf dem SQL Server Management Studio. Das SQL Server Management Studio ist unzweifelhaft das Instrument, das als das vorrangige Arbeitsmittel für Datenbankentwickler (und SQL Server-Administratoren) angesehen werden muss.
3.1
SQL Server-Konfigurations-Manager Den SQL Server-Konfigurations-Manager wird über das Startmenü Microsoft SQL Server 2005/Konfigurationstools/SQL Server-Konfigurations-Manager aufgerufen. Mit diesem Programm können Sie die SQL Server-Dienste verwalten und die Netzwerk-Optionen für den Server und die Clients einstellen.
Abbildung 3.1 Der SQL Server-Konfigurations-Manager
In sehen Sie die Windows-Dienste für die SQL Server-Installation, für die Sie mit dem Konfigurations-Manager genau dieselben Einstellungen vornehmen können, wie dies auch über die Systemsteuerung möglich ist. So können für die einzelnen SQL Server-Dienste zum Beispiel Startoptionen und Anmeldeinformationen spezifiziert werden. Die Abbildung zeigt auch einen Dienst NS$HAL9000-NSI_1. Dies ist der Dienst einer Notification Services-Instanz, die in Kapitel 13 entwickelt wird. Über die Auswahl von SQL Server 2005-Netzwerkkonfiguration kann angegeben werden, welche Netzwerkprotokolle der SQL Server unterstützen soll. Im Zweig SQL Native Client-Konfiguration haben Sie die Möglichkeit festzulegen, welche Netzwerkoptionen dem SQL Server-Client zur Verfügung stehen.
24
3.2 SQL Server-Oberflächenkonfiguration
3.2
SQL Server-Oberflächenkonfiguration Die SQL Server-Oberflächenkonfiguration verwenden Sie zum Aktivieren oder Deaktivieren von SQL Server Features. Die Deaktivierung von Features wird auch als Oberflächenreduzierung bezeichnet. Da nach der SQL Server-Installation aus Sicherheitsgründen eine Reihe von Features deaktiviert ist, werden Sie die Oberflächenkonfiguration sehr wahrscheinlich benötigen, um die Optionen Ihrer Installation im Nachhinein anzupassen. Zu den deaktivierten Features zählen zum Beispiel: Die Erlaubnis von Remoteverbindungen (Developer Edition). Diese Edition lässt zunächst nur lokale Verbindungen zu. CLR-Integration. Dieses Feature, das den Inhalt von Kapitel 10 dieses Buches bestimmt, ist ebenfalls nach der Installation nicht verfügbar. Die SQL Server-Oberflächenkonfiguration starten Sie ebenfalls über das Startmenü, unter Microsoft SQL Server 2005/Konfigurationstools/SQL Server-Oberflächenkonfiguration. Nach dem Start können Sie zwischen den Optionen Oberflächenkonfiguration für Dienste und Verbindungen und der Oberflächenkonfiguration für Features wählen (Abbildung 3.2).
Abbildung 3.2 SQL Server-Oberflächenkonfiguration
In diesem Fenster haben Sie auch die Möglichkeit, die Oberflächenkonfiguration für einen entfernten SQL Server durchführen zu können, wofür Sie dann natürlich auch die entsprechenden Berechtigungen haben müssen.
25
3 SQL Server-Verwaltungswerkzeuge Bei der Auswahl der Konfiguration für Dienste und Verbindungen wird ein Fenster geöffnet, in dem Sie die SQL Server-Dienste konfigurieren sowie die Optionen für Remoteverbindungen einstellen können (Abbildung 3.3).
Abbildung 3.3 Oberflächenkonfiguration für Dienste und Verbindungen
In Abbildung 3.3 sehen Sie, wie für das Datenbankmodul der lokalen Standardinstanz von SQL Server Remoteverbindungen über TCP/IP zugelassen werden. Auch hier haben Sie im Übrigen die Möglichkeit, die Startoptionen für die einzelnen Dienste zu konfigurieren, genauso wie dies auch mit dem SQL Server-KonfigurationsManager möglich ist. In der Liste der Dienste taucht wiederum der Dienst für die existierende Notification Services-Instanz, HAL9000-NSI_1, auf. Wenn Sie im Startfenster den Verweis für die Oberflächenkonfiguration für Features auswählen, öffnet sich ein anderes Fenster, ähnlich dem in Abbildung 3.4 gezeigten. In der Abbildung sehen Sie, wie ein http-Endpunkt, der einen Web Service zur Verfügung stellt, gestartet oder beendet werden kann. Der dort zu sehende Endpunkt AdWrksServicePoint wird in Kapitel 11 erstellt.
26
3.3 SQL Server Management Studio
Abbildung 3.4 Oberflächenkonfiguration für Features
3.3
SQL Server Management Studio Das SQL Server Management Studio ist das zentrale Instrument zur Konfiguration und Verwaltung aller SQL Server-Komponenten zuzusagen die Kommandozentrale von SQL Server. Dabei können nicht nur die SQL Server 2005-Komponenten, sondern auch Datenbankmodule der früheren Versionen 7.0 und 2000 mit dem Management Studio administriert werden. Für diese Module steht allerdings nur eine begrenzte Funktionalität zur Verfügung. Das Management Studio starten Sie über das Startmenü Microsoft SQL Server 2005/SQL Server Management Studio.
3.3.1
Start und Anmeldung
Nach dem Start des Management Studios werden Sie zuerst aufgefordert, eine Verbindung zu einer SQL Server-Komponente herzustellen. Hierzu wird ein Anmeldefenster geöffnet, in dem Sie diese Information eingeben können. Abbildung 3.5 zeigt dieses Fenster, wobei als Authentifizierungsmodus die Windows-Authentifizierung ausgewählt ist. Dies bedeutet, dass für die Anmeldung an die in der Combobox Servertyp ausgewählte SQL ServerKomponente derjenige Benutzer verwendet wird, der sich an das Betriebssystem angemeldet hat.
27
3 SQL Server-Verwaltungswerkzeuge
Abbildung 3.5 Anmeldung an das SQL Server Management Studio mit Windows-Authentifizierung
Für den Fall, dass Sie im Moment nicht genau wissen, was mit dem Authentifizierungsmode gemeint ist, haben Sie bitte einfach noch etwas Geduld. In Kapitel 6 wird hierauf näher eingegangen. Sie können für die Anmeldung auch die SQL Server-eigene Authentifizierung verwenden. In diesem Fall müssen Sie einen Benutzer und ein Kennwort für die Anmeldung am SQL Server angeben. Ein Beispiel für ein solches Anmeldefenster zeigt Abbildung 3.6.
Abbildung 3.6 Anmeldung an das SQL Server Management Studio mit SQL Server-Authentifizierung
Das SQL Server Management Studio kann alle SQL Server-Komponenten verwalten. In der Combobox Servertyp können Sie daher die Komponente auswählen, mit der Sie sich verbinden möchten (Abbildung 3.7).
Abbildung 3.7 Auswahl einer Komponente für die Anmeldung
28
3.3 SQL Server Management Studio Beachten Sie hierbei bitte, dass der Dienst für die entsprechende Komponente gestartet sein muss, ansonsten erhalten Sie beim Verbindungsversuch einen Fehler. In diesem Buch werden im Management Studio grundsätzlich nur Verbindungen zum SQL Server-Datenbankmodul hergestellt. Dies ist auch die Voreinstellung für die Combobox Servertyp. Falls Sie SQL Server 2005 auf Ihrem Computer installiert haben und auf diesem Computer auch lokale Windows-Administratorprivilegien besitzen, so können Sie im Anmeldefenster auch die Voreinstellung Windows-Authentifizierung für den Authentifizierungsmodus übernehmen. Im Feld Servername ist jeweils der Name des Servers voreingestellt, zu dem zuletzt eine Verbindung hergestellt wurde. Möchten Sie eine Verbindung zur Standardinstanz auf Ihrem (lokalen) Computer herstellen, so können Sie hier einfach localhost, (local) oder auch . (einen Punkt) angeben. Wenn Sie einmal erfolgreich eine Verbindung mit der Windows-Authentifizierung hergestellt haben, wird das Anmeldefenster beim nächsten Start von SQL Server Management Studio schon Voreinstellungen enthalten, die es Ihnen erlauben, einfach nur über den Button Verbinden die Verbindung herzustellen. Nach dem erfolgreichen Verbinden zum Datenbankmodul öffnet sich dann das Hauptfenster des SQL Server Management Studios (Abbildung 3.8).
Abbildung 3.8 Das Hauptfenster des SQL Server Management Studios
In den folgenden Abschnitten werden die einzelnen Bereiche des Hauptfensters nun näher erklärt.
29
3 SQL Server-Verwaltungswerkzeuge
3.3.2
Bereiche des Hauptfensters
Ein Hinweis vorweg: Sofern die im Folgenden beschriebenen Fenster in Ihrem Management Studio nicht sichtbar sind, können Sie diese Fenster über das Menü Ansicht öffnen. Registrierte Server Links oben wird das Fenster mit den im Management Studio registrierten Servern angezeigt (Abbildung 3.9). Über die Menüleiste dieses Fensters können Sie weitere Server registrieren. Die unterstützten Servertypen sind: SQL Server-Datenbankmodul SQL Server Analysis Services SQL Server Reporting Services SQL Server Mobile. Dies ist eine spezielle Version des SQL Servers für PDA und Pocket PC SQL Server Integration Services Innerhalb dieses Buches werden nur Verbindungen zum SQL Server-Datenbankmodul benötigt.
Abbildung 3.9 Registrierte Server
Objekt-Explorer Über den Objekt-Explorer können Sie Eigenschaften von SQL Server-Objekten abfragen und verändern. Es ist auch möglich, Objekte hinzuzufügen oder zu löschen. Der ObjektExplorer zeigt die Objekthierarchie des SQL Servers, so wie Sie dies bereits vom Windows Explorer und dem Dateisystem kennen. Oberster Knoten ist hierbei der Server selber, für den im Knoten die Information über den Servernamen, die Version des Servers und den angemeldeten Benutzer ausgegeben wird. In Abbildung 3.8 können Sie im Ordner Datenbanken alle Datenbanken des Servers HAL9000 sehen. Der Ordner Systemdatenbanken ist geöffnet, wodurch Sie auch die im vorigen Kapitel erwähnten Systemdatenbanken des Servers erkennen können.
30
3.3 SQL Server Management Studio Enthalten sind weiterhin die beiden Beispieldatenbanken AdventureWorks und AdventureWorksDW sowie die von den SQL Server Reporting Services verwendeten Datenbanken ReportServer und ReportServerTempDB. Schließlich sehen Sie in der Abbildung noch die Datenbanken für eine existierende Notification Services-Instanz (HAL9000-NSI_1NSMain) und die in dieser Instanz ausgeführte Notification ServicesAnwendung (HAL9000-NSI_1KonzertTermine). Sie können auch erkennen, dass die Datenbank Book sich im Offline-Status befindet, also derzeit keine Verbindungen zulässt. Im Ordner Sicherheit können vor allem SQL Server-Anmeldungen verwaltet werden. Hierzu werden Sie in Kapitel 6 mehr erfahren. Der Ordner Serverobjekte enthält Sicherungsmedien, SQL Server-Endpunkte (zum Beispiel HTTP-Endpunkte für Web Services, siehe Kapitel 11), Informationen für Verbindungen zu anderen Servern und Trigger, die beim Auftreten von Server-Ereignissen gefeuert werden. (Solche Trigger werden in Kapitel 7 entworfen.) Im Zweig Replikation können Publikationen und Abonnements konfiguriert und überwacht werden. Replikation wird in diesem Buch nicht behandelt. In der Mappe Verwaltung finden Sie zum Beispiel die SQL Server-Protokolle, in denen Information über SQL Server-Ereignisse abgelegt werden. In diesem Ordner hat der Administrator die Möglichkeit, SQL Server-Wartungsaufträge zu konfigurieren und die Aktivitäten der aktuellen SQL Server-Verbindungen zu überwachen. Der Notification Services-Ordner enthält einen Eintrag für jede konfigurierte Notification Services-Instanz. In Abbildung 3.8 ist eine solche Instanz mit dem Namen HAL9000-NSI_1 vorhanden. Der Zweig SQL Server-Agent ermöglicht die Konfiguration des Agenten und seiner zugehörigen Komponenten. Hier können Operatoren, Aufträge und Warnungen konfiguriert und kontrolliert werden. In Kapitel 6 wird der Agent verwendet, um zyklische Datensicherungen durchzuführen. Filter verwenden Filter sind eine nützliche Möglichkeit, um Objekte bestimmten Namens zu suchen bzw. die Liste der angezeigten Objekte zu reduzieren. Wenn Sie zum Beispiel den Ordner Systemsichten in der Datenbank master öffnen, so werden einige hundert Elemente angezeigt (Abbildung 3.10).
31
3 SQL Server-Verwaltungswerkzeuge
Abbildung 3.10 Einen Filter setzen
In einem solchen Fall kann es sehr hilfreich sein, wenn Sie das Ergebnis einschränken. Hierzu können Sie einfach auf das Filter-Symbol in der Menüleiste des Objekt-Explorers klicken (Abbildung 3.10), nachdem Sie den Ordner ausgewählt haben, in dem Sie den Filter anwenden möchten. Dadurch wird ein Fenster geöffnet, in dem Sie die Einstellungen für den Filter setzen können. Um zum Beispiel nur alle Objekte anzuzeigen, in deren Name die Zeichenkette memory vorkommt, geben Sie für die Eigenschaft Name den Operator Enthält an, und tragen Sie bei Wert memory ein, so wie in Abbildung 3.11 gezeigt.
Abbildung 3.11 Filtereinstellungen konfigurieren
32
3.3 SQL Server Management Studio Nach dem Bestätigen der vorgenommenen Einstellungen wird die Liste der angezeigten Objekte dann entsprechend der Filterung reduziert. Sie können im Objekt-Explorer Verbindungen zu weiteren Servern hinzufügen. Auch hier gibt es wieder die Möglichkeit, eine SQL Server-Komponente auszuwählen (Abbildung 3.12).
Abbildung 3.12 Eine Verbindung zum Objekt-Explorer hinzufügen
Nach der Angabe der Verbindungsinformation wird der ausgewählte Server dann ebenfalls im Objekt-Explorer angezeigt, und Sie können die Objekte dieses Servers überwachen und konfigurieren.
Abbildung 3.13 Das Eigenschaftsfenster der Datenbank AdventureWorks
33
3 SQL Server-Verwaltungswerkzeuge Objekt-Eigenschaften verwalten Für jedes dargestellte Objekt kann über das Kontextmenü ein Eigenschaftsfenster geöffnet werden, das sowohl Informationen zu diesem Objekt anzeigt als auch die Änderung von Eigenschaften gestattet. Um zum Beispiel Informationen zur Datenbank AdventureWorks zu erhalten, öffnen Sie das Kontextmenü für diese Datenbank, und wählen Sie den Eintrag Eigenschaften. Es öffnet sich ein Fenster, in dem Sie die Eigenschaften der Datenbank sehen und verändern können (Abbildung 3.13). Skripterstellung Falls Sie Eigenschaften verändern, so können Sie diese Änderungen durch den Button OK sofort übernehmen. Alle Eigenschaftsfenster bieten aber auch die Möglichkeit, dass Sie die vorgenommenen Änderungen nicht unmittelbar ausführen, sondern in einem T-SQL-Skript ablegen. Dieses Skript enthält die entsprechenden Anweisungen, um die Änderungen durchzuführen, und kann zu einem späteren Zeitpunkt ausgeführt oder auch für die wiederholte Ausführung gespeichert werden. Um ein solches Skript zu erstellen, bieten die Eigenschaftsfenster in der Menüleiste den Button Skript. Beim Klick auf diesen Button öffnet sich ein Untermenü, aus dem Sie Optionen für die Skripterzeugung auswählen können (Abbildung 3.14).
Abbildung 3.14 Skripterstellung für durchgeführte Änderungen
Das Menü bietet die folgenden Möglichkeiten zur Skripterzeugung: Skript für Aktion in Fenster Neue Abfrage schreiben. Hierbei wird im Abfrageeditor ein neues Fenster mit dem T-SQL-Skript geöffnet. Skript für Aktion in Datei schreiben. Sie werden zunächst aufgefordert, einen Dateinamen einzugeben. In dieser Datei wird das Skript abgelegt. Skript für Aktion in Zwischenablage schreiben. Das Skript wird in die Zwischenablage geschrieben und kann von dort in beliebige Dokumente eingefügt werden.
34
3.3 SQL Server Management Studio Skript für Aktion in Auftrag schreiben. Es wird ein SQL Server-Agent-Auftrag erstellt, der die Ausführung des Skriptes als Auftragsschritt beinhaltet. Für diesen Auftrag können Sie einen Zeitplan erstellen, um die periodische Ausführung des Skriptes anzustoßen. In Kapitel 6 werden Sie erfahren, wie dies funktioniert. Ich möchte Ihnen an dieser Stelle dazu raten, dass Sie von der Möglichkeit der Skripterstellung zu Beginn Ihrer Arbeit mit SQL Server regen Gebrauch machen. Es gibt kaum eine bessere Möglichkeit, sich mit T-SQL vertraut zu machen. Wirklich alle Eigenschaftsfenster bieten die Möglichkeit zur Skripterzeugung. Sie können dadurch Änderungen sozusagen nur antäuschen, indem Sie Eigenschaften verändern und dann aber die Änderungen nicht ausführen, sondern sich einfach nur ein Skript erstellen lassen. Dieses Skript mit den fiktiven Änderungen bietet wirklich eine hervorragende Möglichkeit, T-SQL zu lernen. Hinzu kommt, dass Sie ebenfalls Skripte für alle Datenbankobjekte wie Tabellen, Sichten, gespeicherte Prozeduren und auch für die Datenbanken selber erstellen können. Abbildung 3.15 zeigt dies am Beispiel einer Tabelle, für die ein Skript zur Erzeugung in einem neuen Abfrage-Editorfenster erstellt werden soll.
Abbildung 3.15 Tabellenerzeugungsskript erstellen
Das SQL Server Management Studio bietet sogar einen Assistenten für die Erzeugung von Skripten. Diesen Assistenten starten Sie zum Beispiel über das Kontextmenü der Datenbank, so wie in Abbildung 3.16 gezeigt.
35
3 SQL Server-Verwaltungswerkzeuge
Abbildung 3.16 Skript-Assistenten starten
Der Assistent führt Sie Schritt für Schritt durch die für die Skripterstellung zu verwendenden Optionen. So können Sie zum Beispiel auswählen, welche Datenbankobjekte in das Skript aufgenommen und ob für diese Objekte Erzeugungs- bzw. Löschanweisungen erstellt werden sollen. Abbildung 3.17 zeigt den Assistenten in Aktion.
Abbildung 3.17 Der Skript-Assistent
36
3.3 SQL Server Management Studio Zusammenfassung Der Bereich Zusammenfassung (bitte schauen Sie sich nochmals Abbildung 3.8 an) enthält den eigentlichen Arbeitsbereich, in dem zum Beispiel Abfragen eingegeben und ausgeführt werden können. Wie Sie mit Abfragen arbeiten, erfahren Sie etwas weiter unten. In der Zusammenfassung können Sie sich ebenfalls Informationen zu Datenbankobjekten anzeigen lassen. Hierzu stehen Ihnen diverse Berichte zur Verfügung, die je nach dem im Objekt-Explorer ausgewählten Objekttyp variieren. So existiert zum Beispiel für eine Datenbank ein anderer Satz von Berichten als für den SQL Server insgesamt. Eine Liste der vorhandenen Berichte bekommen Sie, indem Sie im Fenster Zusammenfassung auf den Button Berichte klicken. Abbildung 3.18 zeigt die verfügbaren Berichte für eine Datenbank.
Abbildung 3.18 Auswahl eines Berichtes
Wählen Sie den Bericht zur Datenträgerverwendung, so erhalten Sie ein Ergebnis wie in Abbildung 3.19 gezeigt.
37
3 SQL Server-Verwaltungswerkzeuge
Abbildung 3.19 Datenträgerverwendung in der Datenbank AdventureWorks
Projektmappen-Explorer SQL Server Management Studio bietet Ihnen die Möglichkeit, Datenbankprojekte anzulegen. Wenn Sie hiervon Gebrauch machen, werden Ihre Projekte und die in diesen Projekten enthaltenen Elemente im Projektmappen-Explorer verwaltet. Weiter unten werden Sie sehen, wie Sie Datenbankprojekte verwenden. Abfrageeditor Im Abfrageeditor werden Abfragen eingegeben und ausgeführt. Der Abfrageeditor enthält einen Ergebnisbereich, in dem außer dem eigentlichen Ergebnis der Abfrage auch weitergehende Informationen ausgegeben werden. Ein neues Abfragefenster können Sie einfach durch einen Klick auf den Button Neue Abfrage links oben in der Menüleiste öffnen (Abbildung 3.20).
38
3.3 SQL Server Management Studio
Abbildung 3.20 Ein neues Abfragefenster öffnen
Ist bereits eine Abfrage geöffnet, so werden für die Verbindungsinformationen der neuen Abfrage automatisch die Verbindungsinformationen der gerade geöffneten Abfrage verwendet, ansonsten müssen die Verbindungsinformationen wie in Abbildung 3.5 bzw. Abbildung 3.6 gezeigt angegeben werden. Abbildung 3.21 zeigt eine Abfrage und deren Ergebnistabelle. Bitte stören Sie sich im Moment nicht daran, wenn Sie die SQL-Anweisung nicht verstehen. In den Kapiteln 4 und 7 wird T-SQL noch sehr ausführlich behandelt. An dieser Stelle ist zunächst nur wichtig, dass Sie verstehen, wie man das SQL Server Management Studio verwendet, um Abfragen zu entwerfen. Wie diese Abfragen strukturiert sind, ist hier vorerst belanglos.
Abbildung 3.21 Ausführen einer Abfrage im Abfrageeditor
39
3 SQL Server-Verwaltungswerkzeuge Der Abfrageeditor kann übrigens nicht nur SQL-Abfragen für Datenbankmodule des SQL Servers ausführen. Sie können im Abfrageeditor auch Abfragen für SQL Server Analysis Services entwerfen. Hierfür stehen Ihnen MDX-, DMX- und XMLA-Abfragen zur Verfügung. Das ist aber nicht Gegenstand dieses Buches, wir werden hier nur mit T-SQL arbeiten. Sicherlich ist die Ausführung von Abfragen und das Auswerten der Abfrageergebnisse das hauptsächliche Einsatzgebiet für den Abfrageeditor. Dieses Instrument kann jedoch erheblich mehr. Für die zur Verfügung stehenden Optionen und Kommandos können Sie in den meisten Fällen einfach die Menüleiste verwenden, die so aufgebaut ist, wie in Abbildung 3.22 zu sehen.
Abbildung 3.22 Die Elemente der Menüleiste des Abfrageeditors
Im Folgenden werden die einzelnen Elemente dieser Menüleiste näher erklärt. Hierbei werden Sie auch zusätzliche Hintergrundinformationen erhalten und sicherlich feststellen, dass der Abfrageeditor ein enorm nützliches Werkzeug ist. Verbinden. Der Abfrageeditor erfordert für den Entwurf von Abfragen nicht zwingend eine Verbindung zu einem SQL Server. Falls der Abfrageeditor offline ist, können Sie über diesen Button eine Verbindung herstellen. Sie können Ihre Abfragen offline entwerfen, wenn Sie jedoch eine Abfrage ausführen, muss natürlich eine Verbindung existieren. Verbindung ändern. Betätigen Sie diesen Button, wenn Sie die Verbindungsinformationen ändern möchten. Dadurch wird die bestehende Verbindung beendet, und Sie werden aufgefordert, sich neu anzumelden. Dies ist zum Beispiel nützlich, wenn Sie eine entwickelte Abfrage unter einem anderen Sicherheitskontext ausführen möchten, um zu sehen, wie sich die existierenden Berechtigungen auf die Ausführung Ihrer Abfrage auswirken.
40
3.3 SQL Server Management Studio Trennen. Über diesen Button können Sie eine bestehende Verbindung beenden. Datenbank auswählen. Jede Abfrage beginnt auf einer Datenbank, die Sie in dieser Combobox festlegen können. Oftmals erhalten Sie bei der Abfrageausführung Fehlermeldungen, die besagen, dass die in der Abfrage verwendeten Objekte nicht existieren. In einem solchen Fall kontrollieren Sie bitte zunächst, ob Ihre Abfrage auch auf der richtigen Datenbank ausgeführt wird, dies ist eine häufige Ursache für einen solchen Fehler. Abfrage ausführen. Die eingegebene Abfrage wird ausgeführt. Wenn die Abfrage fehlerhaft ist, erhalten Sie im Meldungsfenster des Ausgabebereiches eine entsprechende Fehlermeldung. Ansonsten wird das Ergebnis der Abfrage vorausgesetzt natürlich, die Abfrage gibt ein Ergebnis zurück im Ergebnisbereich ausgegeben. Abfrage analysieren. Über diesen Button haben Sie die Möglichkeit, eine Syntaxprüfung der eingegebenen Abfrage vorzunehmen, ohne dass die Abfrage tatsächlich ausgeführt wird. Allerdings wird wirklich nur eine Überprüfung der Syntax der SQLAnweisungen vorgenommen. Es wird nicht kontrolliert, ob die in der Abfrage enthaltenen Objekte auch tatsächlich existieren. So wird zum Beispiel eine Abfrage
den Syntaxtest erfolgreich bestehen, obwohl die in der Abfrage verwendete Tabelle nicht existiert. Abfrageausführung abbrechen. Insbesondere bei langen Abfragen kann es nützlich sein, die Abfrage einfach abzubrechen. Wenn Sie zum Beispiel versehentlich eine Abfrage abgeschickt haben, die Millionen von Zeilen zurückliefern wird, so wird dies Server, Netzwerk und auch Client unnötig belasten. Eine solche Abfrage können Sie dann einfach stoppen. Geschätzten Ausführungsplan anzeigen. Ausführungspläne geben Auskunft darüber, wie der Abfrageoptimierer des Datenbankmoduls SQL Anweisungen physikalisch verarbeitet. Diese Ausführungspläne sind eine sehr große Hilfe bei der Abfrageoptimierung. Wenn Sie diesen Button betätigen, so analysiert der Abfrageoptimierer die Anweisungen und gibt einen geschätzten Ausführungsplan in grafischer Form zurück ohne allerdings die Abfrage auch auszuführen. Ein solcher Plan enthält wie gesagt Informationen zur internen Verarbeitung der Abfrage im SQL Server und sieht zum Beispiel so aus:
41
3 SQL Server-Verwaltungswerkzeuge
In diesem Buch werden Ihnen Ausführungspläne nochmals in Kapitel 12, das sich mit der Optimierung von Abfragen auseinandersetzt, begegnen. DTA Analyse. Der Database Tuning Advisor, oder zu Deutsch der DatenbankmodulOptimierungsratgeber, kann Abfragen analysieren und Hinweise für die Optimierung des physikalischen Datenbankentwurfs geben. Über diesen Button können Sie den im Editorfenster enthaltenen Abfragetext an den Datenbankmodul-Optimierungsratgeber übergeben und dann dort eine Analyse starten. Weiter hinten in diesem Kapitel werden Sie noch eine kurze Einführung in den Datenbankmodul-Optimierungsratgeber bekommen. In Kapitel 12 wird der Datenbankmodul-Optimierungsratgeber dann zum Einsatz kommen. Abfrageeditor öffnen. Über diesen Button können Sie einen grafischen Abfrageeditor öffnen und auf eine komfortable Art und Weise Abfragen entwerfen. Wenn Sie bereits Erfahrung mit Microsoft Access haben, so wird Ihnen dieser Editor sicherlich bekannt vorkommen:
42
3.3 SQL Server Management Studio Beim Klick auf OK wird die Abfrage nicht ausgeführt, sondern der Abfragetext nur in das Bearbeitungsbereich des standardmäßigen Abfrageeditors eingetragen. Vorlagenparameter ersetzen. Im SQL Server Management Studio können Sie TSQL-Code aus Vorlagen erzeugen. Solche Vorlagen existieren bereits für eine Vielzahl von Einsatzfällen, Sie können die vorhandenen Vorlagen aber auch durch Ihre eigenen Vorlagen ergänzen. Vorlagen finden Sie im Vorlagen-Explorer, den Sie über das Menü Ansicht/Vorlagen-Explorer öffnen. Die existierenden Vorlagen werden dann angezeigt, und Sie können aus den thematisch geordneten Vorlagen eine auswählen. Eine Vorlage zum Sichern einer Datenbank können Sie beispielsweise so öffnen:
Viele Vorlagen müssen allerdings noch angepasst werden. Hierzu werden in den Vorlagen Parameter verwendet. Dies sind einfach Platzhalter in einem bestimmten Format, die durch konkrete Werte ersetzt werden müssen, damit aus einer Vorlage ein ausführbares Skript wird. Über den Button Vorlagenparameter ersetzen können Sie diese Ersetzung vornehmen. Es öffnet sich ein Fenster, in dem alle Parameter aufgelistet sind und dann mit korrekten Werten versehen werden können. Für die ausgewählte Vorlage zum Sichern einer Datenbank müssen die beiden Parameter für die zu sichernde Datenbank und das Verzeichnis, in dem die Sicherung erstellt werden soll, spezifiziert werden:
43
3 SQL Server-Verwaltungswerkzeuge
Ausführungsplan einschließen. Wenn Sie diese Option auswählen, dann wird der tatsächliche Ausführungsplan zum Ergebnisbereich hinzugefügt. Dies passiert immer dann, wenn die Abfrage ausgeführt wird, und zwar so lange, bis Sie diese Option wieder ausschalten. Der erzeugte Ausführungsplan sieht genauso aus wie weiter oben bereits gezeigt. Clientstatistiken. Bei der Auswahl dieser Option werden zusätzliche Statistiken aus der Sicht des Clients, also des Absenders der Abfrage, im Ausgabebereich angezeigt. Diese Statistiken geben Auskunft über die Abfrage selber, enthalten aber auch Informationen zur Netzwerkauslastung und Ausführungszeit:
Sqlcmd-Modus. Sqlcmd ist ein Kommandozeilenprogramm zur Ausführung von Abfragen. Am Ende dieses Kapitels wird dieses Programm noch genauer erklärt. Der Abfrageeditor arbeitet im Sqlcmd-Emulationsmodus, wenn Sie diese Option einschalten.
44
3.3 SQL Server Management Studio Ergebnis in Text. Das Ergebnis der Abfrage wird in Textform ausgegeben. Über das Menü Abfrage/Abfrageoptionen
können Sie festlegen, wie die einzelnen Spalten voneinander getrennt werden sollen:
Ergebnis in Raster. Durch die Auswahl dieser Einstellung wird das Abfrageergebnis in ein Raster eingetragen, so wie in Abbildung 3.21 zu sehen ist. In diesem Raster können Sie die Breite der einzelnen Spalten verändern. Ergebnis in Datei. Wenn Sie diese Option wählen, werden Sie beim Ausführen einer Abfrage aufgefordert, einen Dateinamen anzugeben. Das Abfrageergebnis wird dann in diese Daten geschrieben. Das Format dieser Datei ist so wie die bei der Option Ergebnis in Text. Auskommentieren. Dieser Befehl fügt Zeilenkommentare (--) am Anfang der markierten Zeilen ein. Dadurch werden diese Zeilen nicht ausgeführt. Kommentierung aufheben. Durch diesen Befehl werden die Zeilenkommentare am Zeilenanfang wieder entfernt. Das Entfernen der Kommentare wird für die markierten Zeilen vorgenommen. Einzug verkleinern. Wenn die ausgewählten Zeilen eingerückt sind, wird der Einzug verkleinert. Einzug vergrößern. Die ausgewählten Zeilen werden weiter eingerückt, also nach rechts verschoben. Über das Menü Extras/Optionen
können Sie die Schrittweite einstellen, um die der Einzug bei jedem Klick auf den Button vergrößert wird. Der Standardwert ist 4:
45
3 SQL Server-Verwaltungswerkzeuge
Abfrageoptionen festlegen Etwas weiter oben haben Sie bereits den Dialog für die Einstellung der Abfrageoptionen gesehen. In diesem Dialog, der über das Menü Abfrage/Abfrageoptionen
geöffnet wird, können Sie weitere Optionen festlegen, welche die Darstellung des Abfrageergebnisses und des Abfragetextes sowie auch das Verhalten der Abfrage beeinflussen. Im Verlaufe dieses Buches wird auf einige dieser Optionen noch näher eingegangen. Teile einer Abfrage ausführen Der Abfrageeditor ermöglicht es auch, eine Abfrage nur teilweise auszuführen. Hierzu markieren Sie einfach den Teil der Abfrage, der ausgeführt werden soll. Wenn Sie nun die Abfrage starten, wird nur der markierte Teil der Abfrage ausgeführt. Abfrageergebnis speichern Sie wissen bereits, dass Sie das Abfrageergebnis über den Button Ergebnisse in Datei aus der Menüleiste direkt in eine Datei umleiten können. Es ist auch möglich, das Abfrageergebnis nach der Ausführung einer Abfrage in einer Datei zu speichern. Hierzu können Sie das Kontextmenü für den Ergebnisbereich öffnen, wie in Abbildung 3.23 gezeigt.
46
3.3 SQL Server Management Studio
Abbildung 3.23 Abfrageergebnis in einer Datei speichern
Alternativ können Sie das Ergebnis auch über das Menü Datei/Ergebnisse speichern unter
in einer Datei ablegen. Das Format der gespeicherten Datei hängt hierbei vom Abfrageergebnis ab. Ist das Ergebnis im Rasterformat ausgegeben worden, so werden die einzelnen Spalten in der Datei durch ein Semikolon voneinander getrennt. Ein Abfrageergebnis im Textformat wird einfach genau so in die Datei übernommen. Ein Raster können Sie auch komplett in die Zwischenablage kopieren und dann in ein anderes Dokument einfügen. In diesem Fall werden die einzelnen Spalten durch einen Tabulator voneinander getrennt. Dadurch können Sie zum Beispiel das Abfrageergebnis direkt in Microsoft Excel kopieren. Excel erkennt den Tabulator als Spaltentrennzeichen und erzeugt dadurch eine Tabelle, die dem Raster aus dem Abfrageergebnis entspricht. Abfragetexte öffnen und speichern Falls Sie Abfragetexte gespeichert haben, so können Sie diese im Abfrageeditor öffnen. Hierzu wählen Sie im Menü Datei/Öffnen/Datei
aus und laden die Datei in den Editor. Normalerweise haben diese Dateien die Endung .sql und sind auch bereits mit dem Abfrageeditor des Management Studios verknüpft, können also auch einfach durch Doppelklick im Abfrageeditor geöffnet werden. Es ist ebenfalls möglich, entworfene Abfragen als Datei zu speichern. Hierzu klicken Sie einfach irgendwo in das Fenster mit dem Abfragetext und wählen in der Menüleiste oder über das Menü Datei den Eintrag ... speichern aus. Natürlich funktioniert hier auch die bekannte Tastenkombination Strg+S.
3.3.3
Datenbankprojekte erstellen
SQL Server Management Studio bietet die Möglichkeit, Datenbankprojekte zu erstellen. In solchen Projekten können Sie Ihre T-SQL-Abfragen organisieren. Sie können eigene Skriptsammlungen erstellen, in denen Sie T-SQL-Skripte für wiederkehrende Aufgaben einordnen oder auch einfach nur interessante Skripte, die Sie zum Beispiel im Internet ge-
47
3 SQL Server-Verwaltungswerkzeuge funden haben, in eigenen Sammlungen ablegen. Durch die Einbindung von Datenbankprojekten in Microsoft Visual SourceSafe können Sie für diese Projekte auch eine Versionskontrolle ermöglichen. Abbildung 3.8 zeigt im rechten Teil den Projektmappen-Explorer. Nach jedem Start von SQL Server Management Studio wird automatisch eine neue Projektmappe mit Namen Solution 1 erstellt, sobald Sie die erste Abfrage hinzufügen. Zu dieser Projektmappe können Sie Datenbankprojekte hinzufügen. Hierfür wählen Sie entweder im Kontextmenü für die Projektmappe Hinzufügen/Neues Projekt
oder aus dem Menü Datei/Neu/Projekt
. In beiden Fällen öffnet sich ein Fenster, in dem eine Projektvorlage ausgewählt werden kann (Abbildung 3.24).
Abbildung 3.24 Ein SQL Server-Projekt
Es existiert hier auch die Möglichkeit, Skriptsammlungen für SQL Server Analysis Services oder SQL Server Mobile zu erzeugen. Für ein Projekt, das SQL Server-Skripte enthalten soll, wählen Sie den Projekttyp SQL Server Skripts und geben im Feld Name einen Namen für das Projekt und ein Verzeichnis an, im Beispiel heißt das Projekt Utilities1 und wird im Verzeichnis C:\SQL2005\Utilities1 abgelegt. Wenn der Dialog mit OK bestätigt wird, existiert das Projekt nun in der Projektmappe Solution 1 (Abbildung 3.25). Sie können die Projektmappe über das Kontextmenü auch umbenennen, wenn Ihnen der Name Solution 1 nicht gefällt. Sobald Sie die Projektmappe speichern, kann ebenfalls ein geeigneter Name für die Projektmappe angegeben werden.
48
3.3 SQL Server Management Studio
Abbildung 3.25 Das erzeugte Projekt Utilities1
Die im Projekt Utilities1 enthaltenen Abfragen sind im Ordner Abfragen enthalten. Im Moment ist dieser Ordner noch leer, über das Kontextmenü können Sie eine Abfrage hinzufügen (Abbildung 3.26).
Abbildung 3.26 Eine Abfrage zum Projekt hinzufügen
Durch das Hinzufügen einer Abfrage wird eine Datei mit einem automatisch vergebenen Namen SQLQuery_.sql im Projektverzeichnis erzeugt (der Unterstrich steht hierbei für eine automatisch vergebene Nummer) und im Abfrageeditor geöffnet. Sie können den Namen dieser Datei anschließend ändern und Ihre Abfrage entwerfen. Es ist auch möglich, eine als Datei vorhandene Abfrage zum Projekt hinzuzufügen. Dies kann einfach über Drag und Drop erledigt werden. Dadurch wird die Datei in das Projektverzeichnis kopiert und in das Projekt aufgenommen. Für den Fall, dass Sie eine Abfrage entworfen haben, die noch nicht gespeichert wurde und Sie diese Anfrage nun in ein Projekt der Projektmappe aufnehmen möchten, können Sie im Menü Datei/
verschieben in/
auswählen ( steht hier für den Namen eines Projektes). Um zum Beispiel die aktuell im Abfrageeditor enthaltene Ab-
49
3 SQL Server-Verwaltungswerkzeuge frage in das Projekt Utilities1 aufzunehmen, können Sie so vorgehen, wie in der folgenden Abbildung 3.27 gezeigt. Die Abfrage wird dadurch nachdem ein Dateiname angegeben wurde in den Ordner Abfragen des Projektes aufgenommen.
Abbildung 3.27 Eine Abfrage in das Projekt verschieben
Im Projektordner Sonstiges können übrigens beliebige Dateien wie zum Beispiel Textoder HTML-Dokumente abgelegt werden, die im Projekt verwaltet werden sollen. Der Projektordner Verbindungen enthält eine Liste aller Datenbankverbindungen, die von Ihren Abfragen verwendet werden. Diese Verbindungen werden nur angezeigt und können nicht geändert werden. Für eine Versionskontrolle Ihrer Skripte kann das Projekt zur Quellcodeverwaltung über das Kontextmenü der Projektmappe hinzugefügt werden (Abbildung 3.28).
Abbildung 3.28 Hinzufügen der Projektmappe zur Quellcodeverwaltung
50
3.4 Business Intelligence Development Studio Es ist übrigens möglich, mehrere Projekte in einer Projektmappe zu verwalten. Über das Kontextmenü der Projektmappe können jederzeit weitere Projekte angelegt und zur Mappe hinzugefügt werden Falls bereits Projekte existieren, ist es auch möglich, diese zu einer bestehenden Projektmappe zu addieren. Die Dateitypen für Projekte und Projektmappen werden bei der Installation mit dem SQL Server Management Studio verknüpft. Dadurch können bestehende Projekte und auch Projektmappen einfach durch Doppelklick im Management Studio geöffnet werden. Dies betrifft Dateien mit diesen Endungen: ssmssln. Die Dateiendung ist die Abkürzung für SQL Server Management Studio Solution. Diese Datei enthält eine Projektmappe. ssmssqlproj. Dies ist die Abkürzung für SQL Server Management Studio SQL Project. In einer Datei dieses Typs ist eine Projektbeschreibung gespeichert.
3.4
Business Intelligence Development Studio Das Business Intelligence Development Studio ist eine Entwicklungsumgebung zur Erstellung von Business Intelligence(BI)-Lösungen. Das Programm kann über das Startmenü Microsoft SQL Server 2005/ Business Intelligence Development Studio gestartet werden. In Abbildung 3.29 sehen Sie die beim Anlegen eines Projektes zur Verfügung stehenden Projektvorlagen.
Abbildung 3.29 Anlegen eines Business Intelligence-Projektes
51
3 SQL Server-Verwaltungswerkzeuge Ein Business Intelligence-Projekt kann unter Verwendung einer der folgenden Vorlagen erstellt werden: Analysis Services-Projekt. In diesem Projekttyp werden Analysis ServicesDatenbanken entwickelt und verwaltet. Solche OLAP-Datenbanken sind für den schnellen Lesezugriff optimiert. Ein Analysis Services-Projekt kann auch Data MiningModelle enthalten. In [SCHRO06] finden Sie hierzu weitergehende Informationen. Analysis Services 9.0-Datenbank importieren. Falls bestehende SQL Server 2000oder SQL Server 7.0-Analysis Services-Datenbanken in eine SQL Server 2005Analysis Services-Datenbank überführt werden sollen, kann dieser Projekttyp verwendet werden. Integration Services-Projekt. Ein Integration Services-Projekt ist ein Projekt zur Integration von Daten. In einem solchen Projekt können zum Beispiel Daten aus unterschiedlichen Quellen eingelesen, bearbeitet (transformiert) und zentral gespeichert werden. Berichtsserverprojekt. In einem Berichtsserverprojekt werden Berichte für SQL Server Reporting Services erstellt. Hierzu steht ein Berichtsdesigner zur Verfügung, mit dem Datenzugriffe und Berichtslayouts entworfen werden. Berichtsmodellprojekt. Ein Berichtsmodell enthält eine Geschäftssicht auf die zugrunde liegenden Daten. Diese Sicht können Benutzer des Modells verwenden, um mit dem Berichtsgenerator Ad-hoc-Berichte zu erstellen. Berichtsserverprojekt-Assistent. Bei der Auswahl dieses Projekttyps wird ein Berichtsserverprojekt unter Verwendung eines Assistenten erstellt. Das erstellte Projekt kann dann anschließend manuell bearbeiten werden. Die Thematik Business Intelligence ist nicht Bestandteil dieses Buches, das Business Intelligence Development Studio wird daher hier auch nicht zum Einsatz kommen.
3.5
SQL Server Profiler Der SQL Server Profiler ist ein Instrument zur Überwachung und Protokollierung der SQL Server-Aktivität. Das erzeugte Protokoll wird auch als Ablaufverfolgung bezeichnet. Eine Ablaufverfolgung kann einfach in einer Datei im nativen Format des Profilers abgelegt werden. Das Protokoll kann aber auch in einer Datei im XML-Format oder in einer Datenbanktabelle gespeichert werden. Für eine Ablaufverfolgung müssen Ereignisse und Ereignisdaten (auch Ereignisspalten genannt), die in die Ablaufverfolgung aufgenommen werden sollen, angegeben werden. Den Profiler starten Sie über das Startmenü Microsoft SQL Server 2005/ Leistungstools/SQL Server Profiler. Alternativ ist der Aufruf auch aus dem SQL Server Management Studio über das Menü Extras/SQL Server Profiler möglich (Abbildung 3.30).
52
3.5 SQL Server Profiler
Abbildung 3.30 Start des SQL Server Profilers aus dem Management Studio
Nach dem Start des Profilers kann über die Menüleiste eine neue Ablaufverfolgung erzeugt werden (Abbildung 3.31).
Abbildung 3.31 Eine neue Ablaufverfolgung konfigurieren
Beim Erzeugen einer neuen Ablaufverfolgung erscheint zunächst der schon aus der Anmeldung an das Management Studio bekannte Anmeldedialog. Nach der erfolgreichen Anmeldung wird ein Dialog geöffnet, in dem die zu protokollierenden Ereignisse ausgewählt werden können. Es existiert eine Vielzahl von Ereignissen, mit denen Sie Ihre Ablaufverfolgung ganz speziell an die jeweiligen Erfordernisse anpassen können. Die Auswahl geeigneter Ereignisse ist keinesfalls trivial. Grundsätzlich wird das Erstellen einer Ablaufverfolgung zu einer Verschlechterung des Laufzeitverhaltens von SQL Server führen, da die Protokollierung ja auch Ressourcen auf dem Server benötigt. Für die Ereignisauswahl sollte deshalb stets der Grundsatz so wenig wie möglich, so viel wie nötig gelten. Glücklicherweise existieren für viele Einsatzfälle bereits vordefinierte Sammlungen von zu protokollierenden Ereignissen, die in Vorlagen zusammengefasst sind. Beim Konfigurieren der Ablaufverfolgungseigenschaften ist es oftmals ausreichend, einfach eine solche Vorlage zu verwenden (Abbildung 3.32). Es ist auch möglich, bestehende Vorlagen zu verändern oder eigene Vorlagen zu entwerfen.
53
3 SQL Server-Verwaltungswerkzeuge
Abbildung 3.32 Vorlagen für zu protokollierende Ereignisse
Tabelle 3.1 enthält eine Kurzübersicht der zur Verfügung stehenden Vorlagen. Tabelle 3.1 Vorlagen für die Ablaufverfolgung Vorlage
Beschreibung
SP_Counts
Geeignet für die Protokollierung der Ausführung gespeicherter Prozeduren.
Standard (Standard)
Protokollierung gespeicherter Prozeduren und T-SQL-Stapel.
TSQL
Protokolliert T-SQL-Anweisungen und deren Startzeit.
TSQL_Duration
Protokolliert T-SQL-Anweisungen und deren Ausführungszeit.
TSQL_Grouped
Protokolliert T-SQL-Anweisungen und deren Startzeit und gruppiert das Protokoll nach dem ausführenden Benutzer.
TSQL_Replay
Protokolliert Informationen, die für einen erneutes Einspielen der Ablaufverfolgung erforderlich sind.
TSQL_SPs
Protokolliert ebenfalls Informationen zur Ausführung von gespeicherten Prozeduren.
Tuning
Erstellt eine Ablaufverfolgung, die als Eingabe für den DatenbankmodulOptimierungsratgeber verwendet werden kann.
Durch die Auswahl einer Vorlage sind bereits die entsprechenden Ereignisse für die Protokollierung ausgewählt. Auf der Seite Ereignisauswahl können die Ereignisse aber auch noch modifiziert werden (Abbildung 3.33).
54
3.5 SQL Server Profiler
Abbildung 3.33 Manuelle Auswahl der Protokollereignisse
Wichtig ist, dass Sie die beiden Optionen Alle Ereignisse anzeigen und Alle Spalten anzeigen auswählen, anderenfalls bekommen Sie nur die bereits ausgewählten Ereignisse angezeigt. Wie gesagt: Die zur Verfügung stehenden Ereignisse sind zahlreich und sollten sorgfältig ausgewählt werden. Dies erleichtert die Zusammenstellung der zu protokollierenden Ereignisse natürlich nicht gerade. Glücklicherweise erhalten Sie aber im Dialog für die Ereignisauswahl eine Kurzbeschreibung für Ereignisse und die Ereignisspalten. Sobald der Mauszeiger sich über einem Ereignis bzw. einer Ereignisspalte befindet, wird die Beschreibung im unteren Teil des Fensters angezeigt. Tabelle 3.2 gibt einen Überblick über einige häufig verwendete Ereignisse. Tabelle 3.2 Einige häufig benötigte Ereignisse Bereich
Ereignis
Beschreibung
Stored Procedures
SP:Starting
Tritt auf, wenn eine gespeicherte Prozedur gestartet wird.
SP:Completed
Tritt auf, wenn eine gespeicherte Prozedur beendet wurde.
SP:StmtStarting
Tritt auf, wenn innerhalb einer Prozedur eine Anweisung beginnt.
SP:StmtCompleted
Tritt auf, wenn innerhalb einer Prozedur eine Anweisung abgeschlossen wurde.
SQL:BatchStarting
Tritt auf, wenn ein T-SQL-Stapel beginnt.
SQL:BatchCompleted
Tritt auf, wenn ein T-SQL-Stapel beendet wurde.
TSQL
55
3 SQL Server-Verwaltungswerkzeuge Bereich
Ereignis
Beschreibung
TSQL
SQL:StmtStarting
Tritt auf, wenn eine Anweisung in einem T-SQLStapel beginnt.
SQL:StmtCompleted
Tritt auf, wenn eine Anweisung in einem T-SQLStapel beendet wurde.
Audit:Login
Tritt auf, wenn eine Clientverbindung zum SQL Server hinzugefügt wurde.
Audit:Logout
Tritt auf, wenn eine Clientverbindung beendet wurde.
Security Audit
Es stehen übrigens nicht alle Ereignisspalten für alle Ereignisse zur Verfügung. Möchten Sie zum Beispiel die Dauer einer Abfrage in die Ablaufverfolgung aufnehmen, so muss die Spalte Duration hinzugefügt werden. Dies ist aber nur möglich, wenn auch das Ereignis zum Beenden einer Abfrage protokolliert wird. Zusätzlich können die protokollierten Zeilen über Spaltenfilter eingeschränkt werden. Hierzu klicken Sie in der Ereignisauswahl auf den Button Spaltenfilter. Es öffnet sich ein Dialog, in dem die Filterbedingungen eingestellt werden können. Abbildung 3.34 zeigt die Konfiguration einer Filterbedingung, die bewirkt, dass nur Ereignisse für die Datenbank AdventureWorks protokolliert werden.
Abbildung 3.34 Spaltenfilter konfigurieren
Speichern von Ablaufverfolgungen Beim Konfigurieren der Ablaufverfolgungseigenschaften (Abbildung 3.32) kann auch festgelegt werden, wie das erzeugte Protokoll gespeichert werden soll. Prinzipiell ist es möglich, ein Protokoll in einer Datei oder in einer Datenbanktabelle zu speichern. Die Speicherung in einer Tabelle hat natürlich einige Vorteile, zum Beispiel kann eine solche
56
3.5 SQL Server Profiler Tabelle relativ einfach unter Verwendung von SQL durchsucht werden, was bei Dateien nicht möglich ist. Allerdings ist die Speicherung in einer Tabelle auch langsamer als die Dateivariante. Es gibt jedoch eine Möglichkeit, eine Ablaufverfolgungsdatei in eine entsprechende Tabelle zu konvertieren. In Kapitel 12 werden Sie erfahren, wie dies funktioniert. Es ist auch jederzeit möglich, eine Ablaufverfolgung über das Menü Datei/Speichern oder Datei/Speichern unter in einer Datei zu speichern. Ist die Auswahl der Ereignisse abgeschlossen, kann die konfigurierte Ablaufverfolgung schließlich über den Button Ausführen gestartet werden. Abbildung 3.35 zeigt den Profiler mit einer laufenden Ablaufverfolgung.
Abbildung 3.35 Der SQL Server Profiler in Aktion
Über die Menüleiste kann die Ablaufverfolgung auch angehalten bzw. gestoppt und fortgesetzt bzw. neu gestartet werden. Es ist auch möglich, die Ereignisse für eine Ablaufverfolgung zu verändern, dafür muss die Ablaufverfolgung aber zumindest angehalten werden (Abbildung 3.36).
Abbildung 3.36 Die Menüleiste des Profilers
57
3 SQL Server-Verwaltungswerkzeuge Der SQL Server Profiler ist übrigens ein ganz hervorragendes Instrument zum Debuggen. Im Profiler können Sie genau verfolgen, wie Ihre vom Client abgeschickten SQLAnweisungen letztlich im SQL Server ankommen. Dies ist oftmals eine große Hilfe bei der Fehlersuche. Seien Sie aber bitte vorsichtig beim Einsatz des Profilers auf Produktionsservern. Denken Sie stets daran, dass die Protokollierung ihrerseits ebenfalls SQL Server-Ressourcen bindet. Dieser Umstand kann das Laufzeitverhalten eines Servers ganz erheblich beeinflussen. Der Profiler wird Ihnen in Kapitel 12 zur Abfrageoptimierung nochmals begegnen. Dort werden Sie auch sehen, wie man serverseitige Ablaufverfolgungen erstellt, die möglichst wenig Einfluss auf das SQL Server Laufzeitverhalten haben.
3.6
Datenbankmodul-Optimierungsratgeber Der Datenbankmodul-Optimierungsratgeber (englisch: Database Engine Tuning Advisor oder kurz DTA) kann sogenannte Arbeitsauslastungen analysieren und Vorschläge zum Ändern des physikalischen Datenbankentwurfs unterbreiten. Ein Analyseergebnis kann Vorschläge für die Änderung von: Indizes Gruppierten Indizes Indizierten Sichten Partitionen enthalten. Arbeitsauslastungen, also die Eingabe für den DTA, können sowohl T-SQL-Skripte als auch gespeicherte SQL Server Profiler-Ablaufverfolgungen sein. Ablaufverfolgungen, die als Eingabe für den DTA verwendet werden sollen, müssen bestimmte Informationen erhalten, damit eine Analyse durchgeführt werden kann. Am einfachsten erreichen Sie dies, indem Sie die Ablaufverfolgung mit der Vorlage Tuning erstellen (schauen Sie sich bitte nochmals Abbildung 3.32 an, dort sehen Sie diese Vorlage als letzte in der Liste). Diese Vorlage enthält bereits alle notwendigen Ereignisse und Ereignisspalten. Den DTA starten Sie über das Startmenü Microsoft SQL Server 2005/Leistungstools/ Datenbankmodul-Optimierungsratgeber oder über das Menü Extras/DatenbankmodulOptimierungsratgeber (siehe Abbildung 3.30) im Management Studio. Beim Start werden Sie zunächst den schon vertrauten Anmeldedialog sehen. Nach der Anmeldung wird sofort eine neue Sitzung geöffnet. Über die Menüleiste kann aber auch jederzeit manuell eine Sitzung hinzugefügt werden (Abbildung 3.37).
58
3.6 Datenbankmodul-Optimierungsratgeber
Abbildung 3.37 Eine DTA-Sitzung hinzufügen
Nach der Erzeugung einer neuen Sitzung können dann die für die Analyse erforderlichen Parameter eingegeben werden. Hierzu zählen der Name für die Sitzung sowie die Datenbanken und Tabellen für die Optimierung. Außerdem benötigt der DTA eine Arbeitsauslastung, welche die Basis für die Analyse bildet Diese Arbeitsauslastung kann in einer Datei stehen und ein T-SQL-Skript bzw. eine Profiler-Ablaufverfolgung enthalten. Da eine Ablaufverfolgung ebenso in einer Tabelle einer Datenbank gespeichert werden kann, ist es hier auch möglich, eine solche Tabelle als Arbeitsauslastung anzugeben. In Abbildung 3.38 sehen Sie eine Beispielkonfiguration.
Abbildung 3.38 Sitzungsparameter festlegen
Auf der Registerkarte Optimierungsoptionen (Abbildung 3.39) kann festgelegt werden, welche Objekttypen optimiert und welche bei der Optimierung nicht berücksichtigt werden sollen. Hier ist es auch möglich anzugeben, dass gewisse physikalische Strukturen beibe-
59
3 SQL Server-Verwaltungswerkzeuge halten werden sollen. Die Analyse untersucht die entsprechenden Datenbanken auch auf überflüssige Elemente, wie zum Beispiel Indizes. Wenn in Ihrer Arbeitsauslastung solche Elemente nicht benötigt werden, so wird der DTA vorschlagen, diese zu entfernen. Falls Sie dies vermeiden möchten etwa weil Sie wissen, dass die Arbeitsauslastung nicht repräsentativ ist , so können Sie festlegen, dass keine Vorschläge für das Entfernen vorhandener Elemente unterbereitet werden sollen.
Abbildung 3.39 Optionen für die Optimierung festlegen
Beim Einstellen der Optionen ist es auch möglich, dass Sie die Zeit für die Optimierung einschränken. Dies ist sicherlich dann sinnvoll, wenn Sie sehr umfangreiche Ablaufverfolgungen verwenden, deren Analyse eventuell zu lange dauern würde. Haben Sie alle Optionen festgelegt, kann die Analyse schließlich gestartet werden. Hierzu klicken Sie einfach auf den Button Analyse in der Menüleiste. Auch der Datenbankmodul-Optimierungsratgeber wird nochmals in Kapitel 12 auftauchen. Dort wird dann auch ein Analyseergebnis präsentiert.
60
3.7 Sqlcmd
3.7
Sqlcmd Sqlcmd ist ein Befehlszeilenprogramm zur Ausführung von SQL-Skripten. Hierbei kann Sqlcmd wahlweise einzelne Anweisungen von der Kommandozeile entgegennehmen, aber auch komplette T-SQL-Skripte aus einer Datei verarbeiten. Das Ergebnis kann dann ebenso entweder ausgegeben oder in einer Datei abgelegt werden. Wie bei Kommandozeilenprogrammen üblich, wird die Ausführung von Sqlcmd über Kommandozeilenparameter gesteuert. Eine Übersicht über die zur Verfügung stehenden Parameter erhalten Sie durch den Aufruf:
Abbildung 3.40 zeigt das Ergebnis des Programmaufrufs.
Abbildung 3.40 Parameter für Sqlcmd
Wenn Sie beim Start von Sqlcmd keine Verbindungsinformation angeben, so wird standardmäßig versucht, eine Verbindung zum lokalen SQL Server unter Verwendung der Windows-Authentifizierung aufzubauen, wobei dann der an Windows angemeldete Benutzer verwendet wird. Sqlcmd kann sehr schön zur Automatisierung innerhalb von Stapelverarbeitungsprogrammen verwendet werden. Schauen Sie sich die obigen Parameter einmal an und versuchen dann, den folgenden Programmaufruf zu verstehen:
61
3 SQL Server-Verwaltungswerkzeuge Über den Parameter Q wird eine Abfrage ausgeführt. Das Ergebnis dieser Abfrage wird dann in der durch den Parameter -o angegebenen Datei gespeichert. Da keine Verbindungsinformationen angegeben wurden, wird der lokale Server mit der WindowsAnmeldeinformation verwendet. Eine interessante Möglichkeit ist die Übergabe von Parametern über die Option v:
Auf einen derart übergebenen Parameter kann im SQL-Code folgendermaßen zugegriffen werden:
Es ist auch möglich, Umgebungsvariablen abzufragen. Um zum Beispiel den Computernamen zu verwenden, existiert die Umgebungsvariable COMPUTERNAME, die so verwendet wird:
Auf die gleiche Weise haben Sie auch Zugriff auf die Kommandozeilenparameter. Hierzu existieren vordefinierte Variablen. Tabelle 3.3 zeigt die Verbindung zwischen Kommandozeilenparameter und Variable. Tabelle 3.3 Variablen für Kommandozeilenparameter Variable
Kommandozeilenparameter
SQLCMDUSER
-U
SQLCMDSERVER
-S
SQLCMDWORKSTATION
-H
SQLCMDDBNAME
-d
SQLCMDLOGINTIMEOUT
-l
SQLCMDSTATTIMEOUT
-t
SQLCMDHEADERS
-h
SQLCMDCOLSEP
-s
SQLCMDCOLWIDTH
-w
SQLCMDPACKETSIZE
-a
SQLCMDERRORLEVEL
-m
SQLCMDMAXVARTYPEWIDTH
-y
SQLCMDMAXFIXEDTYPEWIDTH
-Y
Eine Besonderheit ist der Kommandozeilenparameter A. Durch die Angabe dieses Parameters wird eine dedizierte Verbindung zum Server aufgebaut. Diese Verbindung wird sogar dann funktionieren, wenn die Serverauslastung derart hoch ist, dass normalerweise
62
3.8 SQL Server-Onlinedokumentation keine Verbindungen mehr möglich sind. Ein Administrator kann dadurch eine solche Situation analysieren und eventuell Abhilfe schaffen. Diese Möglichkeit muss jedoch zuvor durch die Oberflächenkonfiguration für Features aktiviert werden. In Abbildung 3.4 sehen Sie links den Eintrag DAC (für Dedicated Administrator Connection). Hierüber kann diese Aktivierung vorgenommen werden.
3.8
SQL Server-Onlinedokumentation Die Onlinedokumentation wird zum Beispiel über das Startmenü Microsoft SQL Server 2005/Dokumentation und Lernprogramme/SQL Server-Onlinedokumentation aufgerufen. Ebenso kann die Dokumentation über das Menü Hilfe im Management Studio gestartet werden. Es ist auch möglich, im Abfrageeditor SQL Schlüsselwörter zu markieren und dann die Dokumentation über die Tastenkombination UMSCHALT+F1 zu aktivieren. Dadurch erhalten Sie gleich die Hilfe zum markierten Schlüsselwort. Abbildung 3.41 zeigt die Hilfe für die Anweisung CREATE DATABASE, die zu Beginn des nächsten Kapitels verwendet wird.
Abbildung 3.41 Onlinedokumentation für CREATE DATABASE
63
3 SQL Server-Verwaltungswerkzeuge In der Onlinedokumentation finden Sie nicht nur die Referenzen zu SQL-Anweisungen, sondern auch Lernprogramme und Beispiele. Die Dokumentation enthält Informationen zum SQL Server-Setup und auch ein Handbuch für den Datenbankentwickler, kurzum: Die Dokumentation ist eine schier unerschöpfliche Informationsquelle für alle Aspekte von SQL Server 2005.
3.9
Zusammenfassung In diesem Kapitel haben Sie die Verwaltungswerkzeuge des SQL Servers kennengelernt. Sie wissen nun, welche Möglichkeiten für die Konfiguration und Überwachung es gibt und welche Instrumente Sie hierfür zur Verfügung haben. Ein besonderer Schwerpunkt war in diesem Kapitel das SQL Server Management Studio mit der Möglichkeit, Server- und Datenbankobjekte zu überwachen und zu konfigurieren. Insbesondere ist nun klar, wie Sie den im Management Studio integrierten Abfrageeditor benutzen, um Ihre Abfragen an den Server zu senden. Auch wenn Sie zu diesem Zeitpunkt noch nicht so genau wissen, welche Möglichkeiten SQL-Abfragen bieten und wie Sie solche Abfragen entwerfen, sollten Sie sich nun mit der Bedienung des Abfrageeditors einigermaßen auskennen. Der Abfrageeditor wird Sie durch alle verbleibenden Kapitel dieses Buches hindurch permanent begleiten. Sie haben erfahren, wie Sie Skriptsammlungen anlegen und in Datenbankprojekten organisieren können, und haben ein grobes Verständnis für den Einsatz des Business Intelligence Development Studios. Zum Schluss dieses Kapitels haben Sie eine Einführung in die Benutzung des SQL Server Profilers und des Datenbankmodul-Optimierungsratgebers erhalten. Schließlich kennen Sie nun auch das Programm Sqlcmd, das die Verarbeitung von Abfragen von der Kommandozeile ermöglicht, und wissen, wie Sie die Onlinedokumentation verwenden.
64
4 Transact SQL-Grundlagen Dieses Kapitel vermittelt Ihnen eine umfassende Einführung in die Structured Query Language (SQL), wobei der spezielle Microsoft-Dialekt Transact SQL im Vordergrund stehen wird. Sie erfahren, wie man mit SQL Datenbanken und Datenbankobjekte anlegt, verändert und löscht. Weiterhin werden Sie lernen, wie Sie SQL-Kommandos zur Datenabfrage und Datenänderung verwenden. In den 70er-Jahren des vorigen Jahrhunderts erblickte die Sprache SQL als Implementierungssprache für ein relationales Datenbankmodell in einem IBM-Forschungslabor das Licht der Welt. Seit diesem Zeitpunkt hat sich SQL als Standardsprache für relationale Datenbank-Management-Systeme (DBMS) weitgehend durchgesetzt und ist mittlerweile Bestandteil aller gängigen DBMS darunter auch Microsoft SQL Server. Die starke Verbreitung von SQL hat auch dazu geführt, dass mittlerweile internationale Standards existieren, die den Sprachumfang definieren. Diese Standards haben zum Ziel, dass alle DBMS nach Möglichkeit denselben SQL Sprachumfang unterstützen, sodass es für die Entwickler von Datenbanklösungen letztlich keine Rolle spielt, auf welchem System ihre Lösungen installiert werden. Auch wenn die existierenden Standards von den Herstellern der unterschiedlichen DBMS in unterschiedlichen Ausbaustufen erfüllt werden und somit sicherlich ein gewisser Standardisierungsgrad gegeben ist, so unterscheiden sich die bestehenden DBMS teilweise doch beträchtlich in ihren SQL-Dialekten. Die bestehenden Standardisierungsvorschriften des American National Standards Institute (ANSI), die später von der International Standards Organization (ISO) übernommen wurden, beschränken sich in der Hauptsache auf die grundlegenden SQL-Kommandos und eröffnen den DBMS-Herstellern somit ein weites Feld für eigene Erweiterungen. Von diesen Möglichkeiten machen alle DBMSAnbieter regen Gebrauch auch die Firma Microsoft hat mit der Sprache Transact SQL ihren eigenen SQL-Dialekt entwickelt. Dieser Dialekt hält sich natürlich in allen wesentlichen Punkten an bestehende Standards, dort, wo es die Standardisierung erlaubt, wird der SQL-Sprachumfang allerdings auch erweitert bzw. angepasst.
65
4 Transact SQL-Grundlagen
4.1
Das Relationenmodell Um erfolgreich mit SQL arbeiten zu können, ist zumindest ein gewisses Grundverständnis des Relationenmodells erforderlich. Dieses Kapitel wird aber keine vollständige Beschreibung der zugrunde liegen Theorie liefern, sondern nur eine kurze Einführung geben, die zum weiteren Verständnis der Sprache SQL unerlässlich ist. Für den theoretischen Aspekt des Datenbankentwurfs und den für einen robusten, performanten und beständigen Entwurf notwendigen Normalisierungsprozess gibt es eigene Bücher, siehe z. B. [STEI03]. Der mathematisch gebildete Leser kann sich durchaus auch an das Original heranwagen: A Relational Model of Data for Large Shared Data Banks. von Erfinder des Relationenmodells Dr. E. F. Codd selbst verfasst. Für den Beginn ist es ausreichend, wenn Sie verstehen, dass die Daten in sogenannten Relationen gespeichert werden. Relationen sind in der Theorie nichts weiter als Mengen. Die Repräsentation einer Relation aus dem relationalen Modell ist im Datenbanksystem eine Tabelle, also eine zweidimensionale Anordnung aus Zeilen und Spalten. Eine Menge von Tabellen wird dann in einer relationalen Datenbank, die wir im Folgenden auch nur kurz als Datenbank bezeichnen wollen, zusammengefasst. Und das ist etwas vereinfacht auch schon alles. Wenn Sie weiterführende Literatur über den Datenbankentwurf und das Relationenmodell studieren, werden Sie mit einer Reihe von Begriffen konfrontiert, die wir in diesem Buch, das ja keine theoretischen Abhandlungen, sondern Praxiswissen liefern soll, nicht weiter verwenden werden. Daher folgt an dieser Stelle auch nur eine kurze Erläuterung der wichtigsten Begriffe: Relation Wie bereits erwähnt ist eine Relation eine zweidimensionale Anordnung von Zeilen und Spalten, also eine Tabelle. Wir verwenden hier nur den Begriff Tabelle. Tupel Ein Tupel ist einfach eine Menge von Feldwerten also eine Zeile in einer Relation (Tabelle). Wir werden im weiteren Verlauf nicht von Tupeln, sondern immer nur von Zeilen reden. Attribut Als Attribut wird eine einzelne Spalte in einem Tupel (also in einer Tabellenzeile) bezeichnet. Schlüssel Schlüssel dienen zur Identifikation von Tupeln. Durch einen Schlüssel muss eine Zeile innerhalb einer Tabelle eindeutig identifizierbar sein, sich also von allen anderen Zeilen unterscheiden.
66
4.2 SQL mit System Die Theorie zum Relationenmodell befasst sich mit der Abbildung, also der Überführung, der Realität1 in ein relationales Modell, das ein Datenmodell aus Tabellen, die zueinander in Beziehung stehen, beschreibt. Das Relationenmodell ist mengenorientiert, hier ist stets die Rede von Mengen, Teilmengen, Schnittmengen etc. SQL als Abfragesprache für relationale Datenbanken ist daher eine mengenorientierte Sprache. Es ist sehr wichtig, dass Sie sich dies einprägen wir werden im weiteren Verlauf dieses Buches noch mehrfach darauf zurückkommen. Eine SQL-Abfrage gibt also genau genommen keine Tabelle, sondern eine Ergebnismenge zurück. Sie sollten sich also das Ergebnis einer Abfrage eher so vorstellen, wie in Abbildung 4.1 in der Ellipse dargestellt.
Abbildung 4.1 Ergebnismenge einer Abfrage
4.2
SQL mit System SQL ist eine faszinierende Sprache. Für das Verständnis einer einfachen SELECT-Anweisung benötigt jemand, der bereits mit einem Tabellenkalkulationsprogramm in Berührung gekommen ist, vielleicht ungefähr zwei Minuten. Um die Sprache allerdings zu beherrschen, zu meistern, sind wahrscheinlich sogar einige Jahre Praxiserfahrung erforderlich. Dabei ist die Sprache SQL an sich gar nicht so besonders umfangreich. Im Prinzip gibt es nur eine Handvoll Anweisungen, die allerdings oftmals viele Klauseln und Optionen enthalten können und dadurch letztlich umfangreich und komplex werden. Diese Aussage trifft insbesondere auf die SELECT-Anweisung zu. SELECT-Anweisungen können sich im Ausdruck durchaus über eine, manchmal sogar über mehrere DIN-A4-Seite(n) erstrecken. Sie können ineinander geschachtelt sein, Tabellen auf verschiedene Arten miteinander verknüpfen, Berechnungen durchführen und umfangreiche Filterbedingungen verwenden.
1
Der Begriff Realität steht hier in Anführungszeichen, weil es so etwas wie eine objektive Realität sicher nicht gibt. Die Interpretation der realen Welt hängt von subjektiven Einflüssen und der Wahrnehmung, Kultur etc. des Einzelnen ab, aber das ist mehr ein philosophisches Thema. Fest steht allerdings, dass unterschiedliche Datenbankdesigner dieselbe Problematik letztlich auch in verschiedene Designs übersetzen werden eben darum, weil die Problemanalyse (auch) durch ihre subjektive Sicht beeinflusst wird.
67
4.3 T-SQL-Stapel (Batches)
4.3
T-SQL-Stapel (Batches) T-SQL-Anweisungen werden vom SQL Server in sogenannten Stapeln ausgeführt. Hierbei können in einem Stapel mehrere Anweisungen enthalten sein, die jeweils voneinander getrennt sein müssen. Auch der Stapel selber muss vom nachfolgenden Stapel getrennt, also mit einem Stapeltrennzeichen abgeschlossen werden. Stellen Sie sich einen T-SQL-Stapel wie ein Paket vor, das als Einheit zum Server gesendet und ausgeführt wird. Trennung von Anweisungen Wie aus vielen anderen Programmiersprachen (zum Beispiel Java, C# oder C++) bekannt, ist das Semikolon das Zeichen für das Ende einer Anweisung. Ein Anweisungsende wird aber auch durch das Auftreten eines SQL-Schlüsselworts im Stapel erkannt. In diesem Fall wird automatisch eine neue Anweisung gestartet, ohne dass hierfür ein Semikolon angegeben werden muss. Die meisten aber eben nicht alle T-SQL-Anweisungen zählen zu diesen Schlüsselwörtern, zum Beispiel auch die in Abbildung 4.2 aufgeführten. Daher wird Ihnen sehr oft T-SQL-Code begegnen, in dem keine explizite Trennung von Anweisungen durch ein Semikolon enthalten ist. Es gibt T-SQL-Anweisungen, die in einem Stapel an erster Stelle stehen müssen. Hierzu zählt zum Beispiel das Kommando CREATE VIEW (siehe Abschnitt 4.17). Eine solche Anweisung muss also entweder am Beginn des gesamten Skripts stehen oder unmittelbar nach dem Stapeltrennzeichen. Stapeltrennzeichen Das Standard-Stapeltrennzeichen ist die Zeichenkette GO. Durch diese Zeichenfolge wird ein T-SQL-Stapel abgeschlossen. Die nächste Anweisung beginnt dann einen neuen Stapel. Es ist möglich, über das Menü Abfrage/Abfrageoptionen ein alternatives Stapeltrennzeichen festzulegen (Abbildung 4.3). Dadurch wird GO allerdings nicht deaktiviert, das so definierte Stapeltrennzeichen steht Ihnen zusätzlich zur Verfügung. Bitte bedenken Sie aber, dass diese Einstellung temporär ist und nur für den lokalen Abfrageeditor gilt. Das Stapeltrennzeichen ist keine SQL-Anweisung, es wird also nicht an den Server übertragen. Nur der Abfrageeditor verwendet das Trennzeichen, um ein Skript in Stapel zu zerlegen, die dann als einzelne Pakete zum Server übertragen werden. Ein alternatives Stapeltrennzeichen wird nur von der lokalen Instanz des Abfrageeditors korrekt als solches interpretiert. Wenn Sie in Ihrem Skript ein alternatives Stapeltrennzeichen verwenden, so wird dieses Skript auch nur im aktuellen Abfrageeditor fehlerfrei funktionieren. Sobald Sie das SQL Server Management Studio beenden und neu starten, ist die Einstellung für das alternative Stapeltrennzeichen verloren und muss erneut vorgenommen werden. Ansonsten wird ein Skript, welches das alternative Stapeltrennzeichen verwendet, nicht laufen. Andere Clients, die T-SQL-Stapel verarbeiten, werden in der Regel davon ausgehen, dass GO als Stapeltrennzeichen verwendet wird, und ein Skript mit einem alternativen Stapeltrennzeichen daher nicht verarbeiten können. Sqlcmd, zum Beispiel, verwendet GO als stan-
69
4 Transact SQL-Grundlagen dardmäßiges Stapeltrennzeichen und kann ein Skript mit einem alternativen Trennzeichen nur verarbeiten, wenn dieses alternative Trennzeichen über den Kommandozeilenparameter -c angegeben wird.
Abbildung 4.3 Festlegen eines alternativen Stapeltrennzeichens
Im Stapeltrennzeichen kann mit einer zusätzlichen Option angegeben werden, wie oft der Stapel an den Server gesendet werden soll. Dieses Feature ist allerdings undokumentiert. Um zum Beispiel einen T-SQL-Stapel zehn Mal auszuführen, kann man schreiben:
Kommentare T-SQL-Quellcode kann Kommentarzeilen enthalten. Solche Zeilen sind zur Dokumentation des Codes gedacht, sie werden bei der Ausführung von Stapeln ignoriert. Unterschieden wird zwischen Zeilen- und Blockkommentar. Ein Zeilenkommentar wird durch zwei Bindestriche () in einer Zeile begonnen und kommentiert dadurch den Rest dieser Zeile aus. Ein Blockkommentar wird durch die Zeichenfolge begonnen und durch wieder beendet. Der zwischen diesen beiden Markierungen liegende Text ist der Kommentar, der sich also auch über mehrere Zeilen erstrecken kann:
70
4.4 SQL-Datentypen
4.4
SQL-Datentypen Durch einen Datentyp werden sowohl die Art der enthaltenen Daten als auch deren Länge festgelegt. Bei numerischen Datentypen bestimmt der Datentyp außerdem die Genauigkeit und die Anzahl der Dezimalstellen. Neben den Datentypen für numerische Werte kennt SQL Server Datentypen für Zeichenketten, Datum und Zeit sowie zur Speicherung binärer Daten. SQL Server unterstützt auch einen nativen XML-Datentyp und einige weitere Datentypen, die in keine der aufgezählten Kategorien eingeordnet werden können. Eine Besonderheit von SQL ist, dass Variablen und Spaltenwerte auch leer sein dürfen. SQL kennt hierzu den Wert NULL, der Spalten bzw. Variablen zugewiesen werden kann. Die Besonderheiten und Probleme mit solchen NULL-Werten werden in Abschnitt 4.15 erläutert. Numerische Datentypen SQL Server unterstützt die in Tabelle 4.2 genannten numerischen Datentypen. Tabelle 4.2 SQL Server: numerische Datentypen Datentyp
Wertebereich
Erklärung
2
63
2
31
smallint
2
15
tinyint
0 bis 255
Eine ganze Zahl mit einer Länge von 1 Byte
bit
0 oder 1
SQL Server kennt keinen nativen booleschen Datentypen für WAHR oder FALSCH. Hierfür kann der Datentyp bit verwendet werden, der nur die Werte 0 (für FALSCH) und 1 (für WAHR) enthalten kann und der auch entsprechende Konvertierungen unterstützt. Datentypkonvertierungen werden weiter unten erklärt.
decimal(p,s) dec(p,s) numeric(p,s)
1038 + 1 bis 1038 1
Eine Zahl mit einer festen Anzahl von Dezimalstellen. Für diesen Datentypen kann die Präzision p und die Anzahl der Dezimalstellen s angegeben werden.
bigint int
bis 2
63
1
Eine ganze Zahl mit einer Länge von 8 Byte
bis 2
31
1
Eine ganze Zahl mit einer Länge von 4 Byte
bis 2
15
1
Eine ganze Zahl mit einer Länge von 2 Byte
Der Parameter p für die Präzision gibt die Gesamtanzahl von zu speichernden Stellen an. Der Wert für diesen Parameter muss zwischen 1 und 18 liegen. Der Parameter ist optional, der Standardwert für diesen Parameter ist 18. Der Parameter s für die Dezimalstellen gibt die Stellen rechts vom Komma an. Auch dieser Parameter ist optional, der Standardwert ist 0.
71
4 Transact SQL-Grundlagen Datentyp
Wertebereich
Erklärung Beispiel: Mit wird eine Zahl deklariert, die insgesamt fünf Stellen enthält, zwei davon als Nachkommastellen. Damit bleiben drei Stellen vor dem Komma, die Zahl hat also das Format:
308
308
float(n)
1,79 e
bis 1,79 e
real
3,4 e bis 3,4 e
Speichert eine Gleitkommazahl.
money
922.337.203.685.477,5808 bis 922.337.203.685.477,5807
Speichert einen Währungswert mit vier Nachkommastellen.
smallmoney
214.748,3648 bis 214.748,3647
Speichert einen Währungswert mit vier Nachkommastellen.
38
38
Speichert eine Gleitkommazahl. Über den Parameter n kann angegeben werden, wie viele Bits zum Speichern benutzt werden sollen. Die Angabe von n ist optional, der Standardwert ist 53.
Zeichenketten Tabelle 4.3 listet die zur Verfügung stehenden Datentypen für Zeichenketten auf. Für alle Zeichenketten-Datentypen gibt es auch eine Unicode-Variante. Dies ist jeweils der Datentyp, der mit dem Buchstaben n beginnt (siehe unten). Im T-SQL-Code können Sie Zeichenketten-Literale verwenden. Hierzu wird der betreffende Text in einfache Anführungszeichen eingeschlossen, also etwa so:
Tabelle 4.3 Datentypen für Zeichenketten
72
Datentyp
Wertebereich
Erklärung
char(n) nchar(n)
Maximal 8000 Zeichen (char) oder 4000 Zeichen (nchar)
Speichert eine Zeichenkette fester Länge. Der Parameter n gibt die Länge der Zeichenkette an und ist optional. Der Standardwert für eine Deklarationsanweisung ist 1. Für die Verwendung des Datentyps in einer Konvertierungsfunktion ist der Standardwert 30. Für char kann n eine maximale Größe von 8000 annehmen, für nchar 4000.
text ntext
Maximal 2.147.483.647 Zeichen (text) oder1.073.741.823 Zeichen (ntext)
Kann Textdaten variabler Länge enthalten. Die Verwendung dieser Datentypen ist relativ kompliziert, für Neuentwicklungen sollten Sie besser die Datentypen varchar(max) bzw. nvarchar(max) verwenden.
4.4 SQL-Datentypen Datentyp
Wertebereich
Erklärung
varchar(n) nvarchar(n)
Maximal 8000 Zeichen (char) oder 4000 Zeichen (nchar)
Speichert eine Zeichenkette variabler Länge. Der Parameter n gibt die maximale Länge der Zeichenkette an und ist auch hier optional. Der Standardwert für eine Deklarationsanweisung ist wiederum 1 für Deklarationen und 30 für Konvertierungsfunktionen. Das Maximum von n ist 8000 für varchar bzw. 4000 für nvarchar.
varchar(max) nvarchar(max)
Maximal 2.147.483.647 Zeichen (varchar) oder1.073.741.823 Zeichen (nvarchar)
Speichert Textdaten variabler Länge.
Falls Sie internationale Zeichen speichern müssen, sollten Sie jeweils den Unicode-Datentyp verwenden. Dieser Datentyp bereitet weniger Probleme bei der Zeichenkonvertierung, hat allerdings auch den doppelten Speicherplatzbedarf, da ein Unicode-Zeichen immer in zwei Byte abgelegt wird im Gegensatz zu einem normalen Zeichen, für das ein Byte ausreicht. Der in der Deklaration eines Datentyps angegebene Wert für n bezeichnet immer die Länge der Zeichenkette und nicht die Anzahl der Bytes, die zur Speicherung benötigt werden. Eine Deklaration steht für eine Zeichenkette von 100 Zeichen Länge und 100 Byte Speicherbedarf. In einer Variablen oder einer Spalte vom Typ kann ebenfalls eine Zeichenkette mit einer maximalen Länge 100 gespeichert werden, hier werden allerdings 200 Byte Speicherplatz benötigt. Bei der Deklaration von Zeichenketten ist auch zu bedenken, dass für die Datentypen und unterschiedliche Mechanismen für die Speicherverwaltung verwendet werden. Für einen -Datentypen wird immer der über den Parameter n angegebene Speicher benötigt, unabhängig davon, wie viele Zeichen tatsächlich gespeichert werden. Für den Datentyp erfolgt die Anforderung von Speicher dynamisch, der tatsächlich benötigte Speicher richtet sich also jeweils nach der Länge der zu speichernden Zeichenkette. Abbildung 4.4 verdeutlicht dies für eine maximale Länge von 15 Zeichen.
Abbildung 4.4 Speicherbedarf für char(15) und varchar(15)
Der Datentyp ist zu bevorzugen, wenn die Länge der gespeicherten Daten in einer Spalte jeweils gleich ist. Ein Beispiel hierfür wäre vielleicht eine Kunden- oder Rechnungsnummer. In so einem Fall kann die bei erforderliche dynamische Speicherverwaltung vermieden werden. Wenn sich die Dateneinträge in der Größe unterscheiden, ist die bessere Wahl.
73
4 Transact SQL-Grundlagen Datum und Zeit SQL Server kennt die in Tabelle 4.4 genannten Datentypen für Datum und Zeit. Es gibt für Datum und Uhrzeit keine separaten Datentypen, Zeit und Datum werden stets zusammen gespeichert. Tabelle 4.4 Datentypen für Datum und Zeit Datentyp
Wertebereich
Erklärung
datetime
01.01.1753 bis 31.12.9999
Der Zeitanteil wird mit einer Genauigkeit von 3,33 Millisekunden gespeichert.
smalldatetime
01.01.1900 bis 06.06.2079
Der Zeitanteil wird mit einer Genauigkeit von einer Minute gespeichert.
Binäre Datentypen In Tabelle 4.5 finden Sie die verfügbaren Datentypen zur Speicherung binärer Daten. Tabelle 4.5 Binäre Datentypen Datentyp
Wertebereich
Erklärung
binary(n)
1 bis 8000 Byte
Speichert binäre Daten fester Länge.
image
Maximal 2.147.483.647 Byte
Der Datentyp image sollte für Neuentwicklungen nicht mehr verwendet werden. Nehmen Sie stattdessen varbinary(max).
varbinary(n)
1 bis 8000 Byte
Speichert binäre Daten variabler Länge.
varbinary(max)
Maximal 2.147.483.647 Byte
Speichert ebenfalls binäre Daten variabler Länge.
Das bereits weiter oben für die Zeichenketten-Datentypen und bezüglich der dynamischen Speicherverwaltung Gesagte gilt ebenso für und . Verwenden Sie , wenn die Länge der gespeicherten Daten in einer Spalte gleich ist. Ist die Länge unterschiedlich, so ist der Datentyp zu bevorzugen. Weitere Datentypen SQL Server enthält noch einige weitere Datentypen, die in keine der bislang besprochenen Kategorien eingeordnet werden können. Tabelle 4.6 listet diese Datentypen auf. Tabelle 4.6 Sonstige Datentypen Datentyp
Erklärung
cursor
Dieser Datentyp kann nicht in Tabellen verwendet werden. Er steht lediglich für Variablen zur Verfügung. Cursor werden in Kapitel 7 behandelt.
sql_variant
Die Bezeichnung wurde in Anlehnung an den Visual Basic-Datentyp Variant gewählt. Eine Variable oder Spalte dieses Typs kann einen Wert eines beliebigen Datentyps speichern. Ausgenommen hiervon sind , ,
, , , , , und
74
4.4 SQL-Datentypen Datentyp
Erklärung
. Wenn möglich, sollten Sie die Verwendung dieses Datentyps vermeiden. table
Speichert eine temporäre Tabelle. Dieser Datentyp wird Insbesondere in sogenannten Tabellenwertfunktionen verwendet. Hierauf kommen wir in Kapitel 7 nochmals zurück. Als Spaltentyp in einer Tabellendeklaration kann dieser Datentyp nicht verwendet werden.
timestamp
Der Datentyp steht für einen 8 Byte langen Binärwert. Wenn Sie in einer Tabelle eine Spalte vom Typ verwenden, so wird diese Spalte automatisch aktualisiert, wann immer die Daten einer Zeile verändert werden. In jeder Datenbank existiert hierfür ein Zähler, der für eine Aktualisierung einer Tabelle inkrementiert wird, sofern diese Tabelle eine -Spalte enthält. Spalten können als Zeilenversionskennung verwendet werden. Wenn Sie beim Lesen einer Zeile aus einer Tabelle die -Spalte speichern und dann später beim Aktualisieren der Zeile feststellen, dass der Wert der -Spalte sich mittlerweile verändert hat, so ist die Zeile in der Zwischenzeit von einer anderen Anwendung verändert worden.
uniqueidentifier
Speichert einen 16 Byte langen GUID. Dieser Datentyp wird zum Beispiel für die Replikation verwendet.
xml
Variablen oder Spalten dieses Typs können XML-Daten aufnehmen. Dieser Thematik ist ein eigenes Kapitel (8) gewidmet.
Konvertierungen SQL Server erlaubt die Konvertierung von Werten eines Datentyps in einen anderen. Hierbei wird unterschieden zwischen einer impliziten und expliziten Konvertierung: Implizite Typkonvertierung. Bei der impliziten Typkonvertierung wird die Konvertierung aus dem Kontext abgeleitet. So ist es zum Beispiel möglich, einen Wert vom Typ einem Datentypen zuzuweisen oder eine Zeichenkette automatisch in einen -Wert zu überführen. Auch die Konvertierung einer Zeichenkette mit dem Wert oder in einen Datentyp kann implizit vorgenommen werden. Hierbei wird aus die 1 und aus der Wert 0. Insbesondere können Werte aller Datentypen den Wert NULL enthalten. Explizite Typkonvertierung. Für eine explizite Typkonvertierung stehen die beiden Konvertierungsfunktionen CAST und CONVERT zur Verfügung. Diese Funktionen werden wir in Abschnitt 4.15.6 näher untersuchen. Sowohl für die explizite als auch für die implizite Typkonvertierung gilt natürlich, dass keine Konvertierung aus prinzipiell unverträglichen Werten erfolgen kann. So wird es zum Beispiel nicht möglich sein, die Zeichenkette in einen Wert vom Typ oder in ein zu konvertieren. Bei beiden Versuchen wird eine Fehlermeldung erzeugt.
75
4 Transact SQL-Grundlagen
4.5
T-SQL-Ausdrücke und -Operatoren Wie jede andere Programmiersprache auch, erlaubt SQL die Berechnung von Ausdrücken. Hierzu steht Ihnen eine Reihe von Operatoren zur Verfügung, mit denen Berechnungen und Vergleiche durchgeführt werden können. Um Ihnen einen Überblick zu verschaffen, werden die verfügbaren Operatoren in diesem Abschnitt zunächst nur aufgezählt. Im weiteren Verlauf des Kapitels werden die Operatoren dann auch zum Einsatz kommen. Insbesondere beschäftigt sich Abschnitt 4.10 mit der Verwendung von Operatoren und Ausdrücken in SELECT-Anweisungen. Arithmetische Operatoren In Tabelle 4.7 finden Sie die zur Verfügung stehenden arithmetischen Operatoren. Tabelle 4.7 Arithmetische Operatoren von SQL Server Operator
Erklärung
+
Additionsoperator. Berechnet die Summe der beiden Operanden.
-
Subtraktionsoperator. Berechnet die Differenz der beiden Operanden.
*
Multiplikationsoperator. Berechnet das Produkt der Operanden.
/
Divisionsoperator. Berechnet den Quotienten der Operanden.
%
Modulooperator. Berechnet den Rest der ganzzahligen Division. Der Rest von 13 geteilt durch 5 ist 3, also liefert der Ausdruck 13 % 5 den Wert 3.
Bitweise Operatoren Die von SQL Server unterstützten bitweisen Operatoren finden Sie in der folgenden Tabelle 4.8. Tabelle 4.8 Bitweise Operatoren von SQL Server Operator
Erklärung
&
Bitweises Und.
|
Bitweises Oder.
^
Bitweises exklusives Oder.
~
Bitweise Negation.
Zeichenkettenoperatoren Tabelle 4.9 zeigt den einzigen für Zeichenketten verfügbaren Operator: Tabelle 4.9 SQL Server: Zeichenkettenoperatoren
76
Operator
Erklärung
+
Verkettungsoperator. Hängt zwei Zeichenketten aneinander.
4.5 T-SQL-Ausdrücke und -Operatoren Vergleichsoperatoren SQL Server erlaubt den Vergleich von Ausdrücken durch die in Tabelle 4.10 angeführten Vergleichsoperatoren: Tabelle 4.10 SQL Server: Vergleichsoperatoren Operator
Erklärung
=
Gibt WAHR zurück, wenn beide Seiten des Vergleichs gleich sind.
>
Gibt WAHR zurück, wenn die linke Seite größer als die rechte ist.
<
Gibt WAHR zurück, wenn die linke Seite kleiner als die rechte ist.
>=
Gibt WAHR zurück, wenn die linke Seite größer oder gleich als die rechte ist
<=
Gibt WAHR zurück, wenn die linke Seite kleiner oder gleich als die rechte ist.
<>
Gibt WAHR zurück, wenn beide Seiten des Vergleichs ungleich sind.
!=
Logische Operatoren Logische Ausdrücke kennen nur die beiden Zustände WAHR und FALSCH2. Beim Anwenden eines logischen Operators auf derartige Ausdrücke kann dieser Wertebereich nicht verlassen werden, das Ergebnis einer solchen Operation muss also wieder WAHR oder FALSCH liefern. Prinzipiell lassen sich alle logischen Operationen auf AND, OR und NOT zurückführen. Die folgende Tabelle 4.11 zeigt die Ergebnisse von AND- und OROperationen für alle möglichen Fälle. Tabelle 4.11 Logische Operationen mit AND, OR und NOT UND
ODER
NICHT
AND
WAHR
FALSCH
OR
WAHR
FALSCH
NOT
WAHR
WAHR
WAHR
FALSCH
WAHR
WAHR
WAHR
WAHR
WAHR
FALSCH
FALSCH
FALSCH
FALSCH
WAHR
FALSCH
Tabelle 4.12 enthält eine Übersicht über die logischen Operatoren von SQL. Solche Operatoren werden vor allem in Abfragen verwendet, um das Abfrageergebnis einzuschränken. In den weiteren Abschnitten dieses Kapitels und auch in Kapitel 7 werden wir diese Operatoren in eigenen Abfragen verwenden und dann jeweils näher darauf eingehen. Die Tabelle soll hier zunächst nur einen Überblick über die verfügbaren Operatoren liefern.
2
SQL bildet hier eine Ausnahme, da SQL-Ausdrücke prinzipiell auch den Wert NULL zurückliefern können. Die dadurch erforderliche dreiwertige Logik wird in Abschnitt 4.16 näher erklärt.
77
4 Transact SQL-Grundlagen Tabelle 4.12 Logische Operatoren von SQL Server Operator
Erklärung
ALL
Mengenvergleich. Gibt WAHR zurück, wenn der Vergleich mit allen in der Menge enthaltenen Werten WAHR ist.
AND
WAHR, wenn beide Seiten WAHR sind (siehe Tabelle 4.11).
ANY SOME
Mengenvergleich. Gibt WAHR zurück, wenn mindestens ein Vergleich mit den in der Menge enthaltenen Werten WAHR ist.
BETWEEN
WAHR, wenn der Operand in einem bestimmten Bereich liegt.
EXISTS
WAHR, wenn die bei EXISTS angegebene Unterabfrage mindestens eine Zeile zurückliefert.
IN
WAHR, wenn der Operand mit mindestens einem Wert aus einer angegebenen Liste übereinstimmt.
LIKE
WAHR, wenn der Operand gleich einem angegebenen Muster ist. (Mustervergleich für Zeichenketten.)
NOT
Logische Umkehrung (siehe Tabelle 4.11).
OR
WAHR, wenn eine der beiden Seiten WAHR ist (siehe Tabelle 4.11).
Zuweisungsoperator SQL kennt auch den Zuweisungsoperator =, mit dem der Wert eines Ausdrucks einem anderen Ausdruck zugewiesen werden kann. Rangfolge von Operatoren in Ausdrücken Da in einem Ausdruck mehrere Operatoren auftreten können, ergibt sich natürlich die Frage nach der Reihenfolge der Auswertung in so einem Fall. Diese Reihenfolge hat Auswirkungen auf das Ergebnis des Ausdrucks und wird durch eine festgelegte OperatorRangfolge definiert. Tabelle 4.13 zeigt die in SQL Server existierende Rangfolge für die weiter oben besprochenen Operatoren. Tabelle 4.13 Rangfolge von Operatoren
78
Rangfolge
Operatoren
1
Bitweise Negation: ~
2
Multiplikation: *, Division: /, % Modulo: %
3
Addition: +, Verkettung: +, Subtraktion: -, bitweises UND: &
4
Vergleichsoperatoren: =, >, <, >=, <=, <>, !=, !>, !<
5
Bitweise Operatoren ^, |
6
NOT
7
AND
8
ALL, ANY, BETWEEN, IN, LIKE, OR, SOME
9
=
4.6 Datenbanken verwalten Falls Sie sich die Reihenfolge der Operatoren nicht einprägen können, so ist es natürlich auch möglich, die Auswertungsreihenfolge durch das Setzen von runden Klammern () explizit zu bestimmen. In Zweifelsfällen sollten Sie stets hierauf zurückgreifen. Wenn die standardmäßige Auswertungsreihenfolge für Ihren Ausdruck nicht verwendet werden kann, so wird Ihnen keine andere Wahl bleiben, als die Reihenfolge durch Klammerung entsprechend zu verändern.
4.6
Datenbanken verwalten Bevor wir damit beginnen können, Daten im SQL Server abzulegen, müssen erst einmal die erforderlichen Objekte erzeugt werden, in denen letztlich diese Daten gespeichert werden sollen. Diese Objekte sind Tabellen, die ihrerseits innerhalb von Datenbanken verwaltet werden. In diesem Abschnitt werden wir uns damit befassen, wie Datenbanken angelegt, geändert und gelöscht werden.
4.6.1
Datenbank erzeugen
Das Anlegen einer Datenbank erfolgt durch die Anweisung CREATE DATABASE. Dieses Kommando hat eine Vielzahl von Optionen, mit denen vor allen Dingen das physikalische Datenbanklayout, aber auch die Standard-Sortierungsreihenfolge und diverse Sicherheitseinstellungen konfiguriert werden können. Dadurch kann eine CREATE DATABASE-Anweisung sehr komplex werden. Die gute Nachricht ist, dass alle Optionen auch weggelassen werden können, wodurch dann im Server hinterlegte Standardwerte für diese Optionen verwendet werden. Tatsächlich ist der Name der erzeugten Datenbank der einzige erforderliche Parameter. Eine Datenbank mit Standardeinstellungen kann also einfach so erstellt werden:
Durch das Kommando wird die Datenbank OmasKochbuch unter Verwendung der Systemdatenbank model als Vorlage erstellt. SQL Server nutzt für die Datenbankverwaltung das Dateisystem des Betriebssystems. Die für die Datenbank OmasKochbuch benötigten Dateien werden durch die obige CREATE DATABASE-Anweisung im Standarddatenverzeichnis des SQL Servers angelegt. Wenn Sie an der Serverkonfiguration nichts verändert haben, ist dies das Verzeichnis:
steht hierbei für das Programmverzeichnis des Computers. In diesem Verzeichnis finden Sie die beiden Dateien OmasKochbuch.mdf und OmasKochbuch_log.ldf. Für jede Datenbank müssen mindestens zwei Dateien existieren: eine Datendatei und eine Protokolldatei. In der Datendatei werden wie der Name bereits vermuten lässt die eigentlichen Daten der Datenbank gespeichert. Die Endung dieser Datei ist mdf. Die Proto-
79
4 Transact SQL-Grundlagen kolldatei mit der Endung ldf enthält Aufzeichnungen über alle in der Datenbank durchgeführten Änderungen. Das Protokoll wird für die Gewährleistung der Datensicherheit und auch für Backup-Strategien benötigt. In den Kapiteln 5 und 6 wird auf die Rolle des Protokolls näher eingegangen. Die Optionen zum physikalischen Datenbanklayout, also die Festlegung, wie eine Datenbank im Dateisystem verteilt werden soll, sind sicherlich diejenigen, die bei CREATE DATABASE am häufigsten verwendet werden. Es kann angegeben werden, wie viele Daten- und Protokolldateien angelegt werden sollen, wie diese Dateien heißen und in welchem Verzeichnis sie abgelegt werden sollen. Es ist möglich zu konfigurieren, auf welche Art und Weise die Dateien automatisch vergrößert werden sollen und ob eine maximale Größe nicht überschritten werden darf. Letztlich ist eine derartige Konfiguration jedoch mehr die Aufgabe des Datenbankadministrators. Für den Datenbankentwickler ist vorrangig die logische Sicht auf die Daten, also die Tabellenstruktur interessant. In welcher Datendatei Tabellendaten oder Protokolleinträge gespeichert werden, interessiert den Datenbankentwickler nur am Rande. Ein Tipp darf jedoch hier nicht fehlen: Aufgrund der unterschiedlichen Verwaltung für Daten- und Protokolldateien und auch, um im Falle eines Medienfehlers eine höhere Wahrscheinlichkeit für eine mögliche Wiederherstellung einer defekten Datenbank zu schaffen, ist es sehr sinnvoll, Daten- und Protokolldateien auf unterschiedlichen Laufwerken abzulegen. Hierfür können Sie die entsprechenden Optionen in der CREATE DATABASE-Anweisung wie folgt angeben:
Ersetzen Sie hierbei bitte und mit den Verzeichnissen für Daten- bzw. Protokolldatei. Die Klausel ON PRIMARY spezifiziert die primäre Dateigruppe für die Datenbank. Jede Datenbank muss genau eine solche primäre Dateigruppe besitzen, in der zumindest die Systemkataloginformationen für die Datenbank gespeichert werden. Die Option NAME gibt jeweils den logischen Dateinamen an, unter dem der SQL Server die physikalische Datei, die über FILENAME spezifiziert wird, verwaltet. Falls Ihnen CREATE DATABASE zu kompliziert ist, können Sie eine Datenbank auch mit dem Objekt-Explorer des Management Studios erzeugen. Öffnen Sie hierzu das Kontextmenü für den Ordner Datenbanken, und wählen Sie Neue Datenbank
(Abbildung 4.5).
80
4.6 Datenbanken verwalten
Abbildung 4.5 Eine Datenbank erzeugen
Es öffnet sich ein Fenster, in dem alle Optionen der anzulegenden Datenbank in einer grafischen Benutzeroberfläche konfiguriert werden können (Abbildung 4.6). Nach Abschluss der Konfiguration kann die Datenbank dann über den Button Hinzufügen erstellt werden. Es ist aber auch möglich, über den Button Skript in der Menüleiste eine CREATE DATABASE-Anweisung erstellen zu lassen und diese dann im Abfrageeditor auszuführen. Wenn Sie dies ein paar Mal gemacht haben, so werden Sie die CREATE DATABASE-Anweisung sehr bald beherrschen.
Abbildung 4.6 Optionen für neue Datenbank festlegen
81
4 Transact SQL-Grundlagen Der Objekt-Explorer zeigt die neue Datenbank im Ordner Datenbanken an (Abbildung 4.7). Möglicherweise müssen Sie aber hierfür noch die Anzeige über den Button Aktualisieren in der Menüleiste des Objekt-Explorers aktualisieren. In der Abbildung ist ersichtlich, dass eine neue Datenbank auch bereits Systemobjekte enthält, die für die Verwaltung der Datenbankobjekte erforderlich sind.
Abbildung 4.7 Die hinzugefügte Datenbank im Objekt-Explorer
Eine existierende Datenbank kann über das USE-Kommando als Kontextdatenbank für die nachfolgenden T-SQL-Anweisungen festgelegt werden:
Der ausgewählte Datenbankkontext hat Auswirkungen auf die Namensauflösung in T-SQLAnweisungen. Hierauf wird in Abschnitt 4.7.1 näher eingegangen.
4.6.2
Datenbank ändern
Eine bestehende Datenbank kann durch die Anweisung ALTER DATABASE geändert werden. Mit dieser Anweisung werden zum Beispiel Datenbankdateien hinzugefügt oder die Eigenschaften von Dateien verändert. ALTER DATABASE wird auch für die Änderung der Optionen oder des Namens einer Datenbank benötigt. ALTER DATABASE enthält viele Optionen und ist ähnlich komplex wie CREATE DATABASE. Oftmals wird für das Ändern von Optionen einer Datenbank ein exklusiver Zugriff auf diese benötigt. ALTER DATABASE wartet dann so lange, bis dieser exklusive Zugriff erfolgreich ist. Es ist aber auch möglich, in der Anweisung Optionen anzugeben, die alle anderen Verbindun-
82
4.6 Datenbanken verwalten gen zur Datenbank beenden, und so dafür zu sorgen, dass der exklusive Zugriff funktioniert. Es folgen einige Beispiele zur Verwendung von ALTER DATABASE: Im ersten Beispiel wird der Datenbankname verändert. Hierfür muss die Datenbank im Exklusivmodus verwendet werden. Es wird so lange gewartet, bis dieser Zugriff möglich ist:
Die folgende ALTER DATABASE-Anweisung setzt die Datenbank in den Offline-Status. Es sind dann keine Verbindungen mehr zur Datenbank möglich. Die Anweisung wartet zehn Sekunden auf den benötigten Exklusivzugriff. Kann dieser nicht erlangt werden, so werden laufende Transaktionen zurückgenommen und alle Verbindungen beendet (dann ist der Exklusivzugriff möglich):
Die nächste ALTER DATABASE-Anweisung setzt die Datenbank in den Nur-LesenStatus. Datenänderungen sind dann nicht mehr möglich.
Abbildung 4.8 Datenbankoptionen verändern
83
4 Transact SQL-Grundlagen Auch Datenbankoptionen können selbstverständlich im Eigenschaftsformular für die Datenbank, das über das Kontextmenü für die Datenbank geöffnet werden kann, verändert werden (Abbildung 4.8). Die Erstellung eines Skriptes für die konfigurierten Änderungen ist natürlich ebenfalls möglich.
4.6.3
Datenbank löschen
Um eine Datenbank zu löschen, verwenden Sie die Anweisung DROP DATABASE:
Eine Datenbank kann nur gelöscht werden, wenn ein exklusiver Zugriff auf diese Datenbank möglich ist. Sobald noch andere Verbindungen zu dieser Datenbank existieren, verwenden diese Verbindungen die Datenbank im Rahmen einer gemeinsamen Sperre. Eine gemeinsame Sperre verhindert den für das Löschen erforderlichen exklusiven Zugriff auf die Datenbank. Daher die USE-Anweisung am Anfang des obigen T-SQL-Skriptes, die dafür sorgt, dass zumindest die Verbindung, die das DROP DATABASE absetzt, die zu löschende Datenbank nicht verwendet. Die Anweisung DROP DATABASE versucht 20 Sekunden lang, exklusiven Zugriff auf die Datenbank zu erhalten. Gelingt dies nicht, so wird eine Fehlermeldung zurückgegeben. Um alle bestehenden Verbindungen zur Datenbank zu beenden, kann für die Datenbank der Einzelbenutzermodus erzwungen werden, bevor DROP DATABASE ausgeführt wird:
Die Option WITH ROLLBACK IMMEDIATE der ALTER DATABASE-Anweisung sorgt dafür, dass alle aktiven Verbindungen zur Datenbank sofort, also ohne auf den Abschluss laufender Transaktionen zu warten, beendet werden. Danach kann die Datenbank dann gelöscht werden.
4.7
Schemas verwenden Schemas sind Container, die Datenbankobjekte enthalten. Jede neu angelegte Datenbank enthält bereits eine Reihe von Standardschemas, darunter auch die Schemas dbo und sys.
84
4.7 Schemas verwenden Die existierenden Schemas finden Sie im Objekt-Explorer unter Sicherheit/Schemas (Abbildung 4.9).
Abbildung 4.9 Standardschemas in einer neuen Datenbank
Standardschemas können nicht gelöscht werden. Bevor etwas weiter unten das Erzeugen von Schemas mit CREATE SCHEMA erklärt wird, folgen noch einige grundlegende Aussagen zu Schemas: 1. Jedes Schema hat einen Besitzer. Wird der Besitzer bei der Erzeugung des Schemas nicht explizit angegeben, so ist das Schema im Besitz des Benutzers, der die CREATE SCHEMA-Anweisung ausführt. 2. Jeder Datenbankbenutzer besitzt ein Standardschema. Das Standardschema für einen Benutzer wird beim Anlegen oder Ändern eines Benutzers zugewiesen. Wenn dies nicht explizit erfolgt, einem Benutzer also kein Standardschema zugewiesen wird, so ist das Standardschema des Benutzers dbo. 3. Die meisten Datenbankobjekte werden in Schemas angelegt. Es gibt hier einige Ausnahmen, wie zum Beispiel Benutzer und Rollen. Wird bei der Erzeugung eines Datenbankobjektes das Schema nicht explizit angegeben, so wird das Objekt im Standardschema des Benutzers angelegt, der das Datenbankobjekt erzeugt. 4. Über ein Schema kann der Zugriff auf die enthaltenen Objekte kontrolliert werden. Für Schemas können Berechtigungen erteilt bzw. entzogen werden. Im Schema enthaltene Objekte erben diese Berechtigungen. Das Schema dbo ist das Standardschema des Datenbankbesitzers (database owner). Für diesen Datenbankbesitzer gibt es auch einen entsprechenden Benutzer, der ebenfalls dbo heißt. Der Benutzer dbo ist auch der Besitzer des Schemas dbo. Im Schema sys werden alle Systemobjekte der Datenbank abgelegt.
85
4 Transact SQL-Grundlagen Abbildung 4.10 verdeutlicht den Zusammenhang zwischen Schemas, Besitzer, Berechtigung und Objekt.
Abbildung 4.10 Schemas: Besitzer und Berechtigungen
Wie bereits erwähnt, wird ein Schema durch die Anweisung CREATE SCHEMA erzeugt, also zum Beispiel so:
In der ersten CREATE SCHEMA-Anweisung wird ein Schema mit dem Namen Marketing erzeugt. Über die Option AUTHORIZATION wird angegeben, dass der Datenbankbenutzer Meier der Besitzer dieses Schemas ist. Wird die AUTHORIZATIONOption nicht angegeben, wie dies in der zweiten Anweisung der Fall ist, so ist der aktuelle Datenbankbenutzer der Besitzer des Schemas. Zum Ändern eines Schemas können Sie die ALTER SCHEMA-Anweisung verwenden. Allerdings können mit dieser Anweisung lediglich Datenbankobjekte in ein anderes Schema verschoben werden. Hierzu kennt die Anweisung ALTER SCHEMA die TRANSFEROption:
Die obige ALTER SCHEMA-Anweisung verschiebt das Datenbankobjekt Umsatz aus dem Schema Verkauf in das Schema Marketing.
86
4.7 Schemas verwenden Zum Ändern des Schema-Besitzers verwenden Sie die Anweisung ALTER AUTHORIZATION. Um den Besitzer Schulze für das Schema Marketing festzulegen, kann diese Anweisung so verwendet werden:
Mit der Anweisung DROP SCHEMA wird ein Schema entfernt:
Hierbei ist zu beachten, dass das Schema nur gelöscht wird, wenn es keine Objekte mehr enthält. Ansonsten schlägt DROP SCHEMA fehl.
4.7.1
Namensauflösung
Auf ein Datenbankobjekt wird durch die Angabe des Objektnamens zugegriffen. Hierbei hat jedes Datenbankobjekt einen aus vier Teilen bestehenden Namen, der so aufgebaut ist:
In einem solchen voll qualifizierten Namen sind die einzelnen Bestandteile jeweils durch einen Punkt voneinander getrennt. Die vier Bestandteile des Namens haben die folgende Bedeutung: obkjektname. Der Objektname ist der Name des Datenbankobjektes, auf das zugegriffen werden soll, also zum Beispiel eine Tabelle oder eine gespeicherte Prozedur. Dieser Teil des Namens muss angegeben werden, ist also obligatorisch. schemaname. Der Name des Schemas, in dem das Datenbankobjekt existiert. Diese Angabe ist optional. Wird das Schema nicht angegeben, so wird das Objekt zunächst im Schema sys gesucht. Wenn das Objekt in diesem Schema nicht gefunden wird, so erfolgt im nächsten Schritt die Suche im Standardschema des Benutzers, der die Anweisung ausführt. Wird das Objekt auch in diesem Schema nicht gefunden, dann wird die Suche im Schema dbo fortgesetzt. Erst wenn auch dort das Objekt nicht gefunden werden kann, erfolgt eine Fehlermeldung. Diese Verfahrensweise ist aus Gründen der Abwärtskompatibilität zu früheren SQL Server-Versionen erforderlich. datenbankname. Der Name der Datenbank, in der das Objekt existiert. Auch diese Angabe ist optional. Wird der Name nicht angegeben, so wird der aktuelle Datenbankkontext verwendet. Dieser Kontext bezieht sich auf die über USE eingestellte Datenbank. servername. Hier wird der Name des SQL Servers angegeben, in dem das Objekt existiert. Die Angabe des Servernamens ist ebenfalls optional und darf nur erfolgen, wenn ein entsprechender Verbindungsserver eingerichtet wurde. Über Verbindungsserver kann auf Objekte entfernter Server zugegriffen werden, so als wenn diese Objekte auf dem lokalen Server existieren würden. In diesem Buch werden wir Verbindungsserver nicht verwenden, den Namensteil für den Servernamen also stets weglassen.
87
4 Transact SQL-Grundlagen Was Schemas anbelangt, so hat die geschilderte Namensauflösung vor allem zwei Konsequenzen: 1. Innerhalb einer Datenbank bildet ein Schema auch einen Namensraum für die enthaltenen Objekte. Dies bedeutet, dass zum Beispiel Tabellen gleichen Namens in unterschiedlichen Schemas existieren können. 2. Wenn Sie für den Zugriff auf Datenbankobjekte keinen Schemanamen angeben, so wird die automatische Namensauflösung anhand von existierenden Standardschemas durchgeführt. Da hierbei ein Datenbankobjekt eventuell über mehrere Ebenen gesucht werden muss, kann die Ausführungszeit Ihrer Abfragen darunter leiden. Geben Sie also nach Möglichkeit den Schemanamen mit an, und vertrauen Sie nicht auf die Automatik. In Abbildung 4.11 sehen Sie ein Beispiel für die Namensauflösung. Die Zeichnung zeigt die beiden Standardschemas sys und dbo sowie zwei benutzerdefinierte Schemas Marketing und Verkauf. In den beiden benutzerdefinierten Schemas ist jeweils ein Objekt (vielleicht eine Tabelle) T1 enthalten, auch im Schema dbo gibt es eine Tabelle mit diesem Namen. Im Szenario existieren weiterhin die beiden Benutzer Meier und Lehmann mit zugehörigen Standardschemas. Dargestellt sind vier verschiedene Fälle für den Zugriff auf die in der Datenbank existierenden Objekte Marketing.T1, Verkauf.T1, dbo.T1 und dbo.T2.
Abbildung 4.11 Schemas, Objekte und Namensauflösung
Die Pfeile zeigen hierbei jeweils den verwendeten Objektnamen wahlweise mit oder ohne Angabe des Schemas. In Tabelle 4.14 finden Sie die Beschreibung der vier dargestellten Fälle. Noch ein Tipp zum Schluss dieses Abschnitts: Versuchen Sie, in Ihren Datenbanken Objektnamen zu vermeiden, die auch im Schema sys existieren. Durch den Start der automatischen Namensauflösung im Schema sys wird immer ein dort enthaltenes Objekt gleichen Namens verwendet, wenn Sie das Schema nicht explizit angeben. Dies kann zu einiger Verwirrung führen.
88
4.7 Schemas verwenden Tabelle 4.14 Beispiele für die automatische Namensauflösung Fall
Szenario
Beschreibung
1
Der Benutzer Meier mit dem zugeordneten Standardschema Marketing greift auf das Datenbankobjekt T1 ohne Angabe des Schemanamens zu.
Da kein Schema angegeben wurde, wird T1 im ersten Schritt im Standardschema sys gesucht3. Diese Suche schlägt fehl und wird im Schema Marketing (dem Standardschema des Benutzers) fortgesetzt. Hier wird das Objekt gefunden.
2
Der Benutzer Lehmann mit dem zugeordneten Standardschema Verkauf greift auf das Datenbankobjekt T1 ohne Angabe des Schemanamens zu.
Auch hier wurde kein Schema angegeben, der Zugriff erfolgt also auf das Objekt T1 im Standardschema des Benutzers Lehmann.
3
Der Benutzer Lehmann mit dem zugeordneten Standardschema Verkauf greift auf das Datenbankobjekt Marketing.T1 zu, gibt also einen Schemanamen an.
Durch die Angabe des Schemanamens wird auf das Objekt T1 im Schema Marketing zugegriffen. Eine Objektsuche bzw. automatische Namensauflösung ist nicht erforderlich.
4
Der Benutzer Lehmann mit dem Standardschema Verkauf greift auf das Datenbankobjekt T2 ohne Angabe des Schemanamens zu.
T2 wird zunächst im Standardschema des Benutzers Lehmann gesucht. Da das Objekt hier nicht gefunden wird, wird die Suche im Schema dbo fortgesetzt. Auf das dort gefundene Objekt T2 wird dann zugegriffen.
4.7.2
Synonyme
Durch die Erstellung eines Synonyms kann auf ein Objekt in einer Datenbank unter einem alternativen Namen zugegriffen werden. Ein Synonym wird durch die Anweisung CREATE SYNONYM wie folgt erzeugt:
In der Datenbank OmasKochbuch existiert nun ein Synonym AWContact. Durch die obige CREATE SYNONYM-Anweisung wird dieses Synonym im Standardschema des Benutzers angelegt, der die Anweisung ausführt. Der exakte Name des Synonyms ist also <Standardschema>.AWContact, wobei <Standardschema> für das Standardschema des anlegenden Benutzers steht. Wann immer in dieser Datenbank der Name <Standardschema>.AWContact in SQL-Anweisungen benutzt wird, erfolgt der Zugriff auf die Tabelle Contact im Schema Person der Datenbank AdventureWorks. Die Anweisung DROP SYNONYM entfernt ein Synonym wieder:
3
Korrekt müsste die Suche im sys-Schema für alle Szenarien dargestellt werden. Für die Szenarien 2, 3 und 4 wurde dieser erste Schritt nur aus Gründen der Übersichtlichkeit weggelassen.
89
4 Transact SQL-Grundlagen Seien Sie bitte vorsichtig mit dem Löschen von Synonymen. Existierender T-SQL-Code (zum Beispiel in gespeicherten Prozeduren), in dem auf das Synonym zugegriffen wird, wird bei DROP SYNONYM nicht überprüft. Dieser Code wird also nach dem Entfernen des Synonyms nicht mehr funktionieren.
4.8
Tabellen entwerfen Letztlich werden Benutzerdaten in Tabellen abgelegt und aus diesen auch wieder abgefragt. Tabellen sind damit unzweifelhaft die wesentlichsten Objekte einer jeden Datenbank. Eine Tabelle besteht aus einer Anordnung von Zeilen und Spalten, wobei für jede Spalte einer Tabelle ein möglicher Wertebereich angegeben werden kann. Dieser Wertebereich wird zunächst einmal durch den Datentyp bestimmt, der für jede Spalte angegeben werden muss. Zusätzlich können dann noch Einschränkungen festgelegt werden, die den Wertebereich weiter eingrenzen.
4.8.1
Tabellen anlegen
Für das Anlegen von Tabellen verwenden Sie die Anweisung CREATE TABLE. Die Anweisung ermöglicht neben der Definition der Tabellenstruktur auch die Festlegung von Optionen, die spezifizieren, in welcher Dateigruppe die Tabelle erzeugt werden soll. Diese Optionen können allerdings auch weggelassen werden, wodurch die Tabellendaten in der Standarddateigruppe abgelegt werden. In diesem Fall sieht das Kommando CREATE TABLE einfach so aus:
Auf die CREATE TABLE-Anweisung folgen zunächst der Name der zu erzeugenden Tabelle und dann in Klammern eine durch Komma getrennte Liste von Spaltendefinitionen: . Dies ist der Name der zu erzeugenden Tabelle. Dieser Name kann auch den Namen einer Datenbank und eines Schemas enthalten. Werden diese Anteile nicht angegeben, so erfolgt die automatische Namensauflösung wie im vorigen Abschnitt geschildert. Allerdings wird die Tabelle niemals im Schema sys abgelegt. Wird kein Schema angegeben, so wird die Tabelle im Standardschema desjenigen Benutzers angelegt, der die CREATE TABLE-Anweisung ausführt. <spaltenname>. Dies ist der Name einer Tabellenspalte. Ein Spaltenname muss den Regeln für Bezeichner genügen, darf also zum Beispiel nicht mit einer Ziffer beginnen. <spaltendefinition>. In der Spaltendefinition muss zumindest der Datentyp der Spalte angegeben werden. Über weitere Optionen ist es dann zum Beispiel auch möglich, den Wertebereich also die für die Spalte gültigen Werte einzuschränken. Hiermit werden wir uns noch im Verlauf dieses Abschnitts naher befassen.
90
4.8 Tabellen entwerfen Nun aber genug der Vorrede. Wir wollen nun zu unserer Datenbank OmasKochbuch eine Tabelle Rezept hinzufügen. Die Tabelle soll zunächst drei Spalten enthalten, in denen Daten für den Namen des Rezeptes, den Energiegehalt in Joule und eine Einstufung des Geschmackes des im Rezept genannten Gerichtes gespeichert werden. Diese Einstufung soll einfach durch eine Ziffer von 1 bis 6 repräsentiert werden, so wie eine Schulnote. Hierzu wird die folgende einfache CREATE TABLE-Anweisung verwendet, die auch eine Besonderheit von CREATE SCHEMA aufzeigt. Innerhalb einer CREATE SCHEMAAnweisung kann nämlich auch eine Tabelle angelegt werden:
Die Tabelle Rezept wird im angelegten Schema Kochen erzeugt. Diese Tabelle enthält drei Spalten Bezeichnung, Joule und Bewertung. Für alle drei Spalten wurde ein Datentyp angegeben, der spezifiziert, dass eine Bezeichnung eine Zeichenkette mit der maximalen Länge von 100 Zeichen ist und dass die Spalten Joule und Bewertung ganze Zahlen enthalten müssen. Im Objekt-Explorer wird die erzeugte Tabelle mit den drei Spalten angezeigt (Abbildung 4.12). Eventuell muss zuvor noch die Anzeige aktualisiert werden. Hierzu können Sie in der Menüleiste des Objekt-Explorers einfach den Button Aktualisieren drücken.
Abbildung 4.12 Die Tabelle Kochen.Rezept im Objekt-Explorer
Grafischer Tabellendesigner Das SQL Server Management Studio stellt Ihnen einen grafischen Tabellendesigner zur Verfügung, der es Ihnen gestattet, eine Tabelle über eine komfortable Benutzeroberfläche zu erstellen oder auch zu ändern. Dieser Designer wird zum Beispiel über das Kontextmenü der Datenbank geöffnet (Abbildung 4.13).
91
4 Transact SQL-Grundlagen
Abbildung 4.13 Eine Tabelle anlegen
In Abbildung 4.14 sehen Sie den Tabellendesigner, in dem Spalten- und Tabelleneigenschaften konfiguriert werden können.
Abbildung 4.14 Der Tabellendesigner des SQL Server Management Studios
Die weiter oben über die Anweisung CREATE TABLE erzeugte Tabelle Kochen.Rezept kann also auch über den Tabellendesigner angelegt werden. Hierfür werden zunächst einfach die Spalten eingegeben, welche die Tabelle enthalten soll. Die obere Liste des Tabellendesigners erlaubt die Eingabe von Spaltennamen und Datentyp. In der dritten Spalte der Liste wird angegeben, ob eine Spalte auch den Wert NULL enthalten, also leer sein darf.
92
4.8 Tabellen entwerfen Im unteren Teil Spalteneigenschaften werden dann zusätzliche Eigenschaften für die einzelnen Tabellenspalten konfiguriert. Hier kann zum Beispiel eine Beschreibung für die Spalte eingegeben werden. Möglich ist dort auch die Festlegung von Optionen für die Replikation der Tabelle. Ist das Tabellenlayout fertig, kann die neue oder geänderte Tabelle durch einen Klick auf das Diskettensymbol in der Menüleiste in die Datenbank übernommen werden. Bei einer neuen Tabelle werden Sie zunächst aufgefordert, einen Tabellennamen einzugeben. Die Tabelle wird dann in der Datenbank gespeichert. Es ist auch hier wieder möglich, ein Skript zu generieren, das die Änderungen als T-SQL-Code enthält. Wählen Sie hierzu aus dem Menü Tabellen-Designer/Änderungsskript generieren
(Abbildung 4.15).
Abbildung 4.15 Ein Skript für die Änderung einer Tabelle generieren
Im anschließenden Dialog haben Sie die Möglichkeit, das erzeugte Skript sofort auszuführen oder für eine spätere Ausführung zu speichern.
4.8.2
Tabellen ändern
Die im weiteren Verlauf dieses Abschnittes besprochenen Aktionen zum Ändern der Tabellenstruktur können selbstverständlich ebenfalls mit dem Tabellendesigner durchgeführt werden. Da dies jedoch ein Buch über Datenbankentwicklung mit T-SQL ist, wollen wir den Tabellendesigner nicht weiter benutzen, sondern die entsprechenden T-SQL-Kommandos verwenden. Bestehende Tabellen können über die ALTER TABLE-Anweisung geändert werden. Mit ALTER TABLE können einzelne Spalten der Tabelle geändert, gelöscht oder hinzugefügt werden. Es ist auch möglich, Optionen zur physikalischen Speicherung der gesamten Tabelle zu konfigurieren, also letztlich festzulegen, in welcher Datei eine Tabelle gespeichert wird. Insbesondere können über ALTER TABLE Einschränkungen auf Spalten- und Tabellenebene vergeben werden. Durch solche Einschränkungen können Sie zum Beispiel den Wertebereich für einzelne Spalten reduzieren. Etwas weiter unten werden Sie sehen, wie dies funktioniert.
93
4 Transact SQL-Grundlagen Spalten hinzufügen mit ADD Zum Hinzufügen von Spalten zu einer Tabelle verwenden Sie die ADD-Klausel der ALTER TABLE-Anweisung. Angenommen, unsere Tabelle Kochen.Rezept soll eine weitere Spalte enthalten, in der gespeichert werden soll, wann das Gericht das letzte Mal gekocht wurde. Hierzu wollen wir eine Spalte ZuletztGekochtAm vom Datentyp hinzufügen. Die entsprechende ALTER TABLE-Anweisung sieht so aus:
Interessanterweise heißt die Klausel zum Hinzufügen von Spalten nur ADD und nicht etwa ADD COLUMN. Spalten ändern mit ALTER COLUMN Zum Ändern von Spalten verwenden Sie die Klausel ALTER COLUMN der Anweisung ALTER TABLE. Durch ALTER COLUMN kann der Datentyp der Spalte geändert werden. Es ist weiterhin möglich festzulegen, ob eine Spalte auch leer sein darf, also den Wert NULL enthalten kann. Das Ändern des Datentyps funktioniert allerdings nur dann, wenn die beiden beteiligten Datentypen kompatibel sind. Dies bedeutet, dass eine implizite Typkonvertierung des alten in den neuen Datentyp möglich sein muss. Ist dies nicht der Fall, so erhalten Sie beim Ausführen einer entsprechenden ALTER TABLE
ALTER COLUMN-Anweisung eine Fehlermeldung. Ebenso gilt für das Ändern der NULLZulässigkeit, dass die Änderung auf nicht zulässig nur erlaubt ist, wenn in der Spalte bislang noch keine NULL-Werte enthalten sind. Für die im vorigen Absatz hinzugefügte Spalte ZuletztGekochtAm wurde der Datentyp angegeben. Wenn Sie sich noch einmal Tabelle 4.4 ansehen, so werden Sie vielleicht feststellen, dass der Wertebereich des Datentyps für diese Spalte ausreichend ist. Um den Datentyp der Spalte ZuletztGekochtAm in zu ändern, verwenden Sie ALTER TABLE wie folgt:
Der Spaltenname selber kann übrigens durch die ALTER COLUMN Klausel nicht geändert werden. Spalten löschen mit DROP COLUMN Die DROP COLUMN-Klausel der ALTER TABLE-Anweisung ermöglicht das Löschen von Spalten aus einer Tabelle. Um zum Beispiel die Spalte ZuletztGekochtAm aus der Tabelle Kochen.Rezept zu entfernen, geben Sie die folgende Anweisung an:
94
4.8 Tabellen entwerfen
4.8.3
Identität/Primärschlüssel
Für die Arbeit mit Tabellen ist es essenziell, dass jede Tabellenzeile von allen anderen Zeilen unterschieden werden kann, oder mit anderen Worten: Eine einzelne Tabellenzeile muss eindeutig identifizierbar sein, eine eigene Identität besitzen. Anderenfalls werden Aktualisierungs- oder Löschoperationen für einzelne, definierte Tabellenzeilen nicht möglich sein, da eine Selektion der für diese Operationen erforderlichen Zeilen unmöglich ist. Eine Zeilenidentität für eine Tabelle wird durch die Vergabe eines Primärschlüssels für diese Tabelle eingeführt. Ein solcher Primärschlüssel besteht letztlich aus einer Aneinanderreihung von Spalten, die zusammengenommen eine Tabellenzeile als eindeutig kennzeichnen. Zum Beispiel könnte in einer Tabelle mit Personendaten die Kombination von Nachname und Vorname als Primärschlüssel definiert werden vorausgesetzt, es gibt keine Personen mit gleichen Nach- und Vornamen. In den meisten Fällen besteht der Primärschlüssel allerdings nur aus einer Spalte, die oftmals auch eigens für diesen Zweck, nämlich für die Verwendung als Primärschlüssel, definiert wurde. In der erwähnten Personentabelle könnte hierfür zum Beispiel eine Personalnummer eingeführt werden, die für jede Person eindeutig vergeben wird. Weitere bekannte Beispiele für eine solche Verfahrensweise sind Kunden- oder Rechnungsnummern in Warenwirtschaftssystemen, die ebenso für die Verwendung als Primärschlüssel hier auch außerhalb der Datenbank eingeführt werden. Bei diesen Nummern versteht es sich von selbst, dass doppelte Werte nicht zulässig sind und somit einer Tabellenzeile für die Kunden- oder Rechnungstabelle eine Zeilenidentität verleihen. Die Wahl eines geeigneten Primärschlüssels für eine Tabelle ist eine wichtige Aufgabe beim Design einer Datenbank. Wesentlich ist vor allem, dass ein Primärschlüssel gefunden wird, dessen Spalten später nicht aktualisiert werden müssen. Eine solche Aktualisierung bringt stets große Probleme mit sich, wie Sie sich sicherlich am Beispiel der erwähnten Kunden- bzw. Rechnungsnummern als Primärschlüssel ausmalen können. Prinzipiell wird zwischen natürlichen und künstlichen Primärschlüsseln unterschieden. Ein natürlicher Primärschlüssel identifiziert ein Objekt auch außerhalb der Datenbank. Beispiele hierfür sind Personalausweisnummer oder Postleitzahl. Natürliche Primärschlüssel können Probleme bereiten, da sie durch Menschen interpretierbar sind. Allein durch diese Möglichkeit sind sie anfällig für Änderungen. Auf der anderen Seite kann durch die Verwendung von natürlichen Primärschlüsseln oftmals eine Tabelle in einer Abfrage eingespart werden. Ein künstlicher Primärschlüssel ist eine eigens für die Verwendung als Primärschlüssel geschaffene Größe, die in einer Spalte einer Tabelle abgebildet wird. Ein solcher Primärschlüssel definiert ein Objekt lediglich innerhalb der Datenbank. SQL Server unterstützt die Vergabe von künstlichen Primärschlüsseln durch die Möglichkeit, automatische Zählwerte für Spalten festlegen zu können. Ein künstlicher Primärschlüssel hat absolut keine Bedeutung für Anwender und wird daher mit großer Wahrscheinlichkeit auch nicht von diesen geändert werden. Ein Nachteil bei der Verwendung eines künstlichen Primärschlüssels ist, dass in Abfragen oftmals eine zusätzliche Tabelle aufgenommen werden muss.
95
4 Transact SQL-Grundlagen Ein Primärschlüssel ist eine Tabelleneinschränkung, die zum Beispiel durch die Anweisung ALTER TABLE oder bereits beim Anlegen der Tabelle über CREATE TABLE festgelegt werden kann. Für in einem Primärschlüssel verwendete Spalten gelten hierbei die folgenden Einschränkungen: Die Spalten dürfen per Deklaration keine NULL-Werte erlauben. Die Spalten dürfen keine doppelten Werte enthalten. Außerdem darf für jede Tabelle nur ein Primärschlüssel angelegt werden. Wir wollen nun für die Tabelle Kochen.Rezept einen Primärschlüssel hinzufügen und suchen in den Spalten der Tabelle zunächst nach einem geeigneten Kandidaten für diesen Primärschlüssel. Möglicherweise könnte die Bezeichnung des Rezeptes als Primärschlüssel verwendet werden, da wir davon ausgehen können, dass die Rezeptliste keine doppelten Gerichte enthalten soll. Allerdings kann diese Bezeichnung 100 Zeichen lang sein. Wann immer eine Zeile der Tabelle Kochen.Rezept in einer SQL-Abfrage identifiziert werden soll, muss dann also eine bis zu 100 Zeichen lange Zeichenkette für diese Identifikation eingegeben werden. Allein das macht die Verwendung dieses Primärschlüssels extrem unhandlich. Außerdem sollte ein Primärschlüssel möglichst kurz sein und vor allem niemals geändert werden und diese beiden Eigenschaften würde der Primärschlüssel ganz sicher nicht aufweisen. Auch die anderen Spalten sowie eine eventuelle Kombination aus diesen sind nicht als Primärschlüssel geeignet. Aus diesem Grund soll ein künstlicher Primärschlüssel eingeführt werden, der für jedes Rezept eine Nummer enthält. Hierzu fügen wir zunächst eine Spalte RezeptId vom Typ zur Tabelle hinzu:
Anschließend kann diese Spalte als Primärschlüssel definiert werden. Hierzu wird eine PRIMARY KEY-Einschränkung zur Tabelle hinzugefügt. Die generelle Syntax der entsprechenden ALTER TABLE-Anweisung ist wie folgt:
und stehen hier stellvertretend für den Namen der
Tabelle, für die der Primärschlüssel erzeugt werden soll, und die Bezeichnung des Primärschlüssels. ist eine durch Kommata getrennte Auflistung der für den Primärschlüssel verwendeten Spalten. In unserem Fall wird nur die gerade hinzugefügte Spalte RezeptId verwendet, die ALTER TABLE-Anweisung sieht also so aus:
Der Primärschlüssel wird hier PK_Rezept genannt. Selbstverständlich können Sie diesen Namen frei wählen. Üblich ist es aber, den Namen für einen Primärschlüssel mit den Buchstaben PK (für Primary Key) zu beginnen.
96
4.8 Tabellen entwerfen Es ist auch möglich, eine Spalte sofort beim Hinzufügen zu einer Tabelle als Primärschlüssel zu definieren, also die beiden obigen Schritte zu einem zusammenzufassen. In unserem Beispiel würde eine solche Definition wie folgt aussehen:
Hierbei vergibt SQL Server automatisch einen Namen für den Primärschlüssel (der aber ebenfalls mit den Buchstaben PK_ beginnt). Ein Primärschlüssel kann auch bereits beim Anlegen einer Tabelle mit CREATE TABLE definiert werden. Hierfür gibt es zwei Möglichkeiten: Erstreckt sich der Primärschlüssel nur über eine Spalte und stören Sie sich nicht an dem vom SQL Server automatisch vergebenen Namen für den Primärschlüssel, so können Sie einfach den Zusatz PRIMARY KEY zur Deklaration der Spalte hinzufügen. Dies ist die gerade etwas weiter oben verwendete Verfahrensweise:
Die andere Möglichkeit bietet die Verwendung der CONSTRAINT-Klausel innerhalb der CREATE TABLE-Anweisung. Hier haben Sie dann auch die Möglichkeit, den Primärschlüssel zu benennen sowie mehrere Spalten in den Schlüssel aufzunehmen:
Die Tabelle Kochen.Rezept verfügt nun über einen Primärschlüssel auf der Spalte RezeptId. Jede Zeile in dieser Tabelle muss daher anhand des Wertes in dieser Spalte eindeutig identifizierbar sein. Insbesondere dürfen keine doppelten Werte in dieser Spalte existieren. Die Sicherstellung dieser Einschränkung von Seiten der Anwendung, die Daten in die Tabelle einfügt, ist natürlich alles andere als einfach. Woher soll denn eine Anwendung wissen, welcher Wert in der Spalte RezeptId noch nicht existiert und daher für das Hinzufügen einer Zeile verwendet werden darf? Dieses Problem verschärft sich in einer Mehrbenutzerumgebung wenn also von mehreren Anwendungen gleichzeitig Zeilen eingefügt werden natürlich noch. Außerdem ist es sicherlich wünschenswert, wenn die RezeptId fortlaufend vergeben, also einfach hochgezählt wird. IDENTITY-Spalten SQL Server bietet die Möglichkeit, dieses Inkrementieren zu übernehmen und somit die Werte für eine Primärschlüsselspalte selbstständig zu vergeben. Eine Tabellenspalte kann
97
4 Transact SQL-Grundlagen hierfür bei der Deklaration mit dem Zusatz IDENTITY versehen werden. Hierbei werden über zwei Parameter der Startwert und die Schrittweite für die automatische Vergabe der Nummern angegeben. Das folgende Beispiel deklariert eine solche Spalte mit einem Startwert von 1000 und einer Schrittweite von 1:
Eine mit der Option IDENTITY deklarierte Spalte wird von SQL Server für jede eingefügte Zeile automatisch inkrementiert und kann somit als Zeilenidentität verwendet werden. Allerdings ist eine IDENTITY-Spalte nicht automatisch auch der Primärschlüssel für die Tabelle, dieser wird stets über eine entsprechende PRIMARY KEY-Klausel festgelegt. Eine Definition der Spalte Id aus dem obigen Beispiel als Primärschlüssel muss explizit erfolgen, also zum Beispiel so:
Da SQL Server die Werte für IDENTITY-Spalten automatisch vergibt, können Werte in diese Spalten normalerweise nicht eingefügt werden. Für derartige Einfügeoperationen muss zuvor die Option IDENTITY_INSERT für die Tabelle aktiviert werden.
4.8.4
Weitere Einschränkungen
Neben dem Primärschlüssel können sowohl für Spalten als auch für die gesamte Tabelle weitere Einschränkungen festgelegt werden. SQL Server sorgt dafür, dass bei Datenänderungsoperationen keine dieser Einschränkungen verletzt wird, und stellt so die Integrität Ihrer Daten sicher. UNIQUE UNIQUE ist eine Einschränkung auf Tabellenebene. Genauso wie für Primärschlüssel werden auch durch UNIQUE-Einschränkungen Spalten festgelegt, die innerhalb der Tabelle eindeutig sein müssen, also keine gleichen Werte in verschiedenen Zeilen zulassen. Eine UNIQUE-Einschränkung wird daher oftmals auch als Sekundärschlüssel bezeichnet. Eine Tabelle kann hierbei auch mehrere UNIQUE-Einschränkungen enthalten. Dabei sind auch NULL-Werte in einer Spalte zulässig (siehe Abschnitt 4.16). UNIQUE-Einschränkungen können über ALTER TABLE oder auch CREATE TABLE festgelegt werden. Die Syntax ist dabei ganz ähnlich der schon bei der Erstellung von Primärschlüsseln besprochenen:
98
4.8 Tabellen entwerfen
Der im zweiten Beispiel bei der Erzeugung der Tabelle T2 verwendete Name für die UNIQUE-Einschränkung beginnt mit den Buchstaben UQ_ und hält sich damit an die Namenskonvention für diesen Einschränkungstyp. Wie bereits weiter oben erwähnt kann eine UNIQUE-Einschränkung auch nachträglich einer Tabelle hinzugefügt werden. Selbstverständlich dürfen die betreffenden Spalten dann keine Duplikate aufweisen. Um in unserer Tabelle Kochen.Rezept keine doppelten Bezeichnungen für ein Rezept zuzulassen, fügen wir eine entsprechende UNIQUE-Einschränkung für die Spalte Bezeichnung hinzu. Die erforderliche ALTER TABLE-Anweisung erinnert wieder an die bereits im Abschnitt über Primärschlüssel verwendete:
Eine existierende Einschränkung kann über DROP CONSTRAINT wie folgt entfernt werden:
NULL und NOT NULL Über die Spalteneinschränkungen NULL bzw. NOT NULL wird festgelegt, ob eine Spalte auch NULL-Werte enthalten darf. Nach Möglichkeit sollten Sie keine leeren Spalten verwenden, manchmal lässt sich dies allerdings nicht vermeiden. In Abschnitt 4.16 erfahren Sie hierzu mehr. Um zum Beispiel für unsere Spalte Bezeichnung keine NULL-Werte zuzulassen Kochrezepte ohne Bezeichnung sind in der Tabelle nicht sinnvoll , kann über eine ALTER TABLE-Anweisung die entsprechende Einschränkung hinzugefügt werden:
Für jede innerhalb von CREATE TABLE oder ALTER TABLE angelegte Spalte wird immer festgelegt, ob diese Spalte auch NULL-Werte aufnehmen kann. Wenn Sie dies nicht explizit angeben, so erfolgt es implizit, so wie in unseren bisherigen Beispielen. Abhängig von der Einstellung der Option wird automatisch NULL oder NOT NULL zur Spaltendeklaration hinzugefügt. Ist die Option ein (ON), so wird NULL addiert, wenn die Option aus (OFF) ist, NOT NULL.
99
4 Transact SQL-Grundlagen
In den die Datenbankeigenschaften können Sie ebenfalls festlegen, wie der Standard sein soll. Öffnen Sie das Eigenschaftsfenster für die Datenbank aus dem Kontextmenü. Auf der Seite Optionen finden Sie die Option ANSI Null Default, die ein- oder ausgeschaltet werden kann (Abbildung 4.16).
Abbildung 4.16 Festlegen der Datenbankoption ANSI NULL Default
Ich kann Ihnen nur raten, sich nicht auf die Standardeinstellungen zu verlassen und stattdessen immer explizit anzugeben, ob eine Spalte NULL-Werte enthalten darf oder nicht. Der nur minimal erhöhte Schreibaufwand ist die Mühe wert, denn Sie erreichen dadurch eine größere Unabhängigkeit von globalen Einstellungen. CHECK Über eine CHECK-Einschränkung können Bedingungen angegeben werden, die Tabellenzeilen oder auch einzelne Spalten erfüllen müssen. Die CHECK-Einschränkung kann als Spalten- oder Tabelleneinschränkung angelegt werden. Sie ist wie gewohnt über ALTER TABLE oder CREATE TABLE konfigurierbar. Eine Einschränkung auf Tabellenebene kann bei CREATE TABLE als separate CONSTRAINT-Klausel angegeben werden:
100
4.8 Tabellen entwerfen
Über die CHECK-Option wird die Bedingung angegeben, die erfüllt sein muss, damit eine Tabellenzeile gültig ist. Mit anderen Worten: Die obige Tabelle T1 darf nur Zeilen enthalten, in denen der Wert der Spalte C1 größer als der Wert der Spalte C2 ist. Der Name der Einschränkung beginnt mit CK_ und folgt damit wieder der Konvention. Es soll nun eine Einschränkung für unsere Tabelle Kochen.Rezept hinzugefügt werden, die dafür sorgt, dass in die Spalte Geschmack nur die Werte 1 bis 6 eingetragen werden dürfen. Zuvor soll der Datentyp für diese Spalte noch auf geändert werden. Außerdem wollen wir dafür sorgen, dass NULL-Werte nicht mehr erlaubt sind. Die entsprechenden ALTER TABLE-Anweisungen sehen so aus:
Beim Erstellen einer Tabelle über CREATE TABLE können in der Spaltendeklaration ebenfalls CHECK-Einschränkungen angegeben werden, die sich dann wiederum nur auf die Spalte beziehen und automatisch benannt werden. Für die Spalte Bewertung hätten wir beim Anlegen der Tabelle auch so vorgehen können:
Der Ausdruck für die CHECK-Einschränkung verwendet hier den BETWEEN-Operator. Der Ausdruck gibt WAHR zurück, wenn die Bewertung zwischen 1 und 6 liegt. DEFAULT Über die DEFAULT-Option definieren Sie einen Standardwert für eine Spalte. Die Spalte erhält diesen Wert bei Einfügeoperationen immer dann, wenn kein expliziter Wert angegeben wird. Alle Spalten, für die kein Standardwert über DEFAULT angegeben wird, erhalten implizit NULL als Standard. Dies gilt auch, wenn die Spalte an sich keine NULLWerte erlaubt. Anders gesagt: Werte für Spalten, die als NOT NULL und ohne DEFAULT deklariert werden, müssen bei Einfügeoperationen immer angegeben werden.
101
4 Transact SQL-Grundlagen Am einfachsten geben Sie die DEFAULT-Option bei der Spaltendeklaration in der CREATE TABLE-Anweisung an:
Für die Spalte C1 wird der Standardwert 0 angegeben. Der Wert dieser Spalte wird dadurch bei Einfügeoperationen immer auf 0 gesetzt, wenn für die Spalte kein expliziter Wert angegeben wird. Der angegebene DEFAULT-Wert muss übrigens keine Konstante sein. Er kann zum Beispiel auch durch eine der in Abschnitt 4.15 angegebenen Funktionen berechnet werden. Es ist auch natürlich ebenfalls möglich, eine DEFAULT-Einschränkung zu einer existierenden Spalte hinzuzufügen. Hierzu verwenden Sie ALTER TABLE in der folgenden Form:
Pro Spalte kann selbstverständlich nur ein Standardwert vergeben werden. Wenn für eine Spalte bereits ein Standardwert existiert, so schlägt die ADD DEFAULT-Option fehl. Beim Festlegen von DEFAULT-Einschränkungen sollten Sie beachten, dass eine Typenkompatibilität der Spaltendeklaration mit dem DEFAULT-Wert gewährleistet ist. So kann zum Beispiel die folgende Tabelle ohne Probleme erstellt werden:
Obwohl der DEFAULT-Wert nicht in ein passt, wird ein Fehler nicht beim Anlegen der Tabelle, sondern erst beim Einfügen einer Zeile ohne explizite Angabe eines Wertes für die Spalte C1 erzeugt.
4.8.5
Berechnete Spalten
SQL Server erlaubt berechnete Spalten in Tabellen. Solche Spalten enthalten lediglich einen Ausdruck, durch den der Spaltenwert aus anderen Tabellenspalten berechnet wird. Dieser Ausdruck wird nach dem Spaltennamen und dem Schlüsselwort AS angegeben:
In der Tabelle Person wird eine berechnete Spalte Name deklariert, die den kompletten Namen einfach durch eine Verkettung der Spaltenwerte für Nachname und Vorname durch ein Komma getrennt berechnet. Eine berechnete Spalte benötigt keinen Speicherplatz zur Speicherung der Spaltenwerte.
102
4.8 Tabellen entwerfen
4.8.6
Ein Wiedersehen mit CREATE TABLE
Nachdem Sie nun wissen, wie Tabellen- und Spalteneinschränkungen beim Anlegen einer Tabelle festgelegt werden, können wir die CREATE TABLE-Anweisung für das Erzeugen der Tabelle Kochen.Rezept mit allen erforderlichen Einschränkungen konstruieren. Fassen wir noch einmal zusammen, wie diese Tabelle aufgebaut sein soll. Tabelle 4.15 zeigt zunächst noch einmal die Spalten für Kochen.Rezept: Tabelle 4.15 Spalten für die Tabelle Kochen.Rezept Spalte
Datentyp
NULL-Werte?
Standardwert
RezeptId
int
Nein
Keiner
Bezeichnung
varchar(100)
Nein
Keiner
Joule
smallint
Nein
0
Bewertung
smallint
Nein
Keiner
ZuletztGekochtAm
smalldatetime
Ja
Keiner
Zusätzlich sollen die folgenden Einschränkungen gelten: 1. Die Spalte RezeptId soll der Primärschlüssel sein. Diese Spalte wird als Identitätsspalte mit dem Startwert 1 und der Schrittweite 1 angelegt. 2. Die Werte für die Spalte Bezeichnung sollen eindeutig innerhalb der Tabelle sein. 3. In die Spalte Joule dürfen nur Werte größer als 0 und kleiner als 10000 eingetragen werden. 4. Der Wert für die Spalte Bewertung muss zwischen 1 und 6 liegen. Aus den obigen Bedingungen ergibt sich dann die folgende CREATE TABLE-Anweisung:
Im oberen Teil der Anweisung werden zunächst die Spalten deklariert. Der zweite Teil legt die erforderlichen Einschränkungen fest. Für die Spalte ZuletztGekochtAm sind hier auch NULL-Werte zugelassen. Der Wert NULL in dieser Spalte soll anzeigen, dass das entsprechende Gericht noch nie gekocht wurde.
103
4 Transact SQL-Grundlagen Da sich alle Einschränkungen nur auf jeweils eine Spalte beziehen, könnte die Tabelle Kochen.Rezept mit gleichen Eigenschaften auch so erzeugt werden:
Es ist also in unserem Fall auch möglich, die Einschränkungen gleich bei der Deklaration der einzelnen Spalten anzugeben. Die für eine Tabelle existierenden Einschränkungen können Sie sich im Objekt-Explorer auch ansehen. Hierzu öffnen Sie den Ordner Einschränkungen für die entsprechende Tabelle (Abbildung 4.17).
Abbildung 4.17 Einschränkungen für die Tabelle Kochen.Rezept
Dort sehen Sie allerdings nur die Namen der Einschränkungen, wobei im Übrigen zu erkennen ist, dass für die DEFAULT-Einschränkung der Spalte Joule automatisch eine Bezeichnung generiert wurde. Über das Kontextmenü der einzelnen Einschränkungen ist es möglich, eine T-SQL-Anweisung für das Erzeugen, Ändern oder Löschen der Einschränkung zu erstellen.
4.8.7
Beziehungen festlegen
Zur Datenbank OmasKochbuch soll nun eine weitere Tabelle hinzugefügt werden, in der die Zutaten für ein Rezept enthalten sind. Diese Tabelle wollen wir auch im Schema Kochen erzeugen und Zutat nennen. In jeder Tabellenzeile soll eine Zutat gespeichert werden, wobei die Daten für eine Zutat sich aus einer Bezeichnung, einer Menge und der Mengeneinheit zusammensetzen. Für die Einheit der Menge sind hierbei nur bestimmte Zeichenketten zugelassen. Eine entsprechende CREATE TABLE-Anweisung könnte also so aussehen:
104
4.8 Tabellen entwerfen
Die CHECK-Einschränkung für die Einheit bewirkt, dass nur die in der Auflistung enthaltenen Zeichenketten als Werte für diese Spalte zulässig sind. Außerdem hat die Tabelle eine Identitätsspalte bekommen, die als Primärschlüssel festgelegt wird. In die Tabelle Kochen.Zutat können nun ebenfalls Zeilen eingefügt werden. Allerdings besteht im Moment noch keinerlei Bezug zur Tabelle Kochen.Rezept. Eingefügte Zutaten hängen also sozusagen in der Luft und können nicht einem bestimmten Rezept, also einer Zeile in der Tabelle Kochen.Rezept, zugeordnet werden. Um diese Zuordnung zu ermöglichen, müssen die Tabellen irgendwie miteinander verbunden in Beziehung gesetzt werden. Da für ein Rezept in der Regel mehrere Zutaten benötigt werden, spricht man in diesem Fall von einer 1:n-Beziehung zwischen Rezept und Zutat. Eine solche Beziehung wird in einer relationalen Datenbank durch einen sogenannten Fremdschlüssel repräsentiert. Hierzu muss die Tabelle auf der 1-Seite die eindeutige Identifikation einer Zeile erlauben, also einen Primärschlüssel besitzen. Die Tabelle auf der n-Seite der Beziehung muss Spalten enthalten, die den Primärschlüssel der 1-Seite duplizieren Über diese, in beiden Tabellen enthaltenen, Spalten kann dann eine Fremdschlüsselbeziehung erstellt werden, welche die Tabellen miteinander verbindet. In unserem Fall besitzt die Tabelle Kochen.Rezept einen Primärschlüssel auf der Spalte RezeptId. Für eine 1:n-Fremdschlüsselbeziehung zwischen Kochen.Rezept und Kochen.Zutat muss diese Primärschlüsselspalte im ersten Schritt auch in der Tabelle Kochen.Zutat erzeugt werden. Hierzu verwenden wir, wie gewohnt, ALTER TABLE:
Der Name für diese Spalte ist frei wählbar, sie wird hier aus Gründen der Übersichtlichkeit ebenfalls RezeptId benannt. Im zweiten Schritt wird nun die eigentliche Fremdschlüsselbeziehung erzeugt, die auf der in beiden beteiligten Tabellen enthaltenen Spalte RezeptId basiert. Auch dies wird über die Anweisung ALTER TABLE erledigt. Auch eine Fremdschlüsselbeziehung ist eine Einschränkung, die wie üblich durch die Klausel ADD CONSTRAINT erzeugt wird. Der Typ der Einschränkung ist FOREIGN KEY (für Fremdschlüssel), wobei die am Fremdschlüssel beteiligten lokalen und über REFERENCES entfernten Spalten angegeben werden. Damit sieht die entsprechende ALTER TABLE-Anweisung so aus:
105
4 Transact SQL-Grundlagen Zur Tabelle Kochen.Zutat wird ein Fremdschlüssel FK_Zutat_Rezept hinzugefügt. Der Fremdschlüssel verwendet die lokale Spalte RezeptId und bezieht sich auf die Spalte RezeptId in der Tabelle Kochen.Rezept. Die Fremdschlüsselbeziehung bewirkt, dass in die Spalte RezeptId der Tabelle Kochen.Zutat nur Werte eingetragen werden können, für die ein gleicher Wert in einer Zeile der Tabelle Kochen.Rezept existiert. Dadurch, dass die Spalte Kochen.Zutat.RezeptId als NOT NULL deklariert wurde, muss jede Zutat zu einem existierenden Rezept gehören es gibt also keine Zutat ohne Rezept. Wären für die Spalte NULL-Werte erlaubt, so dürften auch Zeilen mit dem Wert NULL in dieser Spalte hinzugefügt werden. Solche Zeilen sind ebenfalls erlaubt, da die Fremdschlüsselbeziehung nur für Werte sichergestellt wird, in denen die beteiligten Spalten auf der n-Seite ungleich NULL sind. Wie alle bisherigen Einschränkungen, so kann auch eine Fremdschlüsselbeziehung bereits beim Anlegen einer Tabelle erzeugt werden. Für die Tabelle Kochen.Zutat wäre die Tabellenerstellung also auch so möglich gewesen:
Hier wird die Fremdschlüsselbeziehung in der Spaltendeklaration angegeben, möglich wäre auch wieder die Spezifizierung nach der Deklaration der Spalten in einer separaten CONSTRAINT-Klausel. Fremdschlüsselbeziehungen definieren Anhängigkeiten zur Sicherstellung der referenziellen Integrität. So dürfen zum Beispiel in der Tabelle Kochen.Rezept keine Zeilen gelöscht werden, auf die noch durch eine Fremdschlüsselbeziehung verwiesen wird. Gleiches gilt für die Aktualisierung solcher Zeilen. Auch hierbei wird SQL Server die referenzielle Integrität gewährleisten, es also nicht gestatten, deklarierte Fremdschlüsselbeziehungen zu verletzen. Bei der Deklaration eines Fremdschlüssels kann angegeben werden, wie mit Zeilen in der abhängigen Tabelle bei Schlüsseländerungen verfahren werden soll. Die FOREIGN KEYKlausel kennt hierzu die Optionen ON DELETE und ON UPDATE, über welche die auszuführende Aktion durch die folgenden Optionen spezifiziert werden kann: NO ACTION. Das Ändern oder Löschen von Zeilen in der Primärtabelle führt zu einem Fehler, wenn Verweise auf diese Zeilen existieren. Dies ist die Standardeinstellung, wenn keine Option angegeben wird. CASCADE. Alle Zeilen mit Fremdschlüsseln, die auf die gelöschte oder geänderte Zeile verweisen, werden ebenfalls gelöscht oder geändert.
106
4.8 Tabellen entwerfen SET NULL. In allen Zeilen mit Fremdschlüsseln, die auf die gelöschte oder geänderte Zeile verweisen, werden die an der Fremdschlüsselbeziehung beteiligten Spalten auf den Wert NULL gesetzt. SET DEFAULT. In allen Zeilen mit Fremdschlüsseln, die auf die gelöschte oder geänderte Zeile verweisen, werden die an der Fremdschlüsselbeziehung beteiligten Spalten auf den Standardwert gesetzt. Dies kann wenn nicht anders festgelegt natürlich ebenfalls der Wert NULL sein. Um zum Beispiel dafür zu sorgen, dass beim Löschen eines Rezeptes alle zugehörigen Zutaten ebenfalls gelöscht werden, kann die Fremdschlüsselbeziehung zwischen den Tabellen Kochen.Rezept und Kochen.Zutat durch die Klausel ON DELETE CASCADE ergänzt werden.
4.8.8
Zusammenfassung zu Einschränkungen
Dieser Abschnitt enthält noch einmal eine Zusammenfassung der verfügbaren Einschränkungen, die Ihnen zur Sicherstellung der Datenintegrität zur Verfügung stehen. NOT NULL. Eine durch den Zusatz NOT NULL deklarierte Spalte darf nicht leer sein, lässt also keine NULL-Werte zu. CHECK. CHECK-Einschränkungen beschränken den Wertebereich für einzelne Spalten. Mit CHECK-Einschränkungen können auch Bedingungen deklariert werden, die eine Tabellenzeile erfüllen muss. UNIQUE. Durch eine UNIQUE-Einschränkung wird die Eindeutigkeit von Spaltenwerten innerhalb einer Tabelle erzwungen. PRIMARY KEY. Eine PRIMARY KEY-Einschränkung dient zur eindeutigen Identifikation einer Tabellenzeile. Mehrere Zeilen in einer Tabelle dürfen also nicht denselben Wert für den Primärschlüssel verwenden. FOREIGN KEY. Eine FOREIGN KEY-Einschränkung erzwingt eine Beziehung zwischen Tabellen.
4.8.9
Tabellen löschen
Eine Tabelle wird durch die Anweisung DROP TABLE gelöscht. Diese Anweisung erwartet als einzigen Parameter den Tabellennamen. Unsere Tabelle Kochen.Zutat könnten wir also so entfernen:
Sie dürfen keine Tabelle löschen, auf die noch durch einen Fremdschlüssel verwiesen wird. Bei einem entsprechenden Versuch erhalten Sie eine Fehlermeldung.
107
4 Transact SQL-Grundlagen
4.8.10 Datenbankdiagramme Der Objekt-Explorer bietet die fantastische Möglichkeit, grafische Präsentationen einer Datenbank zu erstellen. Hierzu existiert für jede Datenbank der Ordner Datenbankdiagrame. In diesem Ordner können Sie Diagramme Ihrer Datenbank erstellen und speichern. Wenn Sie das erste Mal auf den Ordner klicken, werden Sie zunächst aufgefordert, die erforderlichen Unterstützungsobjekte für die Verwaltung von Datenbankdiagrammen zu installieren (Abbildung 4.18).
Abbildung 4.18 Unterstützungsobjekte für Diagramme anlegen
Bestätigen Sie die Frage mit Ja, so haben Sie die Möglichkeit, anschließend Diagramme zu erstellen. Aus dem Kontextmenü des Ordners Datenbankdiagramme wählen Sie hierzu den Punkt Neues Datenbankdiagramm. Es öffnet sich ein Dialog, in dem Sie die im Diagramm darzustellenden Tabellen bzw. Sichten auswählen können. Die beiden Tabellen unser Datenbank OmasKochbuch sehen Sie in (Abbildung 4.19).
Abbildung 4.19 Tabellen zum Datenbankdiagramm hinzufügen
Wählen Sie für beide Tabellen Hinzufügen, und schließen Sie den Dialog, so ergibt sich das in Abbildung 4.20 gezeigte Datenbankdiagramm. Das Diagramm zeigt die Tabellen mit ihren jeweiligen Spalten, aber auch die Beziehungen zwischen den ausgewählten Tabellen. Für eine 1:n-Beziehung, wie in unserem Beispiel, wird die 1-Seite durch den Schlüssel dargestellt. Die n-Seite der Beziehung wird durch das Zeichen (Unendlich) gekennzeichnet. Im Datenbankdiagramm können Sie zwischen verschiedenen Darstellungsformen wählen. So ist es zum Beispiel möglich, neben den Spaltennamen auch die Datentypen der Spalten anzuzeigen. Datenbankdiagramme können (in den Systemtabellen
108
4.8 Tabellen entwerfen der Datenbank) gespeichert und natürlich auch wieder abgerufen werden. Außerdem dürfen pro Datenbank beliebig viele Diagramme angelegt werden.
Abbildung 4.20 Datenbankdiagramm für die Datenbank OmasKochbuch
Gerade für unbekannte Datenbanken sind Datenbankdiagramme eine enorme Hilfe bei der Einarbeitung in die Datenbankstruktur; hauptsächlich deshalb, weil auch die Beziehungen zwischen den Tabellen angezeigt werden. Erstellen Sie ruhig einmal ein Diagramm der Beispieldatenbank AdventureWorks mit allen Tabellen, und schauen Sie sich die Struktur dieser Datenbank an.
4.8.11 Temporäre Tabellen SQL Server erlaubt die Erstellung von temporären Tabellen. Solche Tabellen werden grundsätzlich in der Systemdatenbank tempdb angelegt. Bei temporären Tabellen wird zwischen lokalen und globalen Tabellen unterschieden. Eine lokale temporäre Tabelle wird einfach dadurch erzeugt, dass der Tabellenname mit einem #-Zeichen beginnt, also zum Beispiel so:
Die Tabelle #T1 ist lokal, weil sie nur für die Verbindung, in der sie angelegt wurde, existiert. Andere Verbindungen können auf diese Tabelle nicht zugreifen. Der Name einer globalen temporären Tabelle beginnt mit zwei ##-Zeichen, also beispielsweise so:
Die Tabelle ##T2 ist ebenfalls eine temporäre Tabelle. Diese Tabelle ist jedoch auch für andere Verbindungen sichtbar. Sowohl globale als auch lokale temporäre Tabellen werden beim Beenden der Verbindung, in der sie erzeugt wurden, automatisch gelöscht. Temporäre Tabellen werden oftmals in der T-SQL-Programmierung eingesetzt, um Daten aus unterschiedlichen Tabellen zusammenzusuchen, in einer temporären Tabelle zwischenzuspeichern und dann die Zeilen dieser Tabelle zurückzugeben.
109
4 Transact SQL-Grundlagen
4.9
INSERT Daten einfügen Zum Hinzufügen von Zeilen zu einer Tabelle wird die INSERT-Anweisung verwendet. Diese Anweisung hat eine einfache Syntax und ist daher entsprechend gradlinig zu benutzen:
Die einzelnen Bestandteile der Anweisung werden im Folgenden kurz erklärt: [into]. Die Angabe dieser Klausel ist in T-SQL optional. . Dies ist die Bezeichnung der Tabelle, in welche die Zeile eingefügt werden soll. <spaltenliste>. Die Spaltenliste enthält die durch Kommata getrennten Namen der Spalten, in die Werte eingefügt werden sollen. Hier müssen nicht alle Spalten der Tabelle angegeben werden. Nicht genannte Spalten werden mit dem Standardwert belegt. Die Angabe der Spaltenliste ist optional. Wird die Liste weggelassen, wird so verfahren, als wären alle Spalten in der bei CREATE TABLE angegebenen Reihenfolge angegeben worden. <spaltenwerte>. Die einzelnen Spaltenwerte für die neue Tabellenzeile, ebenfalls durch Kommata getrennt. Wenn eine <spaltenliste> angegeben wird, muss die Anzahl der Spaltenwerte mit der Anzahl der Spalten in der Spaltenliste übereinstimmen. Die folgenden vier INSERT-Anweisungen fügen Zeilen in die Tabelle Kochen.Rezept ein:
Interessant sind vor allem die Werte für die Spalte ZuletztGekochtAm vom Typ . Diese Werte werden in drei unterschiedlichen Formaten angegeben: 1. Als Zeichenkette in der Form 'JJJJMMTT'. Es erfolgt eine implizite Konvertierung in den Datentyp . Der Zeitanteil für den Wert wird in der Zeichenkette nicht angegeben und ist daher 00:00:00. 2. NULL. In die Spalte wird der Wert NULL eingetragen. 3. DEFAULT. In die Spalte wird der bei der Deklaration angegebene Standardwert eingetragen. In unserem Fall ist dieser Wert ebenfalls NULL. Da die Spalte RezeptId eine Identitätsspalte ist, sorgt SQL Server intern dafür, dass für jede Zeile ein Spaltenwert vergeben wird. Wenn Sie versuchen, explizit einen Wert für diese Spalte anzugeben, erhalten Sie eine Fehlermeldung. SQL Server gewährleistet auch,
110
4.10 SELECT Daten abfragen dass die Integritätsbedingungen eingehalten werden. Die folgenden INSERT-Anweisungen werden daher nicht ausgeführt, sondern liefern eine Fehlermeldung:
4.10
SELECT Daten abfragen Mit der SELECT-Anweisung werden Daten aus existierenden Tabellen abgefragt. SELECT ist sicher das mit Abstand am häufigsten verwendete und auch komplexeste SQL-Kommando. In diesem Abschnitt werden wir zunächst sehen, wie die Komplexität aus der SELECT-Anweisung herausgenommen werden kann, und uns das Kommando mit seinen Möglichkeiten und Tücken Schritt für Schritt erarbeiten. In Abschnitt 4.17 werden wir dann noch einmal auf SELECT zurückkommen und komplexere Abfragen entwerfen. Die grundlegende Struktur einer SELECT-Anweisung besteht aus den folgenden Teilen:
Die nachstehende Aufzählung erklärt die einzelnen Bestandteile: . In den meisten Fällen wird diese Liste einfach eine durch Komma getrennte Auflistung von Spaltennamen sein, die sich auf die in der FROM-Klausel angegebene Tabelle bezieht. Es ist aber auch möglich, Ausdrücke anzugeben, um zum Beispiel Berechnungen mit Tabellenspalten durchzuführen. [from ]. Über diese Klausel wird die Tabelle angegeben, die abgefragt werden soll. Die Klausel ist tatsächlich optional, SELECT kann also auch einfach dazu verwendet werden, Ausdrücke zu berechnen und auszugeben. []. Hier können weitere Optionen angegeben werden, die allesamt nicht obligatorisch sind. Auf einige der zur Verfügung stehenden Optionen werden wir im Verlaufe dieses Kapitels noch mehrfach zurückkommen. Eine SELECT-Anweisung muss also nicht zwingend eine Tabelle abfragen. Auch die folgende Anweisung, mit der einfach nur Ausdrck berechnet wrd, funktioniert:
Die Anweisung gibt tatsächlich diese Zeile zurück:
111
4 Transact SQL-Grundlagen Das zweite Beispiel zeigt eine SELECT-Anweisung, mit der die Spalten Bezeichnung und Bewertung aus der Tabelle Kochen.Rezept abgefragt werden:
Hier ist das Ergebnis:
Eine bequeme Möglichkeit ist die Angabe eines Sternchens * für die Spaltenliste als Abkürzung für alle Spalten:
Das Abfrageergebnis enthält alle Spalten der Tabelle:
Aliasnamen für Spalten Die für die Ausgabe verwendeten Spaltennamen können durch die Klausel AS angegeben werden. Schauen Sie sich noch einmal das obige Beispiel an, in dem nur Ausdrücke berechnet werden. In der Ausgabe wird für beide Spalten der Name (Kein Spaltenname) angegeben. Diese wenig aussagekräftigen Spaltenbezeichnungen können über die ASKlausel geändert werden:
Die Ausgabe sieht nun so aus:
In Aliasnamen können auch Zeichen verwendet werden, die ansonsten in Bezeichnern nicht erlaubt sind, also zum Beispiel Leerzeichen oder Zeichen, die Operatoren kennzeichnen. In so einem Fall muss der Name für den Alias aber in eckige Klammern oder doppelte Anführungszeichen eingeschlossen werden:
112
4.10 SELECT Daten abfragen Spaltennamen und Aliasnamen für Tabellen Ein voll qualifizierter Spaltenname in einer SELECT-Anweisung ist so aufgebaut:
Der Name besteht also aus dem Tabellennamen gefolgt vom eigentlichen Namen der Spalte, beide Namensteile werden durch ein Komma getrennt. Die obige Abfrage zum Ausgeben der Spalten Bezeichnung und Bewertung aus der Tabelle Kochen.Rezept hätte korrekt so formuliert werden müssen:
Der Zugriff auf Spaltennamen ohne Angabe des Tabellennamens ist immer dann problemlos möglich, wenn der Name der Spalte über alle an der Abfrage beteiligten Tabellen eindeutig ist. In so einem Fall erfolgt die Zuordnung des Spaltennamens zu einer Tabelle automatisch. In unserem Fall wird nur eine Tabelle verwendet, also sollte diese Automatik funktionieren. Sind an einer Abfrage mehrere Tabellen beteiligt, so müssen für Spaltennamen, die nicht eindeutig sind, also in mindestens zwei Tabellen vorkommen, die voll qualifizierten Bezeichnungen verwendet werden. In so einem Fall kann ein Aliasname für die Tabelle die Schreibarbeit erleichtern. Genau wie für Spalten werden Aliasnamen für Tabellen durch den Zusatz AS vergeben, der hier an den Tabellennamen angehängt wird. Ist für eine Tabelle ein Aliasname vergeben worden, kann dieser Aliasname anstelle des Tabellennamens in der Abfrage verwendet werden. Die obige SELECT-Anweisung könnte also auch so aussehen:
In T-SQL kann die Option AS auch weggelassen und stattdessen der Aliasname direkt an den Tabellennamen angehängt werden:
Für Abfragen mit nur einer Tabelle sind Aliasnamen für Tabellen zwar möglich, aber nicht erforderlich. Sortieren Das Ergebnis einer Abfrage kann über eine ORDER BY-Klausel sortiert werden. Diese Klausel muss am Ende der SELECT-Anweisung angegeben werden und eine Liste von Spaltennamen oder -nummern enthalten. Nach diesen Spalten wird die Ausgabe dann sortiert. Für jede in der Liste aufgezählte Spalte kann über die Option ASC oder DESC angegeben werden, ob die Sortierung aufsteigend (ASC) oder absteigend (DESC) erfolgen soll. Die Angabe dieser Option ist optional, der Standard ist ASC. Für eine Ausgabe aller Spal-
113
4 Transact SQL-Grundlagen ten der Tabelle Kochen.Rezept und eine aufsteigende Sortierung nach der Spalte Joule können Sie die ORDER BY-Klausel wie folgt angeben:
Soll die Sortierung absteigend erfolgen, geben Sie die Option DESC an:
Testdaten einfügen Bevor wir nun mit SELECT fortfahren, sollen zunächst noch einige Zeilen in die Tabelle Kochen.Zutat eingefügt werden:
Die Datenbanktabellen Kochen.Rezept und Kochen.Zutat haben nun die in Tabelle 4.16 und Tabelle 4.17
dargestellten Inhalte.
Tabelle 4.16 Inhalt der Tabelle Kochen.Rezept
114
RezeptId
Bezeichnung
Joule
Bewertung
ZuletztGekochtAm
1
Schweinebraten
1600
1
2005-04-05 00:00:00
2
Erbsensuppe
660
3
NULL
3
Kassler
1430
5
NULL
4
Matjes
1230
2
2005-02-16 00:00:00
4.10 SELECT Daten abfragen Tabelle 4.17 Inhalt der Tabelle Kochen.Zutat
ZutatId
RezeptId
Bezeichnung
Menge
Einheit
1
1
Schweinenacken
1.00
kg
2
1
Kartoffeln
650.00
g
3
1
Salz
1.00
Prise
4
2
Erbsen
1.30
kg
5
2
Speck
350.00
g
6
2
Wasser
1.40
l
7
4
Hering
1.30
kg
8
4
Mayonnaise
450.00
ml
9
4
Zwiebeln
4.00
Stück
10
4
Gurken
600.00
g
4.10.1 Filtern: Projektion und Selektion Die bisherigen Abfragen haben stets alle Zeilen der Tabelle Kochen.Rezept zurückgegeben. Dies wird in den meisten Fällen nicht erwünscht sein, in der Regel soll eine Abfrage nur ein eingeschränktes Ergebnis also eben nicht alle Zeilen liefern. Genauso wie aus der Menge der in einer Tabelle verfügbaren Spalten durch die Angabe von Spaltennamen nur einige ausgewählt werden können, ist es natürlich auch möglich, bestimmte Zeilen herauszufiltern und nur diese in das Ergebnis aufzunehmen. Die Spaltenauswahl also die vertikale Filterung wird auch als Projektion bezeichnet, die Zeilenauswahl ist die horizontale Filterung und heißt Selektion (Abbildung 4.21).
Abbildung 4.21 Projektion und Selektion
115
4 Transact SQL-Grundlagen Eine Projektion ist in der Regel relativ simpel. In vielen Fällen reicht die Auflistung der Spaltennamen. Allerdings können die einzelnen Ausgabespalten auch durch Ausdrücke berechnet werden. Dadurch ist es möglich, Spalten zusammenzufassen oder auch Spalten zum Ergebnis hinzuzufügen, die nicht in der Tabelle enthalten sind. Der Schwerpunkt in diesem und auch in allen folgenden Abschnitten ist das Entwerfen horizontaler Filter. Filterbedingungen können durch die Vielzahl der zur Verfügung stehenden Operatoren recht komplex werden. Wir werden zunächst mit einfachen Filterbedingungen beginnen und dann im weiteren Verlauf des Kapitels die Komplexität nach und nach erhöhen. WHERE Die Zeilenfilterbedingung wird in der WHERE-Klausel der SELECT-Anweisung als logischer Ausdruck angegeben. Für einen solchen Ausdruck werden vor allem die in Abschnitt 4.5, dort in Tabelle 4.10 bzw. Tabelle 4.12, genannten logischen und Vergleichsoperatoren benötigt. Der angegebene Filter-Ausdruck wird für jede Zeile der Tabelle ausgewertet. Nur diejenigen Zeilen, für welche die Auswertung den Wert WAHR ergibt, werden dann in das Abfrageergebnis aufgenommen. Um zum Beispiel aus der Tabelle Kochen.Rezept nur alle Zeilen zu erhalten, in denen die Spalte Joule kleiner als 1200 ist, verwenden Sie die WHERE-Klausel folgendermaßen:
Möchten Sie alle Zutaten für das Rezept mit der RezeptId 1 sehen, benutzen Sie die folgende SELECT-Anweisung:
Eine WHERE-Klausel kann mehrere Bedingungen, die durch logische Operatoren miteinander verknüpft werden, enthalten. Sollen alle Rezepte ausgegeben werden, die eine Bewertung von besser als 3 haben und vor dem 01.01.2006 das letzte Mal gekocht wurden, sieht die WHERE-Klausel so aus:
Hier ist das Ergebnis:
Die beiden Zeilen mit einem NULL-Wert in der Spalte ZuletztGekochtAm werden nicht in das Ergebnis aufgenommen. Warum dies so ist, erfahren Sie in Abschnitt 4.16.
116
4.10 SELECT Daten abfragen Übrigens: Ob beim Vergleich von Zeichenketten zwischen Groß- und Kleinschreibung unterschieden wird, hängt von der eingestellten Sortierreihenfolge der Datenbank ab. Normalerweise werden Sie eine Sortierreihenfolge benutzen, in der dies nicht der Fall ist. Wenn Sie bei der SQL Server-Installation nichts anderes angegeben haben, dann ist die standardmäßige Sortierreihenfolge so, dass nicht zwischen Groß- und Kleinschreibung unterschieden wird. Die gewählte Einstellung gilt auch für alle Objektnamen in der Datenbank. Bei einer Datenbank, die zwischen Groß- und Kleinschreibung unterscheidet, muss also zum Beispiel ein Tabellenname unter Beachtung der Groß- und Kleinschreibung angegeben werden. In allen folgenden Beispielen wird davon ausgegangen, dass nicht zwischen Groß- und Kleinschreibung unterschieden wird. IS NULL Auf NULL-Werte in Spalten muss explizit getestet werden. Die sonst üblichen Vergleichsoperatoren können Sie für so einen Test nicht verwenden. Hier hilft nur der IS NULL-Operator, der WAHR zurückgibt, wenn ein Ausdruck NULL ist:
Um alle Rezepte zu erhalten, die noch nie gekocht wurden, verwenden Sie IS NULL in der WHERE-Klausel:
Gerichte, die bereits einmal gekocht wurden, enthalten ein Datum in der Spalte ZuletztGekochtAm. Diese Zeilen werden von der folgenden Abfrage zurückgegeben:
BETWEEN Der Operator BETWEEN ermöglicht Bereichsabfragen. Mit BETWEEN wird geprüft, ob der Wert eines Ausdrucks zwischen einer unteren und oberen Grenze inklusive der Grenzwerte liegt. Der BETWEEN-Operator hat diese Syntax:
Um alle Rezepte mit einer Bewertung zwischen 2 und 4 zu erhalten, können Sie diese Abfrage angeben:
BETWEEN kann immer durch die Vergleichsoperatoren >= und <= ersetzt werden. Die folgende Abfrage liefert dasselbe Ergebnis wie die obige SELECT-Anweisung mit BETWEEN:
117
4 Transact SQL-Grundlagen IN Der IN-Operator testet einen Ausdruck auf eine Zugehörigkeit zu einer Liste von Werten. Die generelle Syntax des IN-Operators ist wie folgt:
Eine Abfrage, die alle Gerichte zurückgibt, die eine Bewertung von 1, 3, oder 5 haben, sieht unter Verwendung des IN-Operators so aus:
Ein Ausdruck unter Verwendung des IN-Operators kann immer durch OR verknüpfte Tests auf Gleichheit ersetzt werden. Die obige Abfrage hätte auch so formuliert werden können:
Der IN-Operator ist insbesondere dann nützlich, wenn die Werte der Liste nicht explizit angegeben, sondern aus einer Abfrage erstellt werden. Hierzu kann in den runden Klammern auch eine SELECT-Anweisung stehen, die dann genau eine Spalte zurückgeben muss (aber natürlich beliebig viele Zeilen enthalten kann). Die folgende Abfrage gibt die (sortierten) Bezeichnungen aller Zutaten für Gerichte, die noch nie gekocht wurden, aus:
Abfragen dieser Art sind ein häufiger Einsatzfall für den IN-Operator. EXISTS Der EXISTS-Operator testet auf die Existenz von Zeilen in einer Tabelle. Dies ist die Syntax für den Operator EXISTS:
Die Unterabfrage ist eine SELECT-Anweisung, bei der es egal ist, welche Spalten sie enthält. Sobald die Unterabfrage mindestens eine Zeile zurückgibt, liefert der Operator den Wert WAHR. Die folgende Abfrage gibt alle Rezepte zurück, für die keine Zutaten existieren:
Die Abfrage enthält eine äußere und eine innere SELECT-Anweisung, die miteinander verbunden sind. Die Verbindung zwischen beiden Abfragen wird durch die WHEREKlausel in der inneren Anweisung hergestellt:
118
4.10 SELECT Daten abfragen Die innere SELECT-Anweisung wird für jede Zeile des von der äußeren Anweisung zurückgegebenen Ergebnisses ausgeführt. Dabei bekommt die innere Anweisung den Wert der Spalte Kochen.Rezept.RezeptId sozusagen als Parameter übergeben und filtert dann nur Zeilen für diese eine übergebene RezeptId heraus. Sobald auch nur eine Zeile gefunden wurde, liefert der EXISTS-Operator den Wert WAHR, der durch den NOT-Operator allerdings noch invertiert wird. Die in der äußeren Abfrage angegebene WHERE-Klausel wird also nur dann WAHR, wenn für eine RezeptId keine Zeilen in der Tabelle Kochen.Zutat existieren. Somit liefert die Abfrage nur Gerichte, für die keine Zutaten vorhanden sind. LIKE Der LIKE-Operator ermöglicht einen Mustervergleich von Zeichenketten. Bei einer Übereinstimmung gibt der Vergleich WAHR zurück. Die generelle Syntax für den LIKE Operator ist wie folgt:
In einem Vergleichsmuster können verschiedene Platzhalter verwendet werden, die auch miteinander kombiniert werden dürfen. Folgende Platzhalter stehen zur Verfügung: _. Ein Unterstrich steht für genau ein beliebiges Zeichen innerhalb des Musters. Beispiele Muster
Erklärung
WAHR für
'Me_er'
Zeichenketten, die mit Me beginnen und auf er enden. An dritter Stelle kann ein beliebiges Zeichen stehen.
Meier, Meyer
M__er
Zeichenketten, die mit M beginnen und auf er enden. An dritter und vierter Stelle kann jeweils ein beliebiges Zeichen stehen.
Meier, Meyer, Mayer, Maier, Mixer
%. Das Prozentzeichen ist ein Platzhalter für beliebig viele Zeichen. Beispiele Muster
Erklärung
WAHR für
'M%'
Alle Zeichenketten, die mit M beginnen.
Max, Mann, Monika
'M%r'
Alle Zeichenketten, die mit M beginnen und mit r enden.
Meier, Müller, Münchner, Marsianer
[]. Ein Platzhalter für ein Zeichen. Die eckigen Klammern enthalten eine Aufzählung erlaubter Zeichen. Durch einen Bindestrich kann auch ein Bereich angegeben werden. Beispiele Muster
Erklärung
WAHR für
[aeiou]%
Alle Zeichenketten, die mit einem Vokal beginnen.
Angelika, Otto, Uwe
[0-9][0-9][0-9]
Eine Zeichenkette aus genau drei Ziffern.
000, 123, 999
119
4 Transact SQL-Grundlagen Muster
Erklärung
WAHR für
M[ae][iy]er
Alle Zeichenketten, die mit M beginnen und mit er aufhören. An zweiter Stelle steht entweder ein a oder e, an dritter Stelle ein i oder y.
Maier, Mayer, Meier, Meyer
[a-e]%r
Alle Zeichenketten, die mit A bis E beginnen und mit r enden.
Anger, Bootsverleiher Eckensteher,
M[ae][iy]%
Alle Zeichenketten, die mit M beginnen und an zweiter Stelle entweder ein a oder e sowie an dritter Stelle entweder ein i oder y enthalten.
Maier, Meier, Meise, Meyenburg
[^]. Steht für ein beliebiges Zeichen, das sich nicht im angegebenen Bereich bzw. in der Aufzählung befindet. Beispiele Muster
Erklärung
WAHR für
[^xy]%
Alle Zeichenketten, die nicht mit einem x oder y beginnen.
Frank, Max
[^0-9]%
Alle Zeichenketten, die nicht mit einer Ziffer beginnen
Eins, Zwei, Drei
Die folgende Abfrage liefert nur Zeilen aus der Tabelle Kochen.Rezept, in deren Spalte Bezeichnung die Zeichenkette schwein enthalten ist:
Damit würden zum Beispiel solche Übereinstimmungen gefunden: Schweinebraten, Gegrillter Schweinebauch, Tischwein. TOP Die Angabe von TOP(n) vor der Spaltenliste bewirkt, dass nur die ersten n Zeilen aus dem Ergebnis ausgegeben werden. TOP ist insbesondere nützlich bei der gleichzeitigen Verwendung der ORDER BY-Klausel, weil so die Möglichkeit besteht, Ranglisten auszugeben. Diese Abfrage gibt die Spalten Bezeichnung und ZuletztGekochtAm zurück, wobei die Ausgabe absteigend sortiert wird:
Als Ergebnis bekommen wir dies:
120
4.10 SELECT Daten abfragen Wenn nun die Klausel TOP(1) vor der eigentlichen Spaltenliste hinzugefügt wird, so enthält das Ergebnis nur noch die erste Zeile der obigen Tabelle und gibt somit den Termin aus, an dem zuletzt etwas gekocht wurde:
Das Ergebnis ist wie erwartet:
In der TOP-Klausel kann auch ein Prozentwert angegeben werden. In diesem Fall wird aus dem Ergebnis nur die angegebene Prozentzahl von Zeilen ausgegeben. Schauen Sie sich hierzu bitte die folgende Anweisung an:
Die Abfrage sortiert die Zeilen der Tabelle Kochen.Rezept und gibt anschließend die Hälfte der Zeilen zurück. TABLESAMPLE Durch die Angabe der Klausel TABLESAMPLE werden aus dem Gesamtergebnis einer Abfrage zufällig Daten ausgewählt. Diese Klausel ist besonders nützlich für statistische Berechnungen. Wenn derartige Berechnungen mit sehr großen Tabellen durchgeführt werden sollen, ist es oft ausreichend, nur eine Stichprobe für die Berechnung zu verwenden. Eine solche Stichprobe kann durch die TABLESAMPLE-Klausel erzeugt werden, die nach der FROM-Klausel angegeben wird:
Die obige Anweisung soll 20 Prozent der in der Tabelle enthaltenen Zeilen zurückgeben. Allerdings funktioniert dies nicht wie erwartet, stattdessen werden keine Zeilen zurückgegeben. Ursache ist, dass das Ergebnis durch TABLESAMPLE nicht zeilenweise ausgewertet wird. TABLESAMPLE wählt nach einem Zufallsprinzip ganze Datenseiten aus und nimmt dann alle Zeilen der Seite in das Ergebnis auf. Diese Verfahrensweise ist sehr performant, aber eben vor allem bei kleinen Tabellen auch etwas ungenau. In unserem Fall ist die Tabelle so klein, dass sie komplett in nur eine Datenseite passt. Je nach Angabe der Prozentzahl wird diese Datenseite in das Ergebnis aufgenommen oder nicht. Da alle Zeilen der Tabelle in dieser einen Seite liegen, enthält das Ergebnis also entweder alle oder keine Zeilen der Tabelle. Erwarten Sie also bitte nicht, dass TABLESAMPLE immer exakt die angeforderte Prozentzahl von Zeilen liefert. Wenn Sie dies benötigen, werden Sie auf TOP zurückgreifen müssen.
121
4 Transact SQL-Grundlagen DISTINCT Durch die Angabe von DISTINCT vor der Spaltenliste werden doppelte Zeilen aus dem Ergebnis entfernt. Schauen Sie sich hierzu die folgenden beiden SELECT-Anweisungen an, die nur den Wert der Spalte Einheit aus der Tabelle Kochen.Zutat ausgeben:
Hier sind die Ergebnisse der beiden SELECT-Anweisungen:
Wie Sie sehen, werden durch die DISTINCT-Option die doppelten Zeilen entfernt.
4.11
Informationen ausgeben mit PRINT Die Anweisung PRINT erwartet eine Zeichenkette als einzigen Parameter. Diese Zeichenkette wird durch PRINT einfach in den Meldungsbereich der Ausgabe geschrieben. Dies ist der Bereich, in dem auch Fehlermeldungen ausgegeben werden. Dadurch ermöglicht PRINT die Ausgabe von zusätzlichen Informationen, die nicht im eigentlichen Ausgabebereich der Ausgabe erscheinen sollen. Der Aufruf kann zum Beispiel so aussehen:
4.12
Ausführen von gespeicherten Prozeduren SQL Server bietet die Möglichkeit, T-SQL-Code in Form von gespeicherten Prozeduren in einer Datenbank zu speichern. Jede erzeugte Datenbank enthält bereits eine Reihe von gespeicherten Systemprozeduren, von denen einige in Kapitel 6 näher betrachtet werden. An dieser Stelle soll zunächst nur demonstriert werden, wie gespeicherte Prozeduren aufgerufen also ausgeführt werden. Dafür gibt es prinzipiell zwei Möglichkeiten:
122
4.13 UPDATE Daten ändern Die Anweisung EXECUTE (oder abgekürzt: EXEC) erlaubt den Aufruf einer gespeicherten Prozedur an einer beliebigen Stelle im T-SQL-Skript. Hierzu wird nach EXECUTE der Name der Prozedur angegeben. Falls die Prozedur Parameter erwartet, so werden diese durch Komma getrennt aufgezählt. Ein Prozeduraufruf kann also zum Beispiel so aussehen:
ist also der Name der Prozedur, die Zeichenkette ein Pa-
rameter, der an diese Prozedur übergeben wird. Eine Prozedur kann auch einfach durch ihren Namen und die Parameterliste aufgerufen werden, also ohne die Verwendung von EXEC(UTE), wenn der Prozeduraufruf die erste Anweisung in einem T-SQL-Stapel ist. In so einem Fall können Sie also auch einfach schreiben:
In Kapitel 7 werden wir eigene gespeicherte Prozeduren entwickeln. Dort werden wir auch noch einmal etwas genauer auf die Übergabe von Parametern an gespeicherte Prozeduren eingehen.
4.13
UPDATE Daten ändern Um bestehende Zeilen in einer Tabelle zu ändern, verwenden Sie die UPDATE-Anweisung. Diese Anweisung hat die folgende Syntax:
Die folgende Aufzählung erläutert die einzelnen Bestandteile der UPDATE-Anweisung: . Dies ist der Name der zu aktualisierenden Tabelle. set <spalte> = . Über die SET-Klausel werden die neuen Werte für die angegebenen Spalten durch einzelne Zuweisungen, die jeweils durch ein Komma getrennt sind, festgelegt. Hierbei kann der Spaltenname sowohl auf der linken als auch auf der rechten Seite der Zuweisung auftreten. Dadurch ist es möglich, einen existierenden Spaltenwert zur Berechnung eines neuen Spaltenwertes zu verwenden. Die folgende SET-Klausel zum Beispiel wird den Wert der Spalte Listenpreis um 3% erhöhen:
where . Über die WHERE-Klausel wird eine Zeilenfilterbedingung angegeben, welche die Zeilen für die Aktualisierung selektiert. Die Angabe der WHEREKlausel ist optional. Wird sie weggelassen, so wird das UPDATE für alle Zeilen der
123
4 Transact SQL-Grundlagen Tabelle durchgeführt. Für die Konstruktion der Filterbedingung existieren die im vorhergehenden Abschnitt aufgezeigten Möglichkeiten. SQL Server stellt sicher, dass bei UPDATE-Operationen keine Integritätsbedingungen verletzt werden. So werden zum Beispiel Wertbereichseinschränkungen, die beispielsweise durch CHECK-Optionen angegeben wurden, in jedem Fall eingehalten. Auch die referenzielle Integrität wird selbstverständlich nicht verletzt. Wenn Sie versuchen, eine Spalte zu aktualisieren, die an einer Fremdschlüsselbeziehung beteiligt ist, so wird die Aktualisierung nur funktionieren, wenn die Fremdschlüsselbeziehung auch nach der Aktualisierung gültig ist. Wie verfahren wird, ist auch abhängig von der Option ON UPDATE, die bei der Erzeugung oder Änderung der FOREIGN KEY-Einschränkung angegeben wurde. Soll die Menge für die Zutat mit der Id 1 von derzeit 1 kg auf 1200 g geändert werden, so ist diese UPDATE-Anweisung erforderlich:
Die folgende UPDATE-Anweisung erzeugt eine Fehlermeldung:
Durch eine CHECK-Einschränkung wurde angegeben, dass für die Spalte Bewertung nur Werte zwischen 1 und 6 zugelassen sind. Der Wert 0 ist also für die Spalte Bewertung unzulässig. Die erzeugte Fehlermeldung macht dies auch unmissverständlich klar:
Auch die Verletzung der referenziellen Integrität ist nicht möglich, die folgende UPDATEAnweisung liefert daher ebenfalls eine Fehlermeldung:
Da es in der Tabelle Kochen.Rezept keine Zeile gibt, in welcher der Wert der Spalte RezeptId 22 ist, wird die obige UPDATE-Anweisung nicht ausgeführt und erzeugt lediglich die folgende Fehlermeldung:
Es ist jedoch möglich, den Wert der Spalte Kochen.Zutat.RezeptId so zu ändern, dass die Zutat einem anderen Rezept zugeordnet wird. Um alle Zutaten für das Rezept mit der Id 3 dem Rezept mit der Id 2 zuzuordnen, können wir schreiben:
124
4.14 DELETE Daten löschen
Durch die UPDATE-Anweisung wird die referenzielle Integrität nicht verletzt, da in der Tabelle Kochen.Rezept eine Zeile mit der RezeptId 2 existiert.
4.14
DELETE Daten löschen Die DELETE-Anweisung ermöglicht das Löschen von Zeilen aus einer Tabelle. Das Kommando hat die folgende Syntax:
In T-SQL ist die Angabe des Schlüsselwortes FROM optional. Um die zu löschenden Zeilen auszuwählen, kann über die WHERE-Klausel wiederum ein Zeilenfilter angegeben werden. Wie schon bei UPDATE, so ist auch hier die Angabe der WHERE-Klausel optional. Wenn Sie diese Klausel weglassen (oder vergessen!), werden einfach alle Zeilen der Tabelle gelöscht. SQL Server wird selbstverständlich auch für DELETE-Anweisungen die Datenintegrität gewährleisten. Es dürfen also keine Zeilen gelöscht werden, auf die noch durch eine Fremdschlüsselbeziehung verwiesen wird, es sei denn, dass für diese Fremdschlüsselbeziehung über die Option ON DELETE ein entsprechendes Verhalten konfiguriert wurde. Schauen Sie sich hierzu bitte das folgende Listing 4.1 an: Listing 4.1 ON DELETE CASCADE
125
4 Transact SQL-Grundlagen
Die zweite DELETE-Anweisung funktioniert, weil durch die Option ON DELETE CASCADE angegeben wurde, dass alle abhängigen Zeilen in der Tabelle Kochen.Zutat automatisch gelöscht werden sollen, wenn eine Zeile in der Primärtabelle Kochen.Rezept gelöscht wird. Eine Tabelle leeren Um alle Zeilen einer Tabelle zu entfernen, kann auch die Anweisung TRUNCATE TABLE verwendet werden. Die folgenden beiden T-SQL-Kommandos haben letztlich dieselbe Auswirkung:
TRUNCATE hat den Vorteil, dass es weniger Ressourcen benötigt und um einiges schneller arbeitet als das zeilenweise Löschen mit DELETE. Außerdem werden bei TRUNCATE alle von der Tabelle belegten Datenseiten freigegeben, die Tabelle wird also auch physikalisch verkleinert. Dies ist bei DELETE nicht der Fall. TRUNCTATE TABLE kann allerdings nur für Tabellen durchgeführt werden, auf die keine FOREIGN KEY-Einschränkung verweist. Dies ist unabhängig davon, ob tatsächlich Zeilen existieren, durch deren Löschen die referenzielle Integrität verletzt werden würde. Allein die Existenz der FOREIGN KEY-Einschränkung ist dafür ausreichend, dass TRUNCATE TABLE in so einem Fall nicht funktioniert. Folglich kann die Tabelle Kochen.Rezept nicht mit TRUNCATE abgeschnitten werden, auch nicht wenn für den Fremdschlüssel eine ON DELETE CASCADE-Option angegeben wurde.
4.15
SQL-Funktionen SQL Server stellt eine Vielzahl von integrierten Funktionen bereit, die Sie in Ausdrücken bzw. Abfragen verwenden können. In diesem Abschnitt soll lediglich eine Übersicht über die wichtigsten dieser Funktionen gegeben werden. Eine vollständige Beschreibung aller verfügbaren Funktionen würde ein eigenes Buch füllen, daher finden Sie an dieser Stelle eine Auswahl der am häufigsten benötigten Funktionen. Außerdem werden einige der integrierten Funktionen in späteren Kapiteln benötigt und dann auch dort näher erklärt.
126
4.15 SQL-Funktionen In diesem Abschnitt finden Sie eine Auswahl von Funktionen aus den Bereichen Systemfunktionen, Konfigurationsfunktionen, numerische Funktionen, Zeichenkettenfunktionen, Konvertierungsfunktionen, Datums- und Zeitfunktionen sowie Rangfolgefunktionen.
4.15.1 Systemfunktionen SQL Server-Systemfunktionen liefern Informationen zu einer SQL Server-Instanz. Die am häufigsten benutzten Systemfunktionen finden Sie in den folgenden Abschnitten. HOST_NAME Syntax.
Erklärung. Die Funktion HOST_NAME() gibt den Namen des Computers zurück. Beispiele.
IDENT_CURRENT Syntax.
Erklärung. Die Funktion liefert den letzten generierten Identitätswert für die IDENTITY-Spalte einer Tabelle. Beispiele.
SERVERPROPERTY Syntax.
Erklärung. Die Funktion fragt Eigenschaften der SQL Server-Instanz ab. Hierbei muss die Eigenschaft, die abgefragt werden soll, als Zeichenkette an die Funktion übergeben werden. Für eine vollständige Aufstellung aller verfügbaren Eigenschaften möchte ich Sie an die Online-Dokumentation von SQL Server verweisen. Hier nur einige Beispiele: Eigenschaft
Rückgabewert
Edition
Der Name der SQL Server Edition, also zum Beispiel: Enterprise Edition Express Edition Developer Edition
InstanceName
Der Name der SQL Server-Instanz.
127
4 Transact SQL-Grundlagen Eigenschaft
Rückgabewert
MachineName
Der Name des Computers, auf dem SQL Server läuft.
ProductVersion
Die Versionsnummer von SQL Server in der Form: major.minor.build
ProductLevel
Gibt Informationen zum installierten Servicepack zurück. Mögliche Rückgabewerte sind: RTM Für die Originalversion von SQL Server SPn Für ein installiertes Servicepack mit der Nummer n
Beispiele.
SESSION_USER Syntax.
Erklärung. Die Funktion gibt den Namen des angemeldeten Datenbankbenutzers zurück. Beispiele.
SYSTEM_USER Syntax.
Erklärung. Gibt den Namen der SQL Server-Anmeldung zurück. Beispiele.
128
4.15 SQL-Funktionen
4.15.2 Konfigurationsfunktionen Konfigurationsfunktionen liefern Informationen zu SQL Server-Konfigurationsoptionen. Dieser Funktionstyp ist daran zu erkennen, dass der Name mit einem doppelten @@ beginnt. Die folgenden Konfigurationsfunktionen sind besonders wichtig: @@DATEFIRST Syntax.
Erklärung. @@DATEFIRST zeigt den ersten Tag in der Woche an. Für eine deutsche SQL Server-Version ist dieser Wert 1 für Montag. In einer US-englischen Version ist der Wert 7 für Sonntag. Für eine Verbindung zu SQL Server kann der Wert mit der SET-Anweisung SET DATEFIRST angepasst werden (siehe unten). Beispiele.
@@SERVERNAME Syntax.
Erklärung. Gibt den Namen der SQL Server-Instanz in der Form servername\instanzname zurück. Wenn eine Standardinstanz von SQL Server installiert ist, wird der Name des Computers zurückgegeben. Beispiele.
@@SPID Syntax.
Erklärung. Gibt die ID der aktuellen Verbindung zurück. Jede Verbindung zu SQL Server bekommt eine eindeutige Verbindungs-ID, mit der zum Beispiel bestimmt werden kann, welche Blockierungen diese Verbindung auslöst. Beispiele.
129
4 Transact SQL-Grundlagen @@VERSION Syntax. @@version Erklärung. Gibt Versionsinformationen zu SQL Server zurück. Hierzu gehören Prozessortyp, Erstellungsdatum und Informationen zum Betriebssystem. Beispiele.
4.15.3 Metadatenfunktionen Metadatenfunktionen geben Informationen zu Datenbanken und Datenbankobjekten zurück. Zu den interessanteren Metadatenfunktionen zählen die folgenden: DATABASEPROPERTY Syntax.
Erklärung. Die Funktion gibt den Wert einer Datenbankeigenschaft zurück. Die Namen der Datenbank und der abzufragenden Eigenschaft müssen als Zeichenkette an die Funktion übergeben werden. Die Liste der zur Verfügung stehenden Eigenschaften ist lang, in der folgenden Tabelle finden Sie nur einige Beispiele: Eigenschaft
Beschreibung
IsOffline
Gibt 1 zurück, wenn die Datenbank offline ist, ansonsten 0.
IsReadOnly
Gibt 1 zurück, wenn die Datenbank schreibgeschützt ist, ansonsten 0.
IsSingleUser
Gibt 1 zurück, wenn die Datenbank im Einzelbenutzermodus ist, ansonsten 0.
Beispiele.
DATABASEPROPERTYEX Syntax.
Erklärung. Über diese Funktion können erweiterte Eigenschaften einer Datenbank abgefragt werden. DATABASEPROPERTYEX erwartet ebenfalls den Datenbanknamen und den Namen der abzufragenden Eigenschaft als Parameter. Die folgende Tabelle enthält eine Auswahl der zur Verfügung stehenden Eigenschaften:
130
4.15 SQL-Funktionen Eigenschaft
Erklärung
Collation
Die Bezeichnung der Standardsortierung der Datenbank
IsAutoClose
1, wenn die Datenbank im Auto-Close-Modus ist, ansonsten 0. Eine Datenbank im Auto-Close-Modus wird automatisch heruntergefahren, wenn keine Verbindungen mehr zu ihr existieren.
Recovery
Liefert das Wiederherstellungsmodell für die Datenbank. Der Wert dieser Eigenschaft kann FULL, BULK_LOGGED oder SIMPLE sein. Näheres hierzu erfahren Sie in Kapitel 6.
Status
Liefert den momentanen Status der Datenbank. Dieser Status kann zum Beispiel ONLINE, OFFLINE oder RESTORING sein.
Beispiele.
DB_ID Syntax.
Erklärung. Für jede Datenbank wird intern eine ID vergeben, die oftmals benötigt wird, um Tabellen oder Sichten des Systemkatalogs abzufragen. Mit der Funktion DB_ID kann diese ID ermittelt werden. Hierzu muss der Funktion der Datenbankname übergeben werden. Der Datenbankname kann auch weggelassen werden, in diesem Fall wird der Name der aktuellen Datenbank eingesetzt. Beispiele.
DB_NAME Syntax.
Erklärung. In den SQL Server-Systemkatalogen wird stets nur die ID der Datenbank gespeichert. Um den einer solchen ID zugeordneten Datenbanknamen zu ermitteln, kann die Funktion DB_NAME verwendet werden. DB_NAME ist die Umkehrung zu DB_ID. Die Funktion gibt zu einer bekannten Datenbank-ID den Namen der Datenbank zurück. Die Übergabe der ID ist optional. Wenn keine ID übergeben wird, so wird die aktuelle Datenbank-ID eingesetzt.
131
4 Transact SQL-Grundlagen Beispiele.
OBJECT_NAME Syntax.
Erklärung. Auch Datenbankobjekte werden mit einer ID also einer eindeutigen Kennung versehen. Wenn Sie Systemkataloge abfragen oder sich Informationen zu Blockierungen bzw. aktuellen Aktivitäten im Aktivitätsmonitor ansehen, wird in der Regel nur die ID der beteiligten Objekte, wie zum Beispiel Tabellen, abgezeigt. Über die Funktion OBJECT_NAME kann aus einer solchen ID der Name des Objektes ermittelt werden. Die übergebene ID wird dabei immer im Kontext der aktuellen Datenbank ausgewertet. Beispiele.
4.15.4 Numerische Funktionen In diesem Abschnitt werden die in SQL Server integrierten mathematischen Funktionen vorgestellt. Winkelfunktionen SQL Server unterstützt die in der folgenden Tabelle aufgeführten Winkelfunktionen:
132
Funktion
Syntax
Erklärung
ACOS
x
Berechnet den Arcuskosinus im Bogenmaß. Der Wert für x muss zwischen 1 und 1 liegen, ansonsten wird ein Fehler erzeugt.
ASIN
x
Berechnet den Arcussinus im Bogenmaß. Der Wert für x muss zwischen 1 und 1 liegen, ansonsten wird ein Fehler erzeugt.
ATAN
x
Berechnet den Arcustangens im Bogenmaß.
COS
x
Berechnet den Kosinus für den Winkel x. Der Winkel muss im Bogenmaß übergeben werden.
COT
x
Berechnet den Kotangens des im Bogenmaß übergebenen Winkels.
DEGREES
x
Konvertiert den im Bogenmaß übergebenen Winkel in einen Grad-Wert. Zu beachten ist, dass der Typ des Rückgabewertes dem Typ des im Parameter übergebenen Wertes entspricht. So werden zum Beispiel die beiden folgenden Aufrufe unterschiedliche Ergebnisse liefern:
4.15 SQL-Funktionen Funktion
Syntax
Erklärung
Ursache ist, dass der erste Aufruf einen Wert vom Typ int zurückliefert, der zweite einen Wert vom Typ float. Diese Rückgabetypen werden jeweils aus dem Typ des Parameters abgeleitet, der int bzw. float ist. PI
Berechnet den Wert von Pi (3,1415926
)
SIN
x
Berechnet den Sinus des im Bogenmaß übergebenen Winkels.
TAN
x
Berechnet den Tangens des im Bogenmaß übergebenen Winkels.
RADIANS
x
Konvertiert den als Grad übergebenen Winkel in das Bogenmaß. Auch hier wird wie bei DEGREE der Wert des Rückgabetyps aus dem Typ des übergebenen Parameters abgeleitet.
Beispiele für den Aufruf der Winkelfunktionen.
ABS Syntax.
Erklärung. Berechnet den Absolutwert des übergebenen Ausdrucks. Zu beachten ist, dass der Rückgabetyp des berechneten Wertes aus dem Typ des übergebenen Parameters abgeleitet wird. Beispiele.
CEILING Syntax.
Erklärung. Gibt die kleinste ganze Zahl zurück, die größer oder gleich dem übergebenen Ausdruck ist. Beispiele.
133
4 Transact SQL-Grundlagen EXP Syntax.
Erklärung. Berechnet den Wert ex für den übergebenen Ausdruck x. Beispiele.
FLOOR Syntax.
Erklärung. Gibt die größte ganze Zahl zurück, die kleiner oder gleich dem übergebenem Ausdruck ist. Beispiele.
LOG Syntax.
Erklärung. Berechnet den natürlichen Logarithmus, also den Logarithmus zur Basis e. Der übergebene Parameter muss größer als 0 sein. Beispiele.
LOG10 Syntax.
Erklärung. Berechnet für den übergebenen Ausdruck den Logarithmus zur Basis 10. Der übergebene Ausdruck muss einen Wert größer 0 liefern. Beispiele.
POWER Syntax.
134
4.15 SQL-Funktionen Erklärung. Berechnet für die übergebenen Parameter die Potenz, also den Wert xy. Beispiele.
RAND Syntax.
Erklärung. Liefert einen Zufallswert im Bereich 0 bis 1. Über den Parameter x kann ein Ausgangswert festgelegt werden, der für die Verbindung zu SQL Server gilt. Wenn ein solcher Ausgangswert angegeben wird, so erzeugen alle nachfolgenden Aufrufe von RAND innerhalb der Verbindung immer dieselben Werte (siehe Beispiele). Der Parameter x ist vom Typ int. Beispiele.
ROUND Syntax.
Erklärung. Rundet den Wert x auf die angegebene Länge oder schneidet den Wert ab. Ob gerundet oder abgeschnitten wird, hängt vom Parameter typ ab. Ist dieser Parameter 0 (dies ist auch der Standardwert), so erfolgt eine Rundung, wenn typ ungleich 0 ist, so wird nicht gerundet, sondern abgeschnitten. Beispiele. Die folgende Tabelle zeigt einige Beispiele für den Aufruf von ROUND und das zurückgelieferte Ergebnis. Aufruf
Ergebnis
100,500
100,500
100,400
1360,000
1400,000
1000,000
0,000
135
4 Transact SQL-Grundlagen SQRT Syntax.
Erklärung. Berechnet die Quadratwurzel von x. Der Wert x darf nicht kleiner als 0 sein. Beispiele.
SQUARE Syntax.
Erklärung. Berechtet das Quadrat von x, also den Wert x * x. Beispiele.
4.15.5 Funktionen zur Zeichenkettenbearbeitung SQL Server bietet die im folgenden Abschnitt aufgeführten Funktionen zur Zeichenkettenbearbeitung. Mit diesen Funktionen können zum Beispiel Zeichenketten durchsucht, umgewandelt oder Teilbereiche in Zeichenketten durch angegebenen Text ersetzt werden. CHAR Syntax.
Erklärung. Die CHAR-Funktion wandelt einen ASCII-Code in ein Zeichen um. Dies ist insbesondere nützlich, wenn Steuerzeichen in die Ausgabe eingefügt werden sollen. Hierzu kann die CHAR-Funktion mit diesen Parametern verwendet werden: Tabluator
char(9)
Zeilenvorschub
char(10)
Wagenrücklauf
char(13)
Beispiele.
136
4.15 SQL-Funktionen CHARINDEX Syntax.
Erklärung. Gibt die Position von zeichenkette1 innerhalb von zeichenkette2 zurück. Wird zeichenkette1 nicht gefunden, so gibt die Funktion 0 zurück. Über den Parameter start kann angegeben werden, an welcher Position die Suche beginnen soll. Der Standard ist 1 für den Anfang von zeichenkette2. Beispiele.
DIFFERENCE Syntax.
Erklärung. Gibt eine ganze Zahl zurück, die den phonetischen Unterschied zwischen zwei Zeichenketten angibt. Diese Zahl liegt im Bereich von 0 bis 4, wobei 0 angibt, dass keine Übereinstimmung besteht und 4 für die größtmögliche Ähnlichkeit steht. Mit der Funktion kann zum Beispiel in Namenslisten nach ähnlich lautenden Namen gesucht werden, was durch einen Mustervergleich mit LIKE nicht immer möglich ist. Beispiele.
LEFT Syntax.
Erklärung. Schneidet eine zeichenkette nach der angegebenen Anzahl von zeichen ab, gibt also nur den linken Teil der zeichenkette zurück. Beispiele.
137
4 Transact SQL-Grundlagen LEN Syntax.
Erklärung. Gibt die Anzahl von Zeichen in einer zeichenkette zurück. Beispiele.
LOWER Syntax.
Erklärung. Gibt eine Zeichenkette zurück, in der alle Zeichen aus der übergebenen zeichenkette in Kleinbuchstaben konvertiert wurden. Beispiele.
LTRIM Syntax.
Erklärung. Gibt eine Zeichenkette zurück, in der führende Leerzeichen in der übergebenen zeichenkette entfernt wurden. Beispiele.
PATINDEX Syntax.
Erklärung. Sucht ein muster innerhalb einer zeichenkette und gibt die Position des ersten Auftretens dieses Musters innerhalb der Zeichenkette aus. Wird keine Übereinstimmung festgestellt, gibt die Funktion 0 zurück. Für das Muster können die beim LIKE-Operator angegebenen Platzhalter verwendet werden. Beispiele. Angenommen, die zu durchsuchende Zeichenkette ist:
138
4.15 SQL-Funktionen Die folgende Tabelle enthält einige ausgewählte Beispiele für Muster und das Ergebnis des Funktionsaufrufs: Muster
Erklärung
Ergebnis
'%woch%'
Sucht nach der Zeichenkette woch.
8
'%[^aeiou]%'
Sucht nach dem ersten Auftreten eines Zeichens, das kein Vokal ist.
2
'%[def]%'
Sucht nach dem Buchstaben d, e oder f.
15
'%[0-9][0-9]:[0-9][0-9]%'
Sucht nach einer Zeichenkette im Format 00:00, die also eine Uhrzeit repräsentiert.
32
'%x%'
Sucht nach dem Auftreten des Buchstabens x.
0
In Abfragen kann die Funktion PATINDEX zum Beispiel so verwendet werden:
REPLACE Syntax.
Erklärung. Gibt eine Zeichenkette zurück, in der alle Vorkommen von zeichenkette2 in zeichenkette1 durch zeichenkette3 ersetzt wurden. Beispiele.
REPLICATE Syntax.
Erklärung. Gibt eine Zeichenkette zurück, die aus anzahl Wiederholungen der übergebenen zeichenkette besteht.
139
4 Transact SQL-Grundlagen Beispiele.
REVERSE Syntax.
Erklärung. Gibt die übergebene Zeichenkette umgekehrt also von hinten nach vorne aus. Beispiele.
RIGHT Syntax.
Erklärung. Gibt den anzahl Zeichen langen rechten Teil der übergebenen zeichenkette zurück. Beispiele.
RTRIM Syntax.
Erklärung. Gibt eine Zeichenfolge zurück, aus der am Ende stehende Leerzeichen entfernt wurden. Beispiele.
SOUNDEX Syntax.
140
4.15 SQL-Funktionen Erklärung. SOUNDEX gibt eine Zeichenkette zurück, die aus vier Zeichen besteht. Diese Zeichenkette ist eine Kodierung der übergebenen zeichenkette und kann zum Vergleich der phonetischen Ähnlichkeit verwendet werden. Beispiele.
SPACE Syntax. space(anzahl) Erklärung. Gibt eine Zeichenfolge zurück, die aus der übergebenen Anzahl von Leerzeichen besteht. Beispiele.
STUFF Syntax.
Erklärung. Die Funktion gibt eine Zeichenkette zurück. In dieser Zeichenkette wurden anzahl Zeichen aus zeichenkette1 beginnend ab der Position start entfernt und durch zeichenkette2 ersetzt. Beispiele.
SUBSTRING Syntax.
Erklärung. Liest einen Teil der im Parameter ausdruck übergebenen Daten. Es werden anzahl Zeichen, beginnend an der Stelle start, gelesen. Im ausdruck können hierbei nicht nur Zeichenketten, sondern Werte der Datentypen text, image, binary oder varbinary stehen.
141
4 Transact SQL-Grundlagen Beispiele.
UPPER Syntax.
Erklärung. Gibt eine Zeichenkette zurück, in der alle Zeichen der übergebenen zeichenkette in Großbuchstaben konvertiert wurden. Beispiele.
4.15.6 Konvertierungsfunktionen Konvertierungsfunktionen erlauben eine explizite Typkonvertierung, also die Überführung eines Wertes aus einem Datentyp in einen anderen. Hierdurch können zum Beispiel numerische Werte in Zeichenketten oder Zeichenketten in Datums- und Zeitwerte umgewandelt werden. SQL Server stellt hierfür die folgenden Funktionen zur Verfügung: CONVERT Syntax.
Erklärung. Die Funktion CONVERT nimmt eine explizite Typkonvertierung des übergebenen Ausdrucks in den im ersten Parameter angegebenen Datentyp vor und gibt den konvertierten Wert zurück. Falls die Typkonvertierung nicht durchgeführt werden kann, liefert CONVERT eine Fehlermeldung zurück. Der Parameter format wird vor allem für die Umwandlung von datetime-Werten in Zeichenketten und von Zeichenketten in datetime-Werte benötigt. Er bestimmt jeweils das Format der Zeichenkette. Soll eine Zeichenkette in einen datetime-Wert umgewandelt werden, so wird über den Parameter format das Format des Parameters ausdruck festgelegt. Anderenfalls also wenn ein datetime-Wert im Parameter ausdruck übergeben wird, der in eine Zeichenkette umgewandelt werden soll bestimmt format das Aussehen der zurückgelieferten Zeichenkette. Der Parameter legt durch einen integer-Wert fest, wie die Zeichenkette aussehen soll. Hierbei wird bei Werten über 100 das Datum vierstellig also mit Jahrhundertangabe interpretiert. Für Werte kleiner 100 wird das Jahr nur zweistellig angenommen. Die folgende Tabelle zeigt die wichtigsten Formate am Beispiel des Datums:
142
4.15 SQL-Funktionen Ohne
Mit
Format
Bemerkung
Jahrhundert 1
101
12/19/2004
US-Format
2
102
2004.12.19
ANSI-Format
3
103
19/12/2004
Britisch und Französisch
4
104
19.12.2004
Deutsches Format
5
105
19-12-2004
Italienisches Format
6
106
19 Dez 2004
7
107
Dez 10, 2004
8
108
14:24:12
Zeitanteil ohne Millisekunden
9 oder 109
Dez 19 2004 2:24:12
Datum und Uhrzeit im 12-Stunden-Format
10
110
12-19-2004
US-Format
11
111
2004/12/19
Japanisches Format
12
112
20041219
ANSI-Fomat
14:24:12.323
Zeitanteil im 24-Stunden-Format mit Millisekunden
14 oder114
Beispiele.
CAST Syntax.
Erklärung. CAST dient ebenfalls zur expliziten Konvertierung eines Wertes in einen anderen Datentyp. Der an die Funktion übergebene ausdruck wird als Wert vom Datentyp datentyp dargestellt, der ebenfalls an die Funktion CAST übergeben wird. Im Unterschied zu CONVERT erlaubt die Funktion CAST jedoch nicht die Angabe eines Parameters für die Formatierung. Beispiele.
143
4 Transact SQL-Grundlagen STR Syntax.
Erklärung. Die Funktion STR konvertiert einen numerischen Ausdruck in eine Zeichenkette. In dieser Zeichenkette wird der konvertierte Ausdruck mit der im Parameter dezimalstellen angegebenen Anzahl von Dezimalstellen dargestellt und ist so lang, wie im Parameter länge angefordert. Diese beiden Parameter sind optional. Der Standardwert für länge ist 10, für dezimalstellen ist der Standardwert 0. Wenn Dezimalstellen abgeschnitten werden, so erfolgt eine Rundung. Beispiele.
4.15.7 Datums- und Zeitfunktionen Die SQL Server-Datums- und -Zeitfunktionen ermöglichen das Rechnen mit Werten vom Typ datetime und das Zerlegen dieser Werte in die einzelnen Bestandteile, wie zum Beispiel Jahr, Monat oder Tag. In Funktionen, die Berechnungen mit datetime-Werten durchführen, muss hierbei oftmals ein Intervall angegeben werden. Dieses Intervall wird in der Parameterliste der im folgenden Teil erklärten Funktionen stets mit intervall bezeichnet. Erwartet wird dort stets eine Konstante, welche die in der folgenden Tabelle 4.18 aufgeführten Werte annehmen kann. Tabelle 4.18 Intervalle für das Rechnen mit datetime-Werten
144
Intervall
Synonyme
Erklärung
year
yy, yyyy
Steht für ein gesamtes Kalenderjahr
quarter
qq, q
Steht für ein Quartal
month
mm, m
Steht für einen Monat
dayofyear
dy, y
Steht für den Tag im Jahr, also den Tag ab Jahresbeginn
day
dd, d
Steht für einen Tag
week
wk, ww
Steht für eine Woche
weekday
dw, w
Steht für den Tag in der Woche, also die Nummer des Wochentags
hour
hh
Steht für eine Stunde
minute
mi, n
Steht für eine Minute
second
ss, s
Steht für eine Sekunde
millisecond
ms
Steht für eine Millisekunde
4.15 SQL-Funktionen DATEADD Syntax.
Erklärung. Gibt einen datetime-Wert zurück, in dem zum übergebenen datetimewert die angegebene anzahl von intervallen addiert wurde. Für den Parameter intervall muss eine der in Tabelle 4.18 genannten Konstanten angegeben werden. Beispiele.
DATEDIFF Syntax.
Erklärung. Gibt die Anzahl von intervallen zurück, die zwischen den beiden datetime-Werten start und ende liegt. Für intervall kann dabei einer der in Tabelle 4.18 genannten Konstanten eingesetzt werden. Der Rückgabewert ist immer ganzzahlig, ein Wert wie zum Beispiel 0,68 Jahre wird abgerundet auf 0 (Jahre). Beispiele.
DATENAME Syntax.
Erklärung. Die Funktion gibt eine Zeichenkette zurück, welche die Bezeichnung für den im Parameter intervall angegebenen Datumsteil des übergebenen datetime-Wertes enthält. Echte Bezeichnungen werden hierbei nur für die Intervall-Konstanten month und weekday ausgegeben, alle andere Werte für intervall, wie zum Beispiel year, month oder hour, geben die entsprechende Zahl zurück. Beispiele.
145
4 Transact SQL-Grundlagen DATEPART Syntax.
Erklärung. Gibt einen integer-Wert zurück, der den über intervall angegebenen Teil des übergebenen datetimewertes enthält. Beispiele.
DAY Syntax.
Erklärung. Gibt einen integer-Wert zurück, der den Tag des übergebenen datetime-Wertes enthält. ist ein Synonym für . Beispiele.
GETDATE Syntax.
Erklärung. Die Funktion gibt das aktuelle SQL Server-Datum inklusive Zeit zurück. Beispiele.
146
4.15 SQL-Funktionen
MONTH Syntax.
Erklärung. Gibt einen integer-Wert zurück, der den Monat des übergebenen datetime-Wertes enthält. ist ein Synonym für . Beispiele.
YEAR Syntax.
Erklärung. Gibt einen integer-Wert zurück, der das Jahr des übergebenen datetime-Wertes enthält. ist ein Synonym für . Beispiele.
Oftmals werden Sie in Ihren Datenbanken eine Kalendertabelle benötigen, in der einfach nur Datumswerte gespeichert sind. Eine solche Tabelle ist in vielen Abfragen nützlich. Mit den vorgestellten Datums- und Zeitfunktionen kann eine solche Tabelle so erstellt werden:
147
4 Transact SQL-Grundlagen
Die Tabelle Kalender wird hier im Schema Extras angelegt. Diese Tabelle enthält eigentlich nur eine einzige echte Spalte mit Namen Datum. In dieser Spalte wird pro Zeile ein Datumswert gespeichert. Alle anderen Spalten der Tabelle sind berechnete Spalten, welche Datumsanteile mit den gerade vorgestellten Datums- und Zeitfunktionen ermitteln. Die folgenden INSERT-Anweisungen fügen zur Tabelle Extras.Kalender einige Zeilen hinzu:
Um alle Zeilen und Spalten der Tabelle zu erhalten, kann diese Abfrage verwendet werden:
Abbildung 4.22 zeigt das Ergebnis der obigen Abfrage.
Abbildung 4.22 Inhalt der Tabelle Extras.Kalender
Wir werden die Tabelle Extras.Kalender im weiteren Verlauf dieses bzw. nachfolgender Kapitel noch modifizieren und dabei auch sehen, wie diese Tabelle mit Werten gefüllt werden kann.
148
4.15 SQL-Funktionen
4.15.8 Sonstige Funktionen und Operatoren In diesem Abschnitt werden weitere Funktionen aufgeführt, die in keine der bisherigen Kategorien eingeordnet werden konnten. CASE Syntax.
Erklärung. Die CASE-Funktion wertet eine Reihe von Bedingungen aus. Trifft eine Bedingung zu, so wird der für diese Bedingung angegebene Ausdruck berechnet und dessen Wert zurückgegeben. Eine CASE-Funktion kann in zwei unterschiedlichen Formaten angegeben werden: Einfache CASE-Funktion. Hier wird der Wert eines Ausdrucks nacheinander in unterschiedlichen WHEN-Anweisungen mit verschiedenen diskreten Werten verglichen. Ist der ausdruck mit einem in der WHEN-Anweisung aufgeführten wert identisch, so wird das in der THEN-Klausel zu dieser WHEN-Anweisung angegebene ergebnis zurückgegeben. Komplexe CASE-Funktion. In der komplexen CASE-Funktion enthält jede WHENAnweisung eine eigene Bedingung, die durch einen booleschen Ausdruck beschrieben wird. Liefert dieser Ausdruck den Wert WAHR, so wird das im zugehörigen THEN-Zweig angegebene Ergebnis zurückgegeben. In beiden Formaten kann jeweils eine optionale ELSE-Klausel enthalten sein. Der in dieser Klausel angegebene Wert wird zurückgegeben, wenn keine der vorherigen Bedingungen bzw. Vergleiche zutreffend ist. Beispiele.
149
4 Transact SQL-Grundlagen
@@ERROR Syntax.
Erklärung. Die Funktion @@ERROR gibt die Fehlernummer für die zuletzt ausgeführte T-SQL-Anweisung zurück. Wenn diese Anweisung fehlerfrei war, wird der Wert 0 zurückgegeben. Zu beachten ist, dass wirklich jede T-SQL-Anweisung den von dieser Funktion zurückgelieferten Wert beeinflusst. Dadurch muss diese Funktion unmittelbar nach einer T-SQL-Anweisung aufgerufen werden, um die Fehlernummer zu erhalten. Die Fehlernummern von SQL Server sind mit dem zugehörigen Text in der Systemsicht enthalten. Diese Systemsicht können Sie abfragen, um den Fehlertext zu erhalten (siehe Beispiel). SQL Server bietet die Möglichkeit der strukturierten Ausnahmebehandlung mit TRY/CATCH-Konstrukten. Sie sollten dieser Möglichkeit den Vorzug gegenüber @@ERROR geben, da sie wesentlich leistungsfähiger ist. Mehr hierzu erfahren Sie in Kapitel 7. Beispiele.
ISDATE Syntax.
Erklärung. Die Funktion ISDATE gibt 1 zurück, wenn der angegebene ausdruck ein gültiges Datum zurückliefert, ansonsten 0. Diese Funktion ist besonders für Konvertierungen nützlich, da vor der Konvertierung in einen datetime-Wert geprüft werden
150
4.15 SQL-Funktionen kann, ob die Konvertierung überhaupt möglich ist und nicht eventuell einen Fehler zurückliefert. Beispiele.
Hier ist das Ergebnis der obigen Abfrage:
ISNUMERIC Syntax.
Erklärung. Die Funktion ISNUMERIC gibt 1 zurück, wenn der übergebene ausdruck als numerischer Wert interpretiert werden kann, ansonsten 0. Auch diese Funktion ist für Typumwandlungen nützlich, da vor der Umwandlung in einen numerischen Datentyp geprüft werden kann, ob dies überhaupt möglich ist. Beispiele.
NEWID Syntax.
Erklärung. Jeder Aufruf erzeugt einen eindeutigen Wert vom Typ uniqueidentifier. Beispiele.
151
4 Transact SQL-Grundlagen
@@ROWCOUNT Syntax.
Erklärung. Die Funktion @@ROWCOUNT gibt die Anzahl der Zeilen zurück, die durch die vorhergehende T-SQL-Anweisung geändert oder abgefragt wurde. Wie bereits bei der @@ERROR-Funktion wird auch @@ROWCOUNT durch jede T-SQLAnweisung beeinflusst, muss also immer unmittelbar nach der entsprechenden T-SQLAnweisung abgefragt werden. Beispiele.
WAITFOR Syntax.
Erklärung. WAITFOR blockiert die Ausführung eines T-SQL-Stapels so lange, bis entweder der über die Option TIME angegebene zeitpunkt erreicht oder die über die Option DELAY angegebene zeitspanne verstrichen ist. Sowohl der Zeitpunkt als auch die Zeitspanne werden hierbei in der Form 'HH:MM:SS' angegeben. Dadurch kann eine WAITFOR-Blockierung maximal 24 Stunden dauern. Beispiele.
152
4.16 Die Beispieldatenbank AdventureWorks
4.16
Die Beispieldatenbank AdventureWorks Für die Beispiele in den folgenden Kapiteln wird die einfache Datenbank OmasKochbuch oftmals nicht ausreichend sein. Wir werden in vielen Fällen auf die Beispieldatenbank AdventureWorks zurückgreifen, allerdings benötigen wir für unsere Beispiele nicht die komplette Datenbank, sondern lediglich eine Handvoll von Tabellen und Sichten, die in Tabelle 4.19 und Abbildung 4.23 erklärt bzw. dargestellt sind. Tabelle 4.19 Verwendete Tabellen aus der AdventureWorks-Datenbank Tabelle/Sicht
Erklärung
Sales.vSalesPerson
Eine Sicht mit der Liste der Vertriebsmitarbeiter.
HumanResources.vEmployee
Eine Sicht mit der Liste aller Angestellten.
Sales.SalesOrderHeader
Eine Tabelle mit allen Bestellungen. Über die Spalte SalesPersonID existiert ein Fremdschlüssel zur Sicht vSalesPerson. Die Spalte kann allerdings auch NULL sein, es ist also möglich, Bestellungen ohne Vertriebsmitarbeiter einzufügen.
Sales.SalesOrderDetail
Eine Tabelle mit allen Bestellpositionen. Jede Position ist über die Spalte SalesOrderID einer Bestellung zugeordnet.
Production.ProductCategory
Eine Tabelle mit den Produktkategorien.
Production.ProductSubCategory
Eine Tabelle mit Produkt-Unterkategorien. Jede Unterkategorie kann einer Kategorie über die Spalte ProductCategoryID zugeordnet werden. Diese Zuordnung ist allerdings nicht erforderlich, d.h., es kann auch Unterkategorien ohne Kategorie geben.
Production.Product
Eine Tabelle mit der Liste aller Produkte. Ein Produkt kann über die Spalte ProductSubCategoryID in eine Unterkategorie eingeordnet werden, dies ist aber nicht zwingend notwendig.
Abbildung 4.23 auf der nächsten Seite zeigt ein Datenbankdiagramm, in dem Sie sehen können, wie die oben aufgeführten Tabellen zueinander in Beziehung stehen. Diese Tabellen enthalten übrigens mehr Spalten als im Diagramm dargestellt sind, für eine bessere Übersicht präsentiert das Diagramm nur die in unseren Beispielen benötigten Spalten. Wir werden die Spalte Sales.SalesOrderHeader.TotalDue im Folgenden als Bestellwert interpretieren, auch wenn dies sicher nicht ganz korrekt ist. Für die verwendeten Beispiele ist dies jedoch unerheblich.
153
4 Transact SQL-Grundlagen
Abbildung 4.23 Auszug aus der Datenbank AdventureWorks
4.17
Sichten SQL Server erlaubt die Erstellung von Sichten, die gespeicherte Abfragen enthalten. Eine Sicht ist ein Datenbankobjekt, das wie alle anderen Datenbankobjekte auch unter einem Namen in einem Schema der Datenbank abgelegt wird. Hinter diesem Namen verbirgt sich im Falle einer Sicht einfach eine Abfrage. Eine Sicht wird durch die Anweisung CREATE VIEW erzeugt, welche die folgende generelle Syntax aufweist:
Sichten sind vor allem nützlich, wenn komplexe Abfragen immer wieder verwendet werden müssen. In einem solchen Fall können Sie durch die Definition entsprechender Sichten für diese komplexen Abfragen sozusagen virtuelle Tabellen einrichten. Eine Sicht ist dann letztlich nichts anderes als eine vordefinierte Anordnung von Projektion und Selektion, so wie bereits in Abbildung 4.21 dargestellt. Um die Daten von Kochrezepten zu erhalten, die weniger als 1200 Joule schwer sind, können Sie die folgende Abfrage verwenden:
154
4.17 Sichten
Falls Sie in Ihrem T-SQL Code wieder und wieder solche leichten Gerichte benötigen, so wird die obige Abfrage an den entsprechenden Stellen im Code stets erneut angegeben werden müssen. In diesem Fall kann sich die Definition einer Sicht lohnen, die so angelegt wird:
Die Sicht Kochen.vLeichteGerichte definiert eine virtuelle Tabelle, die in SELECT- und eventuell auch in UPDATE-, INSERT- und DELETE-Anweisungen verwendet werden kann. Wichtig ist hierbei, dass Sie verstehen, dass in der Sicht keine Daten gespeichert werden. Durch CREATE VIEW wird also nicht etwa eine Kopie der entsprechenden Tabellenzeilen angelegt, sondern tatsächlich nur die enthaltene Abfrage gespeichert. Beim Zugriff auf die Sicht durch eine der genannten Anweisungen wird dann diese Abfrage ausgeführt. Im Objekt-Explorer werden die in einer Datenbank existierenden Sichten im Zweig Sichten angezeigt. Hier erscheint auch die gerade angelegte Sicht Kochen.vLeichteGerichte:
Eine Sicht kann in einer Abfrage wie eine Tabelle verwendet werden. Die folgende Abfrage gibt alle Zeilen der Sicht zurück:
Die obige SELECT-Anweisung bewirkt letztlich die Ausführung der in der Sicht hinterlegten Abfrage, also:
Das Ergebnis ist dann wie erwartet:
155
4 Transact SQL-Grundlagen Sichten ermöglichen es Ihnen, eine Datenzugriffsschicht für bestimmte Anwender zu erstellen. So ist es zum Beispiel denkbar, dass Sie für Benutzer, die Berichte entwerfen sollen, ein spezielles Schema in Ihrer Datenbank anlegen und in diesem Schema Sichten ablegen, welche die Daten für Ihre Berichtsentwickler bereitstellen. Die Entwickler von Berichten müssen dadurch nicht die komplette Datenbank kennen und begreifen, sondern können auf eine Datenbasis zurückgreifen, die sie verstehen und für die sie auch entsprechende Zugriffsberechtigungen besitzen.
4.17.1 Ändern von Sichten Stellen Sie sich vor, dass die Bedingung für die Einstufung eines Gerichtes in die Kategorie Leicht sich geändert hat. Von nun an sollen alle Gerichte, deren Energiegehalt kleiner als 1400 Joule ist, als Leichte Gerichte angesehen werden. Ohne eine Sicht für die Abfrage von leichten Gerichten müssten Sie nun alle Stellen in Ihrem T-SQL-Code entsprechend anpassen und die Zeilenfilterbedingung von
ändern in:
Bei der Verwendung einer Sicht können Sie einfach nur Definition der Sicht anpassen ein weiterer Vorteil bei der Verwendung von Sichten. Zum Verändern einer bestehenden Sicht verwenden Sie die Anweisung ALTER VIEW wie folgt:
Für das Ändern unserer Zeilenfilterbedingung können wir also schreiben:
Ab sofort wird dann an allen Stellen, an denen auf die Sicht Kochen.vLeichteGerichte zugegriffen wird, die geänderte Definition der Sicht verwendet.
4.17.2 Aktualisierung von Daten Die Interpretation einer Sicht als virtuelle Tabelle wirft natürlich die Frage auf, inwiefern es möglich ist, die Daten dieser Tabelle auch zu verändern. Wie verhalten sich UPDATE-, INSERT- und DELETE-Anweisungen in Verbindung mit Sichten? Unter Beachtung einiger Einschränkungen ist es tatsächlich möglich, Datenänderungsoperationen auf Sichten durchzuführen. Zunächst einmal müssen alle von einer Sicht zurückgegebenen Zeilen eindeutig den dahinter liegenden Tabellen zugeordnet werden können. In einer Sicht, die nur eine Tabelle enthält, ist dies in der Regel kein Problem. Eine Sicht ge-
156
4.17 Sichten stattet jedoch auch die Verwendung komplexerer Abfragen über mehrere Tabellen. In einem solchen Fall ist eine Aktualisierung einer Sicht in aller Regel nicht oder nur unter sehr eingeschränkten Bedingungen möglich. Sollen Daten zu einer Sicht hinzugefügt also INSERT-Anweisungen ausgeführt werden, dann muss die Sicht alle Spalten der zugrunde liegenden Tabelle(n) zurückliefern, für die kein Standardwert oder keine NULL-Zulässigkeit angegeben wurde. Unsere Sicht Kochen.vLeichteGerichte erfüllt diese Bedingung derzeit nicht, da die Spalte Bewertung in der Ausgabe fehlt. Für diese Spalte, die als NOT NULL deklariert wurde, ist kein Standardwert angegeben worden. Um INSERT-Anweisungen auf diese Sicht anwenden zu können, wollen wir zunächst diese Spalte zur Sicht hinzufügen:
Danach kann über INSERT eine Zeile hinzugefügt werden:
Wenn wir nun die von der Sicht zurückgelieferten Daten anschauen, so ist die gerade über INSERT hinzugefügte Zeile darin nicht enthalten. Natürlich, wir haben ja auch kein leichtes Gericht hinzugefügt, die in der Sicht enthaltene WHERE-Klausel verhindert, dass die hinzugefügte Zeile angezeigt wird. Dieses Verhalten kann wenn man eine Sicht als virtuelle Tabelle ansieht einigermaßen verwirrend sein. Schließlich erwartet man, dass zu einer Tabelle über INSERT hinzugefügte Zeilen über SELECT auch wieder abgefragt werden können. In unserem Fall ist dies nicht so, die neue Zeile kann über eine SELECTAnweisung auf der Sicht nicht abgefragt werden. Es ist jedoch möglich, die Definition einer Sicht so zu gestalten, dass diese Art Anomalie verhindert wird. WITH CHECK OPTION Eine Sicht kann unter Verwendung der Klausel WITH CHECK OPTION deklariert werden. Eine so erstellte Sicht wird auch für Aktualisierungen also INSERT- und UPDATEAnweisungen die in der WHERE-Klausel angegebene Zeilenfilterbedingung überprüfen. Aktualisierungen werden nur durchgeführt, wenn die Zeilenfilterbedingung für die aktualisierten Daten den Wert WAHR ergibt. Dadurch ist sichergestellt, dass nach einer Aktualisierung die geänderten Daten auch über die Sicht abgefragt werden können. Die WITH CHECK OPTION geben Sie bei der Deklaration der Sicht so an:
Wenn nun versucht wird, eine Zeile hinzuzufügen oder zu ändern, welche die WHEREBedingung verletzt, so wird die Aktualisierung nicht durchgeführt. Stattdessen gibt es nur eine Fehlermeldung, die so aussieht:
157
4 Transact SQL-Grundlagen
4.18
Daten einfügen mit SELECT Die SELECT-Anweisung kann auch zum Einfügen von Daten in Tabellen verwendet werden. Hierbei ist es möglich, Daten an eine vorhandene Tabelle anzuhängen oder aber sogar automatisch eine neue Tabelle zu erzeugen.
4.18.1 INSERT und SELECT Die Spaltenwerte einer INSERT-Anweisung können auch durch eine SELECT-Anweisung ermittelt werden. Dadurch besteht die Möglichkeit, mit einer einzigen INSERT-Anweisung, mehrere Zeilen in eine Tabelle einzufügen. Hierzu verwenden Sie INSERT in Kombination mit SELECT in der folgenden allgemeinen Form:
Die Zieltabelle muss bereits existieren. Die Angabe der ist hier optional. Wenn Sie diese Liste weglassen, so müssen Sie für alle in der Zieltabelle existierenden Spalten in der korrekten Reihenfolge Werte in der angeben. Mit korrekter Reihenfolge ist die physikalische Anordnung der Spalten in der Tabelle gemeint. Dies ist die Reihenfolge, in der die Spalten auch im Objekt-Explorer angezeigt werden. Das folgende Skript erzeugt eine Tabelle Kochen.LeichteGerichte und fügt alle Zeilen der Sicht Kochen.vLeichteGerichte in diese Tabelle ein:
Selbstverständlich ist es möglich, für die in der obigen Syntax als bezeichnete Tabelle auch komplexe JOINs oder aber auch Sichten zu verwenden so wie gerade im Beispiel geschehen.
4.18.2 SELECT INTO Über INSERT in Verbindung mit SELECT haben Sie also die Möglichkeit, Zeilen zu bereits existierenden Tabellen hinzuzufügen. Was aber nun, wenn die Zieltabelle noch gar
158
4.19 Null nicht existiert? In diesem Fall müssten Sie die Tabelle zuvor erzeugen, zum Beispiel durch die Anweisung CREATE TABLE. Sie können die SELECT-Anweisung aber auch mit der INTO-Klausel zusammen verwenden, um die Zieltabelle automatisch zu erzeugen. Hierfür sieht die Syntax so aus:
Die Zieltabelle darf in diesem Fall noch nicht existieren, ansonsten wird eine Fehlermeldung erzeugt. Zu beachten ist auch, dass die Struktur der Zieltabelle nicht unbedingt identisch mit der Struktur der Quelltabelle sein wird. Dies gilt insbesondere für die Einschränkungen dieser Tabelle. Das ist sicher leicht einzusehen, da auch hier wieder die Quelltabelle eine komplexe Abfrage und nicht lediglich eine einzelne Tabelle darstellen kann. Außerdem ist es natürlich möglich, in der Spaltenliste Ausdrücke zu verwenden, wodurch Einschränkungen auf Spaltenebene in der Zieltabelle nicht bestimmt werden können oder willkürlich festgelegt werden müssten. Die IDENTITY-Eigenschaft einer Quellspalte wird in die Zieltabelle übernommen, wenn die Spalte in der Spaltenliste direkt (also ohne Verwendung eines Ausdrucks) angegeben wird. Falls Sie Ausdrücke verwenden, um Spaltenwerte zu berechnen, so muss für jede Spalte ein Aliasname angegeben werden, der den Spaltennamen in der Zieltabelle bestimmt. Die folgende Anweisung verwendet die weiter oben eingeführte Tabelle Extras.Kalender. Es wird eine Tabelle J2004 erzeugt, in der alle Zeilen mit dem Wert Jahr=2004 aus der Tabelle Extras.Kalender enthalten sind:
Wenn Sie sich die erstellte Tabelle J2004 einmal ansehen, so werden Sie feststellen, dass dort keine berechneten Spalten vorhanden sind, so wie dies in der Quelltabelle der Fall war. SELECT INTO erzeugt alle Spalten als echte Datenwerte. Die NOT NULLEinschränkung wurde allerdings für alle Spalten übernommen.
4.19
Null Der Wert NULL nimmt im Typsystem von SQL eine Sonderstellung ein und führt regelmäßig zu Verwirrungen. Aus diesem Grund sollen die zu beachtenden Besonderheiten und bei der Verwendung von NULL auftretenden Probleme hier noch einmal explizit herausgestellt werden. Variablen jedes beliebigen SQL-Datentyps können prinzipiell auch leer blieben, also einfach keinen Wert enthalten. In diesem Fall wird der Wert NULL benutzt, der anzeigt, dass für eine entsprechende Variable oder auch Spalte in einer Tabellenzeile kein passender oder bekannter Wert existiert. Probleme bei der Verarbeitung von solchen unbekannten Werten resultieren daraus, dass in SQL dadurch keine binäre, sondern eine dreiwertige Logik implementiert werden muss. Hinzu kommt, dass ein NULL-Wert letztlich aus zwei Gründen existieren kann: Der Wert ist unbekannt oder unpassend. Diese beiden Problemquellen sollen im folgenden Abschnitt etwas genauer untersucht werden.
159
4 Transact SQL-Grundlagen
4.19.1 Dreiwertige Logik Die bekannte boolesche Algebra, die auf einer zweiwertigen Logik basiert, ist leider in SQL nicht gültig. Dadurch, dass der Wert NULL als unbekannt angesehen wird und auch Variablen mit diesem Wert in die Auswertung von logischen Ausdrücken einbezogen werden müssen, muss in SQL mit einer dreiwertigen Logik gearbeitet werden. Das Ergebnis eines logischen Ausdrucks, also z. B. einer Vergleichsoperation, kann in SQL die Werte wahr, falsch oder unbekannt4 annehmen. Tabelle 4.20 und Tabelle 4.21 verdeutlichen dies anhand der Operatoren NOT, OR und AND. Tabelle 4.20 Dreiwertige Logik des NOT-Operators Ausdruck
NOT-Ausdruck
wahr
falsch
falsch
wahr
unbekannt
unbekannt
Tabelle 4.21 Dreiwertige Logik von OR und AND wahr
falsch
unbekannt
OR
wahr
wahr
wahr
AND
wahr
falsch
unbekannt
OR
wahr
falsch
unbekannt
AND
falsch
falsch
falsch
OR
wahr
unbekannt
unbekannt
AND
unbekannt
falsch
unbekannt
wahr
falsch
unbekannt
So ist z. B. das Ergebnis eines Ausdrucks NULL AND wahr unbekannt, da ja für den Fall, dass der NULL-Wert wahr wäre, der Ausdruck ebenfalls wahr werden würde und für den Fall, dass der Wert NULL mit einem Ausdruck ersetzt werden würde, der falsch zurückgibt, der Gesamtausdruck falsch ergeben würde. Aus diesem Grund kann über das Ergebnis des Ausdrucks nichts ausgesagt werden es ist also unbestimmt. Ein großes Problem ergibt sich aus der nicht konsistenten Behandlung von NULL innerhalb der SQL-Sprachelemente. Die Verwendung von unbekannten logischen Resultaten und NULLs kann dadurch wirklich sehr verwirrend werden. In Abfragefiltern, also WHERE-, ON- und HAVING-Klauseln, wird der Wert unbekannt als falsch betrachtet. 4
160
Wenn in diesem Zusammenhang von unbekannt die Rede ist, so bezieht sich dies stets auf den SQL Server. Es ist natürlich möglich, dass ein Wert dem Benutzer sehr wohl bekannt ist, dass dieser Wert aber wegen bestehender Einschränkungen des Wertebereichs nicht in eine Spalte eingetragen werden kann. In diesem Fall würde die Spalte einen NULL-Wert enthalten, obwohl der ursprüngliche Wert durchaus bekannt war. Für die Ausdrucksverarbeitung im SQL Server, also aus Server-Sicht, ist der Wert dann aber im weiteren Verlauf einfach unbekannt.
4.19 Null Dies bedeutet, dass Zeilen, für die eine Filterbedingung den Wert NULL zurückliefert, aus dem Ergebnis ausgeschlossen werden. Schauen Sie sich einmal die folgende Abfrage an:
Diese Abfrage kann leicht so umformuliert werden:
Das WHERE-Prädikat besteht aus drei Vergleichsoperationen, die durch OR miteinander verknüpft sind. Die ersten beiden Vergleiche (1 = 2) und (1 = 3) liefern jeweils den Wert falsch, der letzte Ausdruck (1 = null) den Wert unbekannt. Letztlich bleibt also der Ausdruck falsch OR unbekannt übrig, was nach der dreiwertigen Logik (siehe Tabelle 4.21) wiederum unbekannt ergibt. Da nun in der WHERE-Bedingung unbekannt mit falsch gleichgesetzt wird, gibt die gesamte Abfrage keine Zeilen zurück, obwohl die 1 in der Aufzählung nicht explizit enthalten ist. In diese Falle kann man wirklich leicht hineinlaufen, da das IN-Prädikat auch Unterabfragen zulässt. Betrachten Sie hierzu bitte einmal die folgende Abfrage, die alle Mitarbeiter zurückliefern soll, die noch keine Bestellungen aufgenommen haben:
Die Abfrage gibt keine Zeilen zurück, obgleich eine ganze Reihe von Angestellten existiert, die noch nie eine Bestellungen initiiert haben. Der Grund hierfür ist, dass die Unterabfrage auch eine Reihe von Zeilen liefert, die den Wert NULL für die Spalte enthalten, was dann so wie weiter oben beschrieben dazu führt, dass die gesamte WHERE-Klausel einfach falsch zurückliefert. Auch die Anwendung von Vergleichsoperatoren auf NULL-Werte ergibt stets den Wert unbestimmt. Die folgenden vier Abfragen geben keine Zeilen zurück:
Für alle vier Abfragen liefert der Ausdruck in der WHERE-Klausel den Wert unbestimmt, der wie oben erläutert in dieser Klausel als falsch interpretiert wird. Keine der Abfragen wird also Werte zurückliefern. Führen Sie in Gedanken bitte einmal diese drei Abfragen aus, und überlegen Sie, was Sie als Ergebnis erwarten:
161
4 Transact SQL-Grundlagen
Auf den ersten Blick könnte man denken, dass die Summe der von den ersten beiden Abfragen zurückgelieferten Zeilenanzahl gleich der Gesamtsumme der in der Tabelle enthaltenen Anzahl von Zeilen, die von der dritten Abfrage zurückgegeben wird, entsprechen sollte. Dies ist jedoch nicht der Fall! Die Tabelle enthält eine Reihe von Zeilen, in denen die Spalte Color nicht belegt ist, also NULL enthält. Diese Zeilen werden weder in der ersten noch in der zweiten SELECTAnweisung berücksichtigt, da der Ausdruck genauso unbestimmt ist wie der Ausdruck . Um die Anzahl aller Produkte zu erhalten, für die keine Farbe angegeben wurde, schreibt man:
Ob der Wert einer Variablen oder Spalte NULL ist, muss also stets explizit mit IS NULL abgefragt werden. Genauso können Sie auch IS NOT NULL verwenden, um zu prüfen, ob ein Wert nicht NULL ist:
Im Gegensatz zur WHERE-Klausel wird in einer CHECK-Bedingung der Wert unbekannt, also NULL, als wahr behandelt. Schauen Sie sich die folgende Tabellendefinition an:
Die Tabelle Produkt enthält eine Spalte Masse, in die auch NULL-Werte eingetragen werden dürfen. Allerdings ist auch festgelegt, dass die Spalte nur die Werte Schwer, Mittel und Leicht enthalten darf. Trotz der CHECK-Bedingung darf allerdings der Wert NULL in die Spalte eingetragen werden:
UNIQUE-Einschränkungen behandeln NULL-Werte als gleich. Die folgende Anweisung wird daher eine Fehlermeldung zurückliefern:
Da der Wert NULL bereits in einer anderen Zeile existiert, führt die UNIQUEEinschränkung dazu, dass die INSERT-Anweisung nicht ausgeführt wird. Auch in den Klauseln GROUP BY und ORDER BY werden NULL-Werte als identisch betrachtet. Hier werden jeweils alle Spalten mit NULL-Werten zusammengefasst.
162
4.19 Null
4.19.2 Arithmetische Operationen So wie logische Operatoren unbekannte Ergebnisse liefern können, gilt dies gleichermaßen auch für arithmetische Operationen. Auch hier gibt es wiederum einige Besonderheiten zu beachten. Schauen Sie sich zunächst bitte die folgende Anweisung an, die einige Berechnungen mit NULL-Werten durchführt:
Das Ergebnis der obigen Abfrage sehen Sie hier:
NULL wird also immer im Sinne von unbekannt interpretiert. Aus dieser Sicht ist das Ergebnis also durchaus verständlich wann immer Sie eine Berechnung mit einem unbekannten Ausdruck durchführen, ist das Ergebnis selbst auch wieder unbestimmt. Für die letzten drei Spalten sieht die Sache allerdings ein wenig anders aus. Die Berechnung (null/0) wird immer einen Fehler liefern, sobald Sie die null mit einem beliebigen Ausdruck ersetzen, der nicht NULL zurückliefert. SQL Server gibt allerdings auch hier NULL zurück. Gleiches gilt für den Ausdruck (0*null), der für einen von NULL verschiedenen Ausdruck immer 0 liefern wird. Eine etwas kuriose Besonderheit ist das Ergebnis von (1*null). Hier wird völlig korrekt NULL als Resultat zurückgegeben. Diese NULL ist jedoch nicht das Ergebnis einer wirklichen Berechnung, vielmehr wird sie aus der Unbestimmtheit des Ausdrucks abgeleitet. Bei der Berechnung von Aggregaten werden NULL-Werte nicht berücksichtigt. Schauen Sie sich hierzu bitte die folgende Tabellendefinition an:
Wir wollen nun einige Zeilen zu dieser Tabelle hinzufügen:
Schließlich berechnen wir die Gesamtsumme für die Werte in der Spalte Summe:
Die letzte hinzugefügte Zeile enthält den Wert NULL in der Spalte Summe. Diese Zeile wird einfach aus der Summenberechnung ausgeklammert. Der Wert der Summe ist also nicht NULL, sondern 33.000. Haben Sie die Option SET ANSI_WARNINGS auf ON gesetzt
163
4 Transact SQL-Grundlagen (dies ist der Standard), erhalten Sie eine Warnung, dass NULL-Werte bei der Aggregation ignoriert werden. Ist ANSI_WARNINGS OFF, so wird diese Warnung nicht ausgegeben.
4.19.3 Operationen mit Zeichenketten Überlegen Sie sich nun bitte einmal, was Sie als Ergebnis erwarten, wenn Sie einen Text mit einem NULL-Wert verketten. Interpretieren Sie NULL als nichts, also als leere Zeichenkette, so werden Sie sich als Ergebnis sicherlich den ursprünglichen Text wünschen. Hängt man an einen Text nichts an, so findet eben keine Veränderung statt. Wenn Sie NULL allerdings als unbekannt betrachten, dann würden Sie auch hier ein Unbekannt also eine NULL als Ergebnis erwarten. Es ist nicht so einfach zu entscheiden und dazu oftmals vom speziellen Fall abhängig, welche Variante zu bevorzugen ist. SQL Server hat in der Version 6.5 NULL stets als leere Zeichenkette interpretiert, also den ursprünglichen Text zurückgegeben. Ab der Version 7.0 konnte das gewünschte Verhalten dann über die SET-Option CONCAT_NULL_YIELDS_NULL festgelegt werden. Ist diese Option auf ON gesetzt (das ist die Standardeinstellung), so gibt die Verkettung einer Zeichenkette mit NULL stets NULL zurück, wie das folgende Beispiel demonstriert:
Das Ergebnis der obigen SELECT-Anweisung zeigt die folgende Abbildung:
Stellen Sie CONCAT_NULL_YIELDS_NULL auf OFF, so wird NULL als leerer Text behandelt, das Ergebnis der Verkettung ist dann also die ursprüngliche Zeichenkette:
Hier sehen Sie das Ergebnis der Abfrage:
Die Standardeinstellung für die Option CONCAT_NULL_YIELDS_NULL wird in der Datenbank, also bei CREATE DATABASE oder ALTER DATABASE, festgelegt, z. B. so:
Diese Standardeinstellung wird allerdings auf Verbindungsebene überschrieben, die bei der Verbindung zur Datenbank angegebene Option dominiert also. Wenn Sie im SQL Server Management Studio eine neue Abfrage öffnen, wird die Option CONCAT_NULL_YIELDS_NULL für die Verbindung stets explizit gesetzt, sodass die Einstellung auf Datenbankebene für im Management Studio ausgeführte Abfragen keine Auswirkung hat. Denken Sie bitte unbedingt daran, wenn Sie die Einstellung verändern und unerwartete Ergebnisse erhalten.
164
4.19 Null Die in der Datenbank eingestellte Option kann über die Spalte der Systemsicht abgefragt werden:
Für die Optionen der Verbindung verwenden Sie das Kommando:
Sie erhalten ein Ergebnis, das in etwa so aussieht:
Das Abrufen und Einstellen der Verbindungsoptionen können Sie auch über das Menü Abfrage/Abfrageoptionen
vornehmen:
165
4 Transact SQL-Grundlagen Wenn Sie indizierte Sichten oder Indizes auf berechneten Spalten erstellen oder ändern, muss CONCAT_NULL_YIELDS_NULL auf ON festgelegt sein. Bitte beachten Sie, dass diese Option für die Verbindung eingeschaltet sein muss, die ein CREATE INDEX oder ALTER INDEX ausführt.
4.19.4 Hinweise für die Verwendung von NULL Aus all dem in den vorangegangenen Abschnitten Gesagten, aus den möglichen Problemen, die bei der Verwendung von NULL auftreten können, aus der Sonderstellung, die der Wert NULL innerhalb des SQL Server-Typsystems einnimmt, lässt sich eine sehr einfache Regel ableiten: Vermeiden Sie die Verwendung von NULL, wann immer dies möglich ist. In vielen Fällen können Sie durch eine Belegung mit Standardwerten, die außerhalb des gültigen Wertebereichs liegen, aber doch noch dem Datentyp einer Spalte entsprechen, NULL-Werte vermeiden. Nehmen Sie z. B. eine Spalte Gehalt in einer Mitarbeitertabelle, hier kann wenn das Gehalt noch nicht bekannt ist statt NULL auch der Wert 0 eingetragen werden. Da es sicherlich keine Mitarbeiter mit einem Gehalt von 0 geben wird, ist die 0 hier ebenso geeignet, einen fehlenden Wert anzuzeigen. In einigen Fällen wird es allerdings schwierig oder unmöglich sein, auf NULL zu verzichten. Haben Sie in der Mitarbeitertabelle auch ein Datum, das die Beendigung des Arbeitsverhältnisses anzeigen soll, so kann der Wert NULL hier dafür stehen, dass der Mitarbeiter noch nicht aus der Firma ausgeschieden ist. Hier ein Standarddatum (etwa den 01.01.1900) zu erfinden, das für Mitarbeiter im aktiven Angestelltenverhältnis steht, wird sicherlich für Verwirrung sorgen. Der Einsatz von NULL wird sich also nicht vermeiden lassen, versuchen Sie aber, ihn so weit wie möglich einzuschränken. Eine der Hauptursachen für die Schwierigkeiten mit NULL ist sicherlich, dass der Wert NULL allein keinerlei Information darüber liefert, warum er für einen Spaltenwert eingetragen wurde. Der Erfinder des Relationenmodells, Dr. E. F. Codd, hatte seinerzeit dafür plädiert, für fehlende Werte zwei unterschiedliche Markierungen einzuführen: eine für unbekannt und eine andere für unpassend. Nehmen Sie z. B. eine Spalte, in welche die Farbe eines Automobils eingetragen werden soll. Diese Spalte möge über eine UNIQUEEinschränkung nur die Werte Rot, Grün und Blau zulassen. Sollen Sie nun die Farbe für ein Fahrzeug eintragen, das unter einer Abdeckplane steht, so ist Ihnen diese Farbe unbekannt. Ist ein Auto gelb, so ist die Farbe zwar bekannt, kann aber ebenso nicht in die Spalte eingetragen werden. In beiden Fällen wird die Spalte für die Farbe den Wert NULL enthalten und somit lediglich anzeigen, dass der Wert nicht eingetragen werden konnte, aber nicht, warum dies so ist. Diese Information geht einfach verloren, wenn Sie sie nicht separat in der Tabelle speichern. Und dies ist auch schon der nächste Tipp: Sie können eine NULL-wertige Spalte paarweise mit einer Informationsspalte verwenden, in die Sie den Grund für die NULL eintragen. In unserer gerade besprochenen Tabelle mit der Spalte über die Fahrzeugfarbe könnte dies so aussehen:
166
4.19 Null
Die Tabelle Fahrzeug hat neben der Spalte Farbe auch eine Spalte FarbInfo, in der nähere Informationen zum in der Spalte Farbe enthaltenen Wert stehen. Ist der Wert der Spalte Farbe NULL, so kann aus der FarbInfo-Spalte abgelesen werden, warum dies so ist.
4.19.5 Spezielle Funktionen für NULL-Werte SQL Server stellt zwei Funktionen bereit, die NULL-Werte in deterministische Werte überführen können. COALESCE Syntax.
Erklärung. Die Funktion COALESCE kann eine beliebige Anzahl von Parametern verarbeiten. Die übergebenen Parameter sind typischerweise Ausdrücke, die der Reihe nach ausgewertet werden. Der erste Ausdruck, der einen Wert ungleich NULL zurückliefert, bestimmt das Ergebnis der Funktion. Geben alle Ausdrücke NULL zurück, so ist das Ergebnis der Funktion NULL. Es müssen mindestens zwei Parameter übergeben werden. Außerdem sollten Sie darauf achten, dass alle Ausdrücke einen Wert vom selben Datentyp zurückliefern oder zumindest eine implizite Typumwandlung unterstützen. Die Funktion COALESCE ist äquivalent mit der folgenden CASE-Funktion:
Beispiele.
ISNULL Syntax.
167
4 Transact SQL-Grundlagen Erklärung. Die Funktion ISNULL wertet den übergebenen ausdruck_1 aus. Ist der Wert NULL, so wird der durch ausdruck_2 bestimmte Wert zurückgegeben. Wenn der Wert von ausdruck_1 ungleich NULL ist, so wird dieser Wert zurückgegeben. ISNULL ist somit gleichbedeutend mit dem folgenden Aufruf von COALESCE:
Beispiele.
Unsere in Abschnitt 4.15.7 vorgestellte Tabelle Extras.Kalender besitzt bislang noch keinen Primärschlüssel. Ein solcher Primärschlüssel sollte sicherstellen, dass ein bestimmter Datumswert nur einmal in der Tabelle existieren kann. Für den Primärschlüssel ist die Spalte Datum somit kein geeigneter Kandidat, da in dieser Spalte auch eine Zeitkomponente enthalten ist. Die folgenden beiden Werte für diese Spalte sind daher unterschiedlich, obwohl sie dasselbe Datum enthalten:
Ein Primärschlüssel auf der Spalte Datum würde somit mehrfache Einträge für ein Kalenderdatum zulassen und ist daher ungeeignet für unsere Zwecke. Ein geeigneter Primärschlüssel wäre die Kombination aus den berechneten Spalten Jahr, Monat und Tag. Versuchen wir also, einen solchen Primärschlüssel zur Tabelle hinzuzufügen:
Leider erzeugt die obige ALTER TABLE-Anweisung aber keinen Primärschlüssel, sondern nur die folgende Fehlermeldung:
Ursache für den Fehler ist, dass bei einer berechneten Spalte davon ausgegangen wird, dass der Ausdruck zur Berechnung des Spaltenwertes prinzipiell auch den Wert NULL zurückgeben kann. Und Spalten, die NULL-Werte erlauben, sind nun einmal in Primärschlüsseln nicht erlaubt. Hier hilft ein kleiner Trick weiter. Die Berechnungsausdrücke für die Spalten Jahr, Monat und Tag können unter Verwendung von ISNULL folgendermaßen geändert werden, um explizit anzuzeigen, dass diese Spalten keine NULL-Werte enthalten:
168
4.20 Fehlerbehandlung in T-SQL
Da ein Berechnungsausdruck nicht über ALTER COLUMN geändert werden kann, müssen die drei Spalten über DROP COLUMN entfernt und dann über die Klausel ADD wieder hinzugefügt werden. Durch die Verwendung von ISNULL im Berechnungsausdruck erkennt SQL Server, dass die entsprechenden Spalten niemals den Wert NULL enthalten werden. Das Anlegen eines Primärschlüssels für diese drei Spalten ist nun möglich:
4.20
Fehlerbehandlung in T-SQL Bereits im Abschnitt 0 wurde die @@ERROR-Funktion besprochen, die zum Behandeln aufgetretener Fehler verwendet werden kann. T-SQL bietet auch die Möglichkeit einer strukturierten Ausnahmebehandlung, ähnlich wie dies von anderen Programmiersprachen (zum Beispiel C++ oder C#) bekannt ist. Hierzu kann eine Gruppe von T-SQLAnweisungen in einen BEGIN TRY-/END TRY-Block eingeschlossen werden. Auf einen solchen Block muss unmittelbar ein BEGIN CATCH-/END CATCH-Block folgen. In diesen zweiten Block wird immer dann verzweigt, wenn in irgendeiner der Anweisungen zwischen BEGIN TRY und END TRY ein Fehler auftritt. Dadurch muss nicht nach jeder Anweisung der Wert der Funktion @@ERROR ausgewertet werden. Ein TRY/CATCHBlock kann zum Beispiel so aussehen:
Das obige Beispiel zeigt auch die Benutzung der zur Verfügung stehenden ERROR_...()Funktionen, über die Sie nähere Informationen zum aufgetretenen Fehler erhalten können.
169
4 Transact SQL-Grundlagen
4.21
Komplexe Abfragen mit SELECT In diesem Abschnitt werden wir uns noch einmal mit der SELECT-Anweisung beschäftigen und komplexere Abfragen entwerfen sowie weitere SQL Server-Funktionen für die Verwendung mit SELECT kennenlernen. Im Mittelpunkt werden hierbei Abfragen über mehrere Tabellen stehen. Für die verwendeten Beispiele ist unsere einfache Datenbank OmasKochbuch dabei oftmals nicht mehr ausreichend. In vielen Fällen wird daher auf die Beispieldatenbank AdventureWorks zurückgegriffen. Wir verwenden hierbei die in Tabelle 4.19 bzw. Abbildung 4.20 aufgelisteten Tabellen und Sichten.
4.21.1 Mengenoperationen mit SQL Am Anfang dieses Kapitels wurde bereits erwähnt, dass SQL eine mengenorientierte Sprache ist. Genau genommen geben SELECT-Anweisungen also keine Tabellen, sondern Ergebnismengen zurück. Mit diesen Ergebnismengen können auch Operationen durchgeführt werden. So ist es zum Beispiel möglich, die Ergebnismengen unterschiedlicher SELECTAnweisungen zu vereinen oder voneinander abzuziehen. T-SQL kennt hierfür Operatoren, die auf komplette SELECT-Anweisungen angewendet werden können. Die generelle Syntax einer entsprechenden Mengenoperation sieht so aus:
Die an einer solchen Operation beteiligten SELECT-Anweisungen müssen alle dieselbe Anzahl von Spalten zurückliefern, wobei weiterhin gilt, dass die Spalten an derselben Position in den einzelnen SELECT-Anweisungen kompatible Datentypen aufweisen müssen. Um Ergebnismengen unterschiedlicher SELECT-Anweisungen miteinander zu verknüpfen, stehen Ihnen die folgenden Mengenoperatoren zur Verfügung: UNION Der Operator UNION bildet die Vereinigungsmenge der beiden Ergebnisse. Das Endergebnis enthält also alle Zeilen, die entweder von der ersten oder von der zweiten SELECTAnweisung zurückgegeben wurden. Allerdings werden doppelte Zeilen, also Duplikate, aus dem Ergebnis entfernt. UNION ALL Dieser Operator arbeitet genauso wie UNION, bildet also die Vereinigungsmenge. Duplikate bleiben allerdings erhalten, das Endergebnis kann also mehrere Zeilen mit denselben Daten enthalten.
170
4.21 Komplexe Abfragen mit SELECT INTERSECT Der INTERSECT-Operator bildet die Schnittmenge der beiden Ergebnisse. Im Endergebnis sind also alle Zeilen enthalten, die sowohl von der ersten als auch von der zweiten SELECT-Anweisung zurückgegeben wurden. Duplikate werden hierbei aus dem Ergebnis entfernt. EXCEPT Bei Verwendung des Operators EXCEPT erhalten Sie die Differenzmenge. Im Ergebnis sind also genau diejenigen Zeilen enthalten, die von der ersten, aber nicht von der zweiten SELECT-Anweisung zurückgegeben wurden. Es soll nun an einem einfachen Beispiel erläutert werden, wie die Mengenoperatoren arbeiten. Angenommen, wir haben zwei Tabellen T1 und T2, die so erzeugt werden:
In die einzige Spalte dieser Tabelle werden dann über eine Reihe von INSERT-Anweisungen jeweils fünf Zeilen mit den folgenden Werte für die Spalte x eingefügt: T1: (1, 2, 3, 4, 5) T2: (1, 2, 3, 8, 9) Tabelle 4.22 veranschaulicht die Ergebnisse der unterschiedlichen Mengenoperationen. Tabelle 4.22 Veranschaulichung der T-SQL-Mengenoperatoren Anweisung
Ergebnis
1, 2, 3, 4, 5, 8, 9
Veranschaulichung
1, 2, 3, 4, 5, 1, 2, 3, 8, 9
1, 2, 3
4, 5
171
4 Transact SQL-Grundlagen
4.21.2 Abfragen über mehrere Tabellen In den SELECT-Anweisungen, die Sie bislang kennengelernt haben, wurde stets nur eine einzige Tabelle verwendet. In einer relationalen Datenbank haben wir es jedoch mit Tabellen zu tun, die zueinander in Beziehung stehen. Schauen Sie sich nur einmal unsere einfache Beispieldatenbank OmasKochbuch in Abbildung 4.20 an. Die beiden einzigen enthaltenen Tabellen stehen hier ebenfalls zueinander in Beziehung. Angenommen, Sie möchten die Bezeichnung Ihrer Rezepte mit allen dazugehörigen Zutaten ausgeben. Um diese Informationen zu erhalten, müssen beide Tabellen abgefragt werden. Natürlich kann dies in zwei separaten SELECT-Anweisungen geschehen:
Um nun alle Zutaten für die einzelnen Rezepte zu erhalten, können Sie jeweils eine zweite SELECT-Anweisung angeben, welche die Zutaten für ein bestimmtes Rezept zurückgibt:
Eine solche Verfahrensweise ist natürlich umständlich und auch unpraktikabel. Viel besser wäre es doch, beide Tabellen zu verknüpfen und in einem Ergebnis zurückzugeben. Abfragen dieser Art, in denen also mehrere Tabellen verwendet werden, sollen in diesem Abschnitt entworfen werden. SQL kennt hierzu eine Reihe von JOIN-Operatoren, mit denen Sie Tabellen in Abfragen auf unterschiedliche Art verknüpfen können. Die allgemeine Syntax bei der Verwendung von JOIN in Abfragen ist hierbei wie folgt:
Der erste Teil der obigen SELECT-Anweisung sieht zunächst sehr bekannt aus. Er besteht aus der Spaltenliste und der ersten Tabelle für die Abfrage. Über unterschiedliche JOINKlauseln können dann zu dieser ersten Tabelle weitere Tabellen hinzugefügt werden, wobei in den einzelnen Verknüpfungsbedingungen jeweils angegeben wird, wie die an der Abfrage beteiligten Tabellen zueinander in Beziehung stehen. Diese Verknüpfungsbedingungen werden in der Regel Spalten der beteiligten Tabellen zueinander in Beziehung setzen, die durch Fremdschlüsselbeziehungen bestimmt werden. Die Angabe dieser Bedingungen ist optional, wir werden gleich sehen, was passiert, wenn Bedingungen weggelassen werden. In der Spaltenliste der SELECT-Anweisung haben Sie Zugriff auf die Spalten aller an der Abfrage beteiligten Tabellen. T-SQL kennt unterschiedliche JOINs, die im folgenden Teil erklärt und anhand einiger Beispiele näher untersucht werden.
172
4.21 Komplexe Abfragen mit SELECT CROSS JOIN In der Klausel CROSS JOIN kann keine Verknüpfungsbedingung angegeben werden. Ein CROSS JOIN liefert das kartesische Produkt der beiden beteiligten Tabellen. Die folgende Anweisung gibt alle Spalten der Tabellen Kochen.Rezept und Kochen.Zutat zurück, wobei für jede Zeile der Tabelle Kochen.Rezept jeweils alle Zeilen der Tabelle Kochen.Zutat ausgegeben werden:
Abbildung 4.24 zeigt einen Ausschnitt aus dem Ergebnis.
Abbildung 4.24 Ergebnis des CROSS JOIN
Angenommen, die Tabelle Kochen.Rezept enthält n Zeilen, die Tabelle Kochen.Zutat möge m Zeilen enthalten. Das Ergebnis enthält dann n * m Zeilen! Seien Sie also vorsichtig mit CROSS JOINs, bereits zwei kleine Tabellen mit jeweils nur 1000 Zeilen führen zu einem Ergebnis von 1000.000 Zeilen! Ein CROSS JOIN wird relativ selten benötigt, wichtiger sind auf jeden Fall die im folgenden Abschnitt beschriebenen INNER JOINs. INNER JOIN Ein INNER JOIN verknüpft zwei Tabellen anhand einer Bedingung. Im Ergebnis sind nur diejenigen Zeilen enthalten, für welche die Auswertung dieser Bedingung den Wert WAHR ergibt. Typischerweise wird die Bedingung einfach Primär- bzw. Fremdschlüsselspalten der beteiligten Tabellen auf Gleichheit prüfen. Um zum Beispiel die Bezeichnung aller Rezepte und die dazugehörigen Zutaten auszugeben, kann der folgende INNER JOIN verwendet werden:
173
4 Transact SQL-Grundlagen Die Tabellen Kochen.Rezept und Kochen.Zutat werden hier durch die Fremdschlüsselbeziehung über die gemeinsame Spalte RezeptId miteinander verbunden. Aus der Menge der möglichen Spalten werden dann nur die in beiden Tabellen vorhandenen Bezeichnungen für Rezept und Zutat sowie die Menge mit der zugehörigen Einheit aus der Tabelle Kochen.Zutat ausgegeben. Die Abfrage verwendet Aliasnamen für beide Tabellen, um etwas Schreibarbeit zu sparen. Das Ergebnis der Abfrage sehen Sie in Abbildung 4.25.
Abbildung 4.25 Ergebnis der INNER JOINs
Ein INNER JOIN kann auch über eine Zeilenfilterbedingung in der WHERE-Klausel erstellt werden. Hierzu werden einfach die beteiligten Tabellen in der FROM-Klausel aufgelistet, die WHERE-Klausel enthält dann die Verknüpfungsbedingung. Die obige Abfrage hätte auch so formuliert werden können:
An einem JOIN können grundsätzlich nur zwei Tabellen beteiligt sein. Es ist jedoch möglich, mehr als zwei Tabellen in einer Abfrage zu verwenden. In so einem Fall müssen die beteiligten Tabellen durch mehrere JOINs paarweise miteinander verkettet werden. Das klingt zunächst einmal komplizierter, als es tatsächlich ist. Schauen Sie sich hierzu bitte noch einmal Tabelle 4.19 und Abbildung 4.23 an. Dort finden Sie die Tabellen Production.Product, Production.ProductSubCategory und Production.ProductCategory, die miteinander verbunden sind. Um die Namen aller Produktkategorien, Produktunterkategorien und Produkte in einer Abfrage zurückzugeben, müssen alle drei Tabellen miteinander verbunden werden. Die entsprechende Abfrage sieht so aus:
174
4.21 Komplexe Abfragen mit SELECT
Die Abfrage zeigt so ganz nebenbei übrigens auch, dass in der ORDER BY-Klausel Spaltennummern anstelle der Spaltennamen angegeben werden können. Abbildung 4.26 zeigt das Abfrageergebnis.
Abbildung 4.26 INNER JOIN über drei Tabellen
OUTER JOINs Vielleicht ist Ihnen bei der Abfrage unserer Kochbuch-Tabellen aufgefallen, dass Rezepte ohne zugehörige Zutaten beim INNER JOIN nicht angezeigt wurden. Im Ergebnis waren nur die Zeilen enthalten, welche die Verknüpfungsbedingung erfüllen. Wenn für eine Zeile in der Tabelle Kochen.Rezept keine Zeilen in Kochen.Zutat enthalten sind, so kann die Verknüpfungsbedingung niemals WAHR sein, daher wird auch die in Kochen.Zutat existierende Zeile nicht angezeigt. Möchten Sie aus der Tabelle Kochen.Rezept immer alle Zeilen anzeigen, und zwar unabhängig davon, ob bereits Zutaten für ein Rezept hinzugefügt wurden, so muss eine andere Verknüpfungsform, ein sogenannter OUTER JOIN, verwendet werden. Bei OUTER JOINs unterscheidet man zwischen drei verschiedenen Formen. Die Angabe der Option OUTER ist hierbei optional. LEFT [OUTER] JOIN. Aus der linken Tabelle der Verknüpfung werden immer alle Zeilen angezeigt. Existieren in der rechten Tabelle der Verknüpfung keine Zeilen für die Verknüpfungsbedingung, wird für die Spalten dieser Tabelle ein NULL-Wert ausgegeben. Die folgende SELECT-Anweisung verwendet einen LEFT OUTER JOIN für die Verknüpfung der Tabellen Kochen.Rezept und Kochen.Zutat:
175
4 Transact SQL-Grundlagen
Abbildung 4.27 zeigt das Ergebnis dieser Abfrage. Dort sind nun alle Zeilen aus der Tabelle Kochen.Rezept enthalten. Wenn keine Zutaten für ein Rezept existieren, dann sind die aus der Tabelle Kochen.Zutat angezeigten Spalten NULL.
Abbildung 4.27 Ergebnis des LEFT OUTER JOIN
RIGHT [OUTER] JOIN. Bei einem RIGHT OUTER JOIN enthält das Ergebnis alle Zeilen der rechten Tabelle. Für Spalten der linken Tabelle wird NULL ausgegeben, wenn keine Zeilen existieren. Ein RIGHT OUTER JOIN ist also zuzusagen die Umkehrung des LEFT OUTER JOINs. FULL [OUTER] JOIN. Ein FULL OUTER JOIN ist eine Kombination von LEFT und RIGHT JOIN. Hier werden stets alle Zeilen der beteiligten Tabellen ausgegeben. Im Ergebnis werden die Spaltenwerte wahlweise links oder rechts mit NULL aufgefüllt, je nachdem, ob verknüpfte Zeilen vorhanden sind oder nicht. Ausführungsreihenfolge bei JOINs Prägen Sie sich bitte ein, dass ein JOIN stets vor der Auswertung der WHERE-Klausel ausgeführt wird. Es ist sehr wichtig, dass Sie dies verinnerlichen, anderenfalls werden Sie bei der Ausführung Ihrer Abfragen insbesondere mit OUTER JOINs einige Überraschungen erleben. Schauen Sie sich die beiden folgenden Abfragen an, die dasselbe Ergebnis zurückgeben:
176
4.21 Komplexe Abfragen mit SELECT
In der ersten Anweisung gibt der INNER JOIN nur alle Zeilen zurück, welche die Verknüpfungsbedingung erfüllen. Das Ergebnis enthält also die Zeilen, in denen der Wert der Spalte RezeptId in beiden Tabellen identisch und der Wert der Spalte Kochen.Rezept.Joule kleiner als 1400 ist. In der zweiten Anweisung werden durch den JOIN zunächst alle Zeilen zurückgegeben, in denen die RezeptId übereinstimmt. Dieses Ergebnis wird anschließend durch den in der WHERE-Klausel angegebenen Zeilenfilter nochmals eingeschränkt. Das Ergebnis ist in beiden Fällen dasselbe. Bei einem INNER JOIN ist es egal, ob in der JOIN-Bedingung oder in der WHERE-Klausel gefiltert wird. Für OUTER JOINs sieht die Sache ein wenig anders aus. Wir wollen hierzu ebenfalls zwei Abfragen betrachten, welche die zusätzliche Filterbedingung im JOIN bzw. in der WHERE-Klausel enthalten. Schauen Sie sich bitte die erste dieser beiden Abfragen an:
In einem LEFT JOIN sind immer alle Zeilen der linken Tabelle enthalten. Die zusätzliche Bedingung sorgt hier nur dafür, dass zusätzliche Zeilen mit NULLWerten für die Spalten der Tabelle Kochen.Zutat existieren. Dies sind genau diejenigen Zeilen, für welche die JOIN-Bedingung nicht erfüllt ist. Das Ergebnis der Abfrage zeigt Abbildung 4.28.
Abbildung 4.28 LEFT JOIN mit Filterbedingung
177
4 Transact SQL-Grundlagen Wie Sie in der Abbildung sehen, sind auch Zeilen mit einem Joule-Wert >= 1400 im Ergebnis enthalten, die Spalten der rechten Tabelle der Abfrage sind für solche Zeilen allesamt NULL. Verschiebt man die Filterbedingung aus der ON-Klausel des JOINs in die WHEREKlausel, sieht die Abfrage so aus:
Durch den LEFT JOIN werden zunächst wiederum alle Zeilen der Tabelle Kochen.Rezept zurückgegeben. Erst danach werden diese Zeilen durch die WHERE-Klausel nochmals gefiltert. Heraus kommt das in Abbildung 4.29 gezeigte Ergebnis.
Abbildung 4.29 LEFT JOIN mit WHERE-Klausel
In der WHERE-Klausel kann also auf alle Zeilen zugegriffen werden, die der OUTER JOIN liefert. Die Spaltenwerte für Zeilen ohne Übereinstimmung mit der Verknüpfungsbedingung sind allesamt NULL für die OUTER-Tabelle. Um eine Liste aller Angestellten zu erhalten, die noch keine Bestellung aufgenommen haben, kann also diese Abfrage verwendet werden:
Die WHERE-Klausel bewirkt hier, dass nur diejenigen Zeilen ausgegeben werden, für welche die Verknüpfungsbedingung gerade nicht erfüllt ist. Bei OUTER JOINS ist es also nicht egal, ob Sie eine Einschränkung des Ergebnisses in der JOIN-Klausel oder der WHERE-Klausel vornehmen. Bitte erinnern Sie sich hieran, wenn Ihre OUTER JOINs ungewollte Ergebnisse liefern.
178
4.21 Komplexe Abfragen mit SELECT
4.21.3 Aggregatfunktionen und GROUP BY SQL Server-Aggregatfunktionen ermöglichen eine Berechnung von zusammengefassten Werten aus einzelnen Spaltenwerten. Schauen Sie sich bitte noch einmal die Tabelle Sales.SalesOrderHeader aus Abbildung 4.23 an. Diese Tabelle enthält eine Spalte TotalDue, in welcher der Betrag einer Bestellung gespeichert wird. Angenommen, Sie möchten nun die Summe aller Bestellwerte abfragen, dann können Sie hierfür die Aggregatfunktion SUM in der folgenden Art und Weise verwenden:
Das Ergebnis ist eine einzige Zeile (und auch nur eine Spalte):
Neben der Aggregatfunktion SUM, die Sie soeben kennengelernt haben, unterstützt SQL Server unter anderem die in der folgenden Tabelle 4.23 genannten Aggregatfunktionen: Tabelle 4.23 SQL Server: Aggregatfunktionen Aggregatfunktion
Erklärung
Berechnet den arithmetischen Mittelwert aller ausdrucks-Auswertungen.
Zählt die Anzahl von Zeilen. COUNT(*) zählt alle Zeilen. COUNT([ALL] ausdruck) zählt alle Werte, für welche die Auswertung von ausdruck nicht NULL ergibt. COUNT(DISTINCT ausdruck) zählt alle eindeutigen Werte, für welche die Auswertung von ausdruck nicht NULL ergibt.
Gibt den größten Wert von ausdruck zurück.
Gibt den kleinsten Wert von ausdruck zurück.
Gibt die Summe aller Auswertungen von ausdruck zurück.
Gibt die statistische Standardabweichung aller Auswertungen von ausdruck zurück.
Gibt die statistische Varianz aller Auswertungen von ausdruck zurück.
Alle in der obigen Tabelle genannten Aggregatfunktionen ignorieren bei der Berechnung NULL-Werte, wenn dies nicht anders angegeben ist.
179
4 Transact SQL-Grundlagen Um aus der Tabelle Sales.SalesOrderHeader den maximalen Bestellwert abzufragen, können wir also schreiben:
Sollen für diese eine Bestellung die interessierenden Daten zurückgegeben werden, so kann die Aggregatfunktion auch in der Zeilenfilterbedingung verwendet werden:
Zum Abschluss soll nun auch noch der für diese Bestellung verantwortliche Vertriebsmitarbeiter ausgegeben werden. Hierzu muss die Sicht Sales.vSalesPerson zur obigen Abfrage hinzugefügt werden:
Wir verwenden hier einen LEFT JOIN, da es auch Bestellungen ohne Vertriebsmitarbeiter geben kann. Das Ergebnis der Abfrage sehen Sie hier:
Die folgende SELECTAnweisung zählt alle Zeilen in der Sicht Sales.vSalesPerson:
Um zu ermitteln, aus wie viel verschiedenen Jahren die Bestellungen datieren, können wir DISTINCT in Verbindung mit COUNT verwenden:
Die COUNT-Funktion zählt hier alle unterschiedlichen Werte von . Das Ergebnis ist 4. Gruppieren mit GROUP BY Mit Aggregatfunktionen können nicht nur Gesamtwerte für eine Tabelle berechnet werden. Es ist auch möglich, Berechnungen für Teilbereiche durchzuführen. Interessant wäre es zum Beispiel, Bestellsummen je Kalenderjahr zu bestimmen. Eine erste Idee für eine entsprechende Abfrage könnte so aussehen:
180
4.21 Komplexe Abfragen mit SELECT Allerdings liefert die obige Abfrage lediglich diese Fehlermeldung:
SQL Server erwartet die explizite Angebe der Gruppen, für welche die Berechnung durchgeführt werden soll, über eine GROUP BY-Klausel in der Abfrage. Diese Klausel muss exakt die für die Ausgabe der Gruppen verwendeten Spaltenausdrücke enthalten. Um also eine Gruppierung der Berechnung nach year(OrderDate) zu erhalten, muss die Abfrage so gestaltet werden:
Jetzt werden die Gruppensummen je Kalenderjahr berechnet und das Endergebnis noch ansteigend nach der Spalte Jahr sortiert. Die Abfrage liefert das folgende Ergebnis:
Selbstverständlich kann auch eine Gruppierung über mehrere Spalten erfolgen. Die folgende Abfrage fügt zur obigen Ausgabe eine weitere Spalte für den Kalendermonat hinzu:
Hier ist ein Auszug aus dem Ergebnis:
Filtern mit HAVING Angenommen, es sollen alle Vertriebsmitarbeiter mit der Anzahl ihrer Bestellungen ausgegeben werden. Sie möchten jedoch nur die Vertriebsmitarbeiter im Abfrageergebnis sehen, die mehr als 400 Bestellungen initiiert haben. Eine entsprechende Abfrage könnte vielleicht so aussehen:
181
4 Transact SQL-Grundlagen
Die Abfrage verwendet einen RIGHT OUTER JOIN, um auch Bestellungen ohne verantwortlichen Vertriebsmitarbeiter zu erhalten. Die obige Abfrage funktioniert nicht, sondern liefert lediglich diese Fehlermeldung:
Aggregatfunktionen dürfen nur in besonderen Fällen in der WHERE-Klausel verwendet werden. Ursache ist, dass die WHERE-Klausel in der logischen Ausführungsreihenfolge vor dem GROUP BY liegt, daher kann in der WHERE-Klausel noch nicht auf berechnete Aggregate zugegriffen werden. Für Zeilenfilterbedingungen mit Aggregatfunktionen müssen Sie die HAVING-Klausel nach der Angabe von GROUP BY verwenden. Die obige Abfrage sieht damit so aus:
Jetzt erhalten wir das gewünschte Resultat:
In der ersten Zeile des Ergebnisses sehen Sie, dass der überwiegende Anteil der Bestellungen ohne das Zutun eines Vertriebsmitarbeiters aufgenommen wurde. Im Übrigen geht die Abfrage davon aus, dass für Vertriebsmitarbeiter keine doppelten Nachnamen existieren. Falls dies der Fall ist, werden diese Mitarbeiter in einer Zeile der Ausgabe zusammengefasst, was sicherlich nicht korrekt wäre. Berechnung von Endsummen mit COMPUTE Über die COMPUTE-Klausel können Summenberechnungen für Abfragen durchgeführt werden. Das Ergebnis dieser Summenberechnung wird in einem separaten Abfrageergeb-
182
4.21 Komplexe Abfragen mit SELECT nis ausgegeben. Dies ist etwas ungewöhnlich, da die Verwendung der COMPUTE-Klausel dazu führt, dass eine Abfrage mehrere Ergebnisse hervorbringt. Um in eine Abfrage aller Bestellungen aus dem Jahr 2002 die Gesamtsumme des Bestellwertes einzubeziehen, kann die folgende Abfrage verwendet werden:
Das Ergebnis der COMPUTE-Klausel wird in einem eigenen Abfrageergebnis zurückgegeben (Abbildung 4.30).
Abbildung 4.30 Ergebnis einer Abfrage mit COMPUTE
Zur Berechnung von Zwischenergebnissen kann COMPUTE auch mit einer BY-Klausel verwendet werden. In dieser Klausel wird eine Gruppierung angegeben, für welche die Berechnung von Zwischensummen durchgeführt werden soll. Voraussetzung für die Verwendung der Zwischensummenberechnung über BY ist, dass das Abfrageergebnis nach den in der BY-Klausel angegebenen Spalten (es können mehrere sein, die durch Kommata getrennt sind) sortiert ist. Die folgende Abfrage gibt wiederum die Summe aller Bestellungen je Monat und Jahr aus:
Durch die Angabe der COMPUTE BY-Klausel werden mehrere Ergebnisse zurückgegeben. Für jedes Kalenderjahr wird hierbei ein eigenes Abfrageergebnis erzeugt. Auch die beiden über COMPUTE berechneten Werte für Gesamtsumme und Mittelwert der Bestel-
183
4 Transact SQL-Grundlagen lungen werden in einem separaten Abfrageergebnis ausgegeben. Abbildung 4.31 zeigt einen Auszug aus dem Ergebnis.
Abbildung 4.31 Abfrageergebnis mit COMPUTE BY
COMPUTE BY kann auch zusammen mit COMPUTE verwendet werden. So könnte zu obigen Abfrage noch eine COMPUTE-Klausel hinzugefügt werden, um auch die Gesamtsumme zu erhalten:
ROLLUP Bei Verwendung des ROLLUP-Operators in einer Gruppierung werden automatisch Zwischensummen für die an der Gruppierung beteiligten Spalten berechnet und ausgegeben, wobei die Hierarchie der Gruppierung beachtet wird. Dies lässt sich am besten an einem Beispiel verdeutlichen. Hierzu nehmen wir nochmals die Abfrage zur Berechnung der Bestellwerte je Jahr und Monat, fügen dieser jedoch eine Klausel WITH ROLLUP hinzu:
184
4.21 Komplexe Abfragen mit SELECT
Das Ergebnis enthält nun Zwischensummen für jedes Jahr und auch eine Gesamtsumme. Abbildung 4.32 zeigt einen Ausschnitt aus diesem Ergebnis.
Abbildung 4.32 Ergebnis der Abfrage mit ROLLUP
Die Spalten mit dem Wert NULL für den Monat enthalten jeweils die Zwischensumme für die Gruppe . In der ersten Zeile hier ist sowohl der Wert für die Spalte Jahr als auch der Wert für die Spalte Monal NULL wird die Gesamtsumme ausgegeben. CUBE Der CUBE-Operator arbeitet ähnlich wie ROLLUP. CUBE bewirkt ebenfalls die Ausgabe von Zwischensummen, nur werden diese Summen für alle Kombinationen der Werte der an der Gruppierung beteiligten Spalten erzeugt. Auch dies soll an einem Beispiel verdeutlicht werden. Hierzu verwenden wir noch einmal die bei ROLLUP entworfene Abfrage, ersetzen jedoch die Klausel WITH ROLLUP einfach durch WITH CUBE:
Es werden nun auch für jeden Monat über alle Jahre Zwischensummen ausgegeben, wie in Abbildung 4.33 zu sehen ist.
185
4 Transact SQL-Grundlagen
Abbildung 4.33 Ergebnis der Abfrage mit CUBE
Der Wert NULL in einer Spalte zeigt wiederum an, dass es sich bei dem in der Spalte Bestellwert ausgegebenen Wert um eine Zwischensumme handelt. Wie Sie in Abbildung 4.33 erkennen können, werden nun auch Summen über alle Jahre jedes Monats ausgegeben.
4.21.4 Rangfolgefunktionen Rangfolgefunktionen ermöglichen die Rückgabe eines Rangfolgewertes für jede Zeile im Abfrageergebnis. Dadurch können Sie dem Ergebnis zum Beispiel Zeilennummern hinzuzufügen oder ein Ergebnis mit einer Rangfolge versehen, die angibt, auf welcher Position eine Zeile steht, also welchen Rang diese Zeile einnimmt. Es gibt insgesamt die vier Rangfolgefunktionen: ROW_NUMBER, RANK, DENSE_RANK und NTILE. Für alle Funktionen können mittels einer OVER()-Klausel sowohl Sortierung als auch Gruppierung bzw. Partitionierung für die Rangfolge angegeben werden. Die allgemeine Struktur einer Abfrage unter Verwendung einer der vier Rangfolgefunktionen sieht hierbei so aus:
ROW_NUMBER Die Funktion ROW_NUMBER fügt dem Abfrageergebnis eine Spalte mit einer Zeilennummer hinzu. Schauen Sie sich bitte das folgende Beispiel an:
186
4.21 Komplexe Abfragen mit SELECT Über die OVER-Funktion wird die Rangfolge ausgegeben. Im Beispiel fügen wir eine Spalte Zeilennummer hinzu, die für die Sortierung nach der Spalte LastName vergeben wird. Einen Auszug aus dem Ergebnis sehen Sie in Abbildung 4.34.
Abbildung 4.34 Zeilennummerierung mit ROW_NUMBER
Eine Rangfolgefunktion kann für jede Spalte im Ergebnis hinzugefügt werden. Die folgende Abfrage enthält zwei Spalten mit dem Nachnamen eines Vertriebsmitarbeiters und seinem Gesamtbestellwert. Über ROW_NUMBER wird eine Zeilennummer hinzugefügt, wobei absteigend nach der Gesamtbestellsumme sortiert wird der Vertriebsmitarbeiter mit dem höchsten Bestellwert bekommt also die Zeilennummer 1:
Die ersten Zeilen des Ergebnisses der obigen Abfrage sehen Sie in Abbildung 4.35.
Abbildung 4.35 ROW_NUMBER mit ORDER BY DESC
187
4 Transact SQL-Grundlagen In der OVER-Funktion kann über PARTITION BY auch eine Gruppierung bzw. Partitionierung angegeben werden. In diesem Fall beginnt die Nummerierung für jede Partition von vorne. Schauen Sie sich das folgende Beispiel an, in dem eine Partitionierung nach Kalenderjahr erfolgt:
Um das Ergebnis einzuschränken, werden hier über die LIKE-Klausel nur Vertriebsmitarbeiter ausgewählt, bei denen an der zweiten Position des Nachnamens kein Vokal steht. Abbildung 4.36 zeigt einen Auszug aus dem Ergebnis.
Abbildung 4.36 ROW_NUMBER mit Partitionierung
Die Zeilennummern beginnen in jedem Jahr neu und geben so eine Rangfolge für den Bestellwert pro Vertriebsmitarbeiter und Jahr an. Für die Erzeugung von Ranglisten bietet SQL Server noch zwei weitere Funktionen an, die in den folgenden beiden Abschnitten untersucht werden. RANK und DENSE_RANK Durch die RANK-Funktion kann dem Abfrageergebnis ein Rang hinzugefügt werden, der auf dem Wert einer Spalte des Abfrageergebnisses basiert. So ist es beispielsweise möglich, die Listenpreise der Produkte mit Rangfolge zu bewerten. Zu beachten ist, dass hierbei gleiche Werte auch den gleichen Rang erhalten. Der nächste vergebene Rang ergibt sich dann durch die Anzahl der vorherigen Ränge plus eins. Das ist wie im Sport: Wenn
188
4.21 Komplexe Abfragen mit SELECT zwei Goldmedaillen vergeben werden, bekommt der nächstplatzierte nicht Silber, sondern Bronze. Dadurch können in der Rangfolge auch Lücken entstehen. Bei DENSE_RANK() wird ebenfalls eine Rangfolge vergeben, allerdings treten hierbei keine Lücken in der Nummerierung auf. Betrachten Sie bitte das folgende Beispiel, in dem eine Liste der Produkte mit ihrem Listenpreis ausgegeben wird. Hierbei wird das Produkt mit dem kleinsten Listenpreis an erster Stelle genannt, steht also in der Rangfolge an der ersten Position:
Die beiden letzten Spalten, berechnen die Rangfolge über RANK bzw. DENSE_RANK. Wenn Sie sich den Ausschnitt aus dem Ergebnis der Abfrage in Abbildung 4.37 ansehen, erkennen Sie sofort den Unterscheid zwischen diesen beiden Funktionen.
Abbildung 4.37 RANK und DENSE_RANK
NTILE Mit der NTILE-Funktion können Sie Ihren Abfrageergebnissen Gruppennummern hinzufügen. Hierbei wird nicht jede einzelne Zeile nummeriert, stattdessen wird eine bestimmte Anzahl von Zeilen zu einer Gruppe zusammengefasst. Die Zeilen einer Gruppe bekommen dann dieselbe Gruppennummer. Auch dies lässt sich sicherlich am einfachsten an einem Beispiel demonstrieren. Angenommen, die Vertriebsmitarbeiter sollen nach dem Bestellwert, für den sie verantwortlich sind, in drei unterschiedliche Kategorien Gut, Normal, Unterdurchschnittlich eingeteilt werden. Unter Verwendung von NTILE kann eine entsprechende Abfrage sehr einfach entworfen werden:
189
4 Transact SQL-Grundlagen Listing 4.2 Gruppierung mit NTILE
Die Spalte Kategorie wird eine Gruppennummer von 1 bis 3 enthalten. Dies wird durch die Angabe von erreicht. Die Einordnung in eine der drei Gruppen wird durch die OVER-Funktion anhand des Gesamtbestellwertes für den jeweiligen Vertriebsmitarbeiter vorgenommen. Das Ergebnis sicht dann so aus, wie in Abbildung 4.38 gezeigt.
Abbildung 4.38 Gruppierung mit NTILE
NTILE verwendet für die Einstufung eine Gleichverteilung, versucht also, gleich viele Zeilen in jede Gruppe einzuordnen. Gelingt dies nicht, weil die Anzahl der Zeilen kein Vielfaches der Anzahl der Gruppen ist, so werden zunächst die Gruppen mit der niedrigeren Ordnungszahl um zusätzliche Zeilen ergänzt. Dies ist im obigen Beispiel der Fall. Da die Anzahl der Zeilen (17) geteilt durch die Anzahl der Gruppen (3) einen Rest von 2 liefert, enthalten die ersten beiden Gruppen jeweils sechs Zeilen und die letzte Gruppe fünf. OVER Neben dem Einsatz in Rangfolgefunktionen kann die OVER-Klausel auch zur Berechnung von Aggregaten also zusammen mit Aggregatfunktionen verwendet werden. Dadurch haben Sie die Möglichkeit, Aggregatberechnungen auch sozusagen außerhalb von GROUP BY durchzuführen, indem Sie OVER zusammen mit PARTITION BY verwenden. Die
190
4.21 Komplexe Abfragen mit SELECT folgende Abfrage gibt die Gesamtsumme über alle Bestellwerte zusammen mit der Summe für jeden einzelnen Vertriebsmitarbeiter in einer Zeile aus:
Auffällig ist, dass kein GROUP BY mehr erforderlich ist, wenn die Gruppierung über die OVER-Klausel angegeben wird. Die gesamte OVER-Klausel kann hierbei auch leer sein, in diesem Fall wird das Aggregat über alle Zeilen berechnet. Im Beispiel werden eine Gesamtsumme und eine Summe für jeden Vertriebsmitarbeiter gebildet. Dies erfolgt für alle Zeilen der Verknüpfung der beiden an der Abfrage beteiligten Tabellen. Aus diesem Grund werden mehrfache Zeilen durch die Klausel DISTINCT noch aus dem Ergebnis entfernt. Ein Teilergebnis der Abfrage zeigt Abbildung 4.39.
Abbildung 4.39 Summierung mit OVER
4.21.5 Korrelierende Unterabfragen Die bisher besprochenen Abfragen bestanden meist nur aus einer einzigen SELECTAnweisung. SELECT-Abfragen können jedoch auch ineinander geschachtelt werden. In einem solchen Fall besteht die Abfrage aus inneren und äußeren SELECT-Anweisungen, die in der Regel durch eine Verknüpfungsbedingung miteinander verbunden sind. Solche Abfragen werden als korrelierende Unterabfragen bezeichnet. Hierbei können Unterabfragen an verschiedenen Stellen auftreten. So kann zum Beispiel eine Unterabfrage entworfen werden, die nur einen einzigen Wert (also eine Zeile und Spalte) liefert. Eine solche Abfrage darf als Spaltenausdruck eingesetzt werden:
191
4 Transact SQL-Grundlagen Die obige Abfrage liefert die Namen aller Vertriebsmitarbeiter und deren GesamtBestellwert. Der Bestellwert wird hierbei über eine Unterabfrage ermittelt. Diese Unterabfrage ist über die gemeinsame Spalte SalesPersonId mit der äußeren Abfrage verbunden. Die obige Abfrage liefert dasselbe Ergebnis wie diese:
Und doch unterscheiden sich beide Abfragen. In der ersten Abfrage würden doppelte Nachnamen für Vertriebsmitarbeiter auch in separaten Zeilen ausgegeben. Die zweite Abfrage mit GROUP BY würde diese Werte in einer Zeile zusammenfassen (was nicht korrekt ist). Auch in der Zeilenfilterbedingung können korrelierende Unterabfragen verwendet werden, zum Beispiel in Verbindung mit der IN-Klausel. Eine solche Unterabfrage darf nur eine Spalte, aber beliebig viele Zeilen liefern. Die folgende Abfrage gibt alle Jahre aus, in denen Vertriebsmitarbeiter, deren Namen mit Ab beginnt, Bestellungen initiiert haben:
4.21.6 Abgeleitete Tabellen Bei der Einführung zur SELECT-Anweisung wurde für die FROM-Klausel gesagt, dass hinter dieser Klausel eine Tabelle angegeben werden muss. Dass diese Aussage nicht ganz korrekt war und bei der Einführung nur zur Vereinfachung diente, wissen Sie inzwischen. Immerhin haben wir ja auch bereits Abfragen mit mehreren Tabellen bzw. Sichten erstellt. Wenn man es ganz genau nimmt, dann kann überall dort, wo wir bislang eine Tabelle oder Sicht verwendet haben, ein Tabellenausdruck stehen, also eine Anweisung, die eine Tabelle zurückgibt. In Kapitel 7 werden Sie sehen, wie man benutzerdefinierte Funktionen entwirft, die eine Tabelle zurückliefern. Solche Funktionen können als Tabellenausdruck in SELECT-Anweisungen verwendet werden. An dieser Stelle wollen wir zunächst betrachten, wie SELECT-Anweisungen als Tabellenausdrücke verwendet werden. Die allgemeine Regel hierzu ist recht einfach: Sowohl in der FROM-Klausel als auch im JOIN sind nicht nur Tabellen, sondern auch Tabellenausdrücke erlaubt. Betrachten Sie bitte die folgenden beiden Anweisungen, die dasselbe Ergebnis liefern:
192
4.21 Komplexe Abfragen mit SELECT Die zweite Abfrage verwendet eine sogenannte abgeleitete Tabelle. In der FROM-Klausel wird sozusagen erst die Tabelle für die eigentliche Abfrage erzeugt. Auch die folgenden beiden Abfragen sind logisch äquivalent und liefern identische Ergebnisse:
Die beiden einfachen Beispiele verdeutlichen vielleicht nicht die Nützlichkeit des Konzeptes. Es gibt aber durchaus Fälle, in denen abgeleitete Tabellen erforderlich und auch sinnvoll sind. Für abgeleitete Tabellen muss übrigens immer ein Aliasname angegeben werden. In unserem Beispiel ist dies der Name .
4.21.7 Allgemeine Tabellenausdrücke Allgemeine Tabellenausdrücke (englisch Common Table Expressions, CTE) können Sie sich wie eine Sicht vorstellen, die nur für die Dauer einer Abfrage gültig ist. Eine solche Sicht wird nicht über CREATE VIEW, sondern über die folgende Anweisung erzeugt:
Über die WITH-Klausel wird eine temporäre Sicht erzeugt, auf die dann in der anschließenden SELECT-Anweisung zugegriffen werden kann. In dieser SELECT-Anweisung kann die temporäre Sicht ganz normal in der FROM-Klausel oder auch in JOINs verwendet werden. Nach dem Beenden der gesamten Anweisung existiert die temporäre Sicht nicht mehr. Allgemeine Tabellenausdrücke sind besonders in zwei Fällen nützlich. Zum einen können sie verwendet werden, um Abfragen übersichtlicher zu gestalten. Anstelle eines MonsterSELECTs können Sie beim Einsatz von allgemeinen Tabellenausdrücken mehrere SELECT-Anweisungen verwenden und so die Übersichtlichkeit erhöhen. In einigen Fällen ist es sogar erforderlich, auf CTEs zurückzugreifen, da ein einfaches SELECT nicht die
193
4 Transact SQL-Grundlagen erforderlichen Berechnungen abbilden kann. Ein Beispiel hierfür werden Sie in Kapitel 8 kennenlernen. Ein zweites Einsatzgebiet für allgemeine Tabellenausdrücke sind rekursive Abfragen, die wir weiter hinten in diesem Abschnitt untersuchen werden. Schauen Sie sich bitte nochmals Listing 4.2 und das Ergebnis der Abfrage in Abbildung 4.38 an. Angenommen, Sie möchten statt der Gruppennummer in der letzten Spalte lieber einen Text ausgeben, der die Benotung des entsprechenden Vertriebsmitarbeiters enthält. Hierfür könnten Sie die folgende Abfrage verwenden:
Über die WITH-Klausel wird hier zunächst eine temporäre Sicht mit Namen VBOrders erstellt. Diese Sicht enthält genau die Abfrage aus Listing 4.2. In der äußeren SELECTAnweisung wird VBOrders in der FROM-Klausel ganz genau so wie eine Tabelle verwendet. Über eine CASE-Anweisung werden die Nummern der einzelnen Gruppen in Texte umgewandelt. Außerdem erfolgt eine Sortierung nach dem Nachnamen der Vertriebsmitarbeiter. Ein Teilergebnis der Abfrage zeigt Abbildung 4.40.
Abbildung 4.40 Abfrageergebnis mit allgemeinem Tabellenausdruck
Allgemeine Tabellenausdrücke sind besonders dann nützlich, wenn Sie in Ihren SELECTAnweisungen Unterabfragen verwenden müssen, die mehrfach in derselben Form auftreten. In so einem Fall lohnt sich natürlich die Überführung dieser Unterabfragen in allgemeine Tabellenausdrücke.
194
4.21 Komplexe Abfragen mit SELECT Schauen Sie sich die folgende Anweisung an, in der eine durch einen allgemeinen Tabellenausdruck deklarierte Abfrage mit sich selbst verknüpft wird:
Die Abfrage ermittelt die Anzahl der Bestellungen pro Jahr und Monat und gibt auch eine laufende Summe aus. In Abbildung 4.41 sehen Sie einen Auszug aus dem Ergebnis. Wenn Sie versuchen, dasselbe Ergebnis ohne Verwendung einer CTE zur erhalten, wird die Abfrage erheblich komplizierter (versuchen Sie es ruhig einmal!). Die obige CTE-Deklaration zeigt auch, wie Sie Spaltennamen für die Abfrage in Klammern angeben können.
Abbildung 4.41 Berechnung der laufenden Summe mit einem allgemeinen Tabellenausdruck
Es ist übrigens auch möglich, mehrere Tabellenausdrücke zu deklarieren und diese dann in einer anschließenden SELECT-Anweisung zu verwenden. Es besteht also keine Beschränkung auf nur eine CTE. Wenn Sie mehrere CTEs verwenden möchten, so müssen die einzelnen Deklarationen jeweils durch ein Komma voneinander getrennt werden. Das sieht dann so aus:
195
4 Transact SQL-Grundlagen
Die Abfrage verwendet zwei CTEs,. um den Bestellwert in allen Produktkategorien über die Jahre zu summieren. Ein Teilergebnis der Abfrage zeigt Abbildung 4.42.
Abbildung 4.42 Teilergebnis für mehrere CTEs
Rekursive Abfragen Eine interessante Möglichkeit mit allgemeinen Tabellenausdrücken ist der Umstand, dass in der inneren SELECT-Anweisung auf den Tabellenausdruck selber verwiesen werden kann. Dadurch sind bei der Verwendung von CTEs rekursive Abfragen möglich. Dies soll am Beispiel der Tabelle HumanResources.Employee gezeigt werden. Zunächst einmal schauen wir uns einige Zeilen und Spalten der Tabelle an und verwenden hierfür die folgende Anweisung:
In Abbildung 4.43 sehen Sie einen Auszug aus dem Ergebnis der Abfrage.
196
4.21 Komplexe Abfragen mit SELECT
Abbildung 4.43 Inhalt der Tabelle HumanResources.Employee
Interessant ist hierbei vor allem die Spalte ManagerID. Durch diese Spalte verweist die Employee-Tabelle auf sich selber. Jeder Manager ist auch wieder ein Angestellter, wird also ebenfalls durch eine Zeile in der Tabelle Employee repräsentiert. Der in der Spalte ManagerID enthaltene Wert verweist also auf eine EmployeeID in derselben Tabelle. Hierbei kann ein Manager natürlich auch wieder einen übergeordneten Manager haben. Lediglich ein einziger Angestellter besitzt keinen Manager, dies ist gerade der Chef der gesamten Firma (siehe Abbildung 4.43). Im Normalfall sind derartige rekursive Strukturen mit SQL nur sehr schwer abzubilden. Die Abfrage aller Angestellten mit ihren Managern ist mit normalen Mitteln relativ schwierig und unübersichtlich nicht so jedoch beim Verwenden einer CTE. Allgemeine Tabellenausdrücke erlauben rekursive Abfragen. Hierbei kann eine CTE auf sich selbst verweisen. Eine CTE muss hierfür zunächst ein sogenanntes Anker-Element definieren, welches das Ende einer Rekursion kennzeichnet. Über den UNION ALL-Operator kann dann der rekursive Teil der Abfrage hinzugefügt werden. Angenommen, es sollen alle Mitarbeiter mit ihren Managern angezeigt werden. Eine entsprechende CTE-Abfrage sieht so aus:
197
4 Transact SQL-Grundlagen Der erste Teil der Abfrage legt das Anker-Element fest. Dies ist gerade der einzige Mitarbeiter ohne Manager, also der oberste Chef. Im Anschluss wird dann die Rekursion definiert. Die CTE verweist hier im JOIN auf sich selber über eine Verknüpfung der Spalten ManagerID und EmployeeID. Die äußere SELECT-Anweisung muss schließlich nur noch die erzeugte Liste zurückgeben. Der SQL Server schützt sich vor Endlos-Rekursionen in allgemeinen Tabellenausdrücken. Zu diesem Zweck ist die maximale Rekursionstiefe auf 100 festgelegt. Beim Überschreiten dieser Rekursionstiefe erzeugt die Abfrage eine Fehlermeldung. Über die Option MAXRECURSION kann auch ein anderer Wert definiert werden, im Beispiel verwenden wir eine maximale Rekursionstiefe von 10. Einen Ausschnitt aus dem Ergebnis der Abfrage sehen Sie in Abbildung 4.44.
Abbildung 4.44 Eine rekursive CTE-Abfrage
4.21.8 PIVOT und UNPIVOT Sehen Sie sich bitte die folgende Abfrage an, die eine Summe der Bestellwerte je Vertriebsmitarbeiter und Kalenderjahr ermittelt:
198
4.21 Komplexe Abfragen mit SELECT Abbildung 4.45 zeigt ein Teilergebnis der obigen Abfrage.
Abbildung 4.45 Bestellwert pro Mitarbeiter und Kalenderjahr
Angenommen, Sie möchten nun anstelle der Ausgabe einer separaten Zeile für jedes Kalenderjahr und jeden Vertriebsmitarbeiter die Ausgabe in einer zweidimensionalen Matrix organisieren. Hierbei soll für jeden Vertriebsmitarbeiter eine Zeile und für jedes Jahr eine Spalte in der Matrix enthalten sein. Die Zelle am Kreuzungspunkt zwischen Vertriebsmitarbeiter und Jahr wird dann den entsprechenden Bestellwert enthalten. Das Ergebnis soll also so aussehen, wie in Abbildung 4.46 gezeigt:
Abbildung 4.46 Vertauschung von Zeilen und Spalten
Haben Sie eine Idee, wie so etwas bewerkstelligt werden kann? Die CASE-Anweisung hilft hier weiter. Sie können für jedes Jahr eine eigene berechnete Spalte erzeugen und über eine CASE-Anweisung jeweils nur diejenigen Werte summieren, die zur Spaltenüberschrift also zum Jahr passen. Eine entsprechende SELECT-Anweisung sieht dann so aus:
199
4 Transact SQL-Grundlagen
Diese Anweisung erzeugt exakt die Ausgabe aus Abbildung 4.46. Über den PIVOT-Operator ist es möglich, auch ohne CASE-Anweisung auszukommen. Für die obige SELECT-Anweisung sieht eine adäquate Anweisung unter Verwendung von PIVOT so aus:
Stören Sie sich bitte nicht an der Verwendung einer abgeleiteten Tabelle in der FROMKlausel. Entscheidend ist der PIVOT-Operator, der folgendermaßen arbeitet: Der erste Ausdruck in der Klammer ist eine Aggregatfunktion, die für die einzelnen Zellen der Ausgabematrix berechnet wird. Die FOR-Klausel bestimmt, aus welcher Spalte die Daten für die Ergebnisspalten berechnet werden sollen. Über die IN-Klausel werden die in der Ausgabe enthaltenen Spalten explizit angegeben. Und wie werden die Zeilen für die Ausgabematrix bestimmt? Diese Zeilen ergeben sich durch Gruppierung nach den Spalten der äußeren SELECT-Anweisung, die nicht explizit in der PIVOT-Klausel angegeben werden. In unserem Fall ist dies nur die Spalte VB. Wenn Sie zu Beginn Verständnisprobleme mit der Arbeitsweise des PIVOT-Operators haben, so liegt dies sicherlich vor allem daran, dass die Bestimmung der Zeilen für die Ausgabe nicht so ohne Weiteres zu erkennen ist. Verzweifeln Sie in solch einem Fall bitte nicht, Sie sind mit diesen Problemen in bester Gesellschaft! Ich habe bislang noch niemanden getroffen mich selbst eingeschlossen der die Arbeitsweise von PIVOT auf Anhieb verstanden hat. Mein Rat: Probieren Sie einfach unterschiedliche Varianten aus, und versuchen Sie das erzeugte Ergebnis bzw. die produzierten Fehler zu interpretieren. Die beiden SELECT-Anweisungen unter Verwendung von CASE und PIVOT liefern ein fast identisches Ergebnis. Wenn Sie die beiden Anweisungen ausprobieren, werden Sie feststellen, dass der Unterschied lediglich in der Darstellung von Zellen besteht, für die keine Werte existieren. Die Version mit CASE gibt in diesen Spalten den Wert 0 aus, bei der Verwendung von PIVOT erhalten Sie an dieser Stelle eine NULL. Wir können die Variante mit PIVOT jedoch einfach anpassen und die NULL-Werte in 0 umwandeln:
200
4.21 Komplexe Abfragen mit SELECT
Der Operator UNPIVOT ist die Umkehrung von PIVOT. Mit UNPIVOT können Spalten in Zeilen konvertiert werden. Angenommen, wir hätten eine Tabelle PivotTable folgendermaßen erzeugt:
Der Inhalt dieser Tabelle ist in Abbildung 4.46 zu sehen. Wenn nun die Spalten für die einzelnen Jahre in Zeilen ausgegeben werden sollen, könnte so verfahren werden:
Die einzelnen SELECT-Anweisungen geben jeweils die Werte für ein Jahr zurück. Die SELECT-Anweisungen werden einfach durch UNION-Operatoren aneinandergehängt, das Ergebnis der gesamten Abfrage ist somit das bereits in Abbildung 4.45 gezeigte. Durch die Verwendung des UNPIVOT-Operators kann dasselbe Ergebnis erzielt werden. Die entsprechende Abfrage sieht so aus:
Im UNPIVOT-Operator wird zunächst die Spalte angegeben, für welche die UNPIVOTOperation ausgeführt werden soll. In unserem Fall ist dies die Spalte Betrag. In der
201
4 Transact SQL-Grundlagen FOR-Klausel werden dann die Spalten, die in Zeilen konvertiert werden sollen, explizit benannt. Ein Vergleich von PIVOT und CASE zeigt keinerlei Unterschiede in der Laufzeit. Hier ist es tatsächlich egal, ob Sie die CASE- oder PIVOT-Version bevorzugen. Bei UNPIVOT und UNION sieht die Sache ein wenig anders aus. Die Version mit UNPIVOT ist schneller als eine vergleichbare Anweisung unter Verwendung mehrerer SELECT-Anweisungen, die mit UNION verknüpft werden. Hier sollten Sie also in jedem Fall UNPIVOT den Vorzug geben.
4.21.9 OUTPUT In DELETE-, INSERT- und UPDATE-Anweisungen kann über die OUTPUT-Klausel eine genaue Information darüber ausgegeben werden, welche Zeilen von der jeweiligen Operation betroffen waren. Zu diesem Zweck stehen Ihnen innerhalb der OUTPUT-Klausel die beiden virtuellen Tabellen INSERTED und DELETED zur Verfügung, die Informationen über den Zustand vor und nach der Datenänderung enthalten. Nicht für alle Datenänderungsanweisungen stehen beide Tabellen zur Verfügung. Tabelle 4.1 enthält eine Übersicht über die mögliche Verwendung dieser Tabellen. Tabelle 4.24 INSERTED und DELETED Virtuelle Tabelle
Beschreibung
Verfügbar für
INSERTED
Enthält die Spaltenwerte nach der Änderungsoperation
INSERT UPDATE
DELETED
Enthält die Spaltenwerte vor der Änderungsoperation
DELETE UPDATE
Die folgende Anweisung löscht Zeilen aus der gerade im vorangegangenen Abschnitt erzeugten Tabelle PivotTable und gibt die gelöschten Zeilen im Ergebnisbereich aus:
Die UPDATE-Anweisung ist die einzige Anweisung, innerhalb der ein Zugriff auf beide Tabellen INSERTED und DELETED möglich ist. Die folgende UPDATE-Anweisung ändert einige Zeilen in der Tabelle PivotTable und gibt den Zustand vor und nach der Änderung aus:
Abbildung 4.1 zeigt das via OUTPUT erzeugte Ergebnis der Abfrage:
202
4.21 Komplexe Abfragen mit SELECT
Abbildung 4.47 Ergebnis der UPDATE-Operation mit OUTPUT
In Kapitel 7 werden Sie sehen, wie Sie die OUTPUT-Klausel in der Programmierung mit T-SQL verwenden können.
4.21.10 APPLY APPLY ermöglicht das Aufrufen eines Tabellenwertausdrucks für jede Zeile, die von einer Abfrage zurückgegeben wird. Ein Tabellenwertausdruck ist ein Ausdruck, der eine Tabelle zurückgibt. Dies kann zum Beispiel eine Abfrage oder auch eine Tabellenwertfunktion (siehe Kapitel 7) sein. Es gibt zwei Sorten von APPLY: CROSS APPLY und OUTER APPLY, die sich ähnlich wie bei JOINs in der Behandlung von nicht existierenden Zeilen unterscheiden. CROSS APPLY liefert nur Werte für Zeilen, in denen der Tabellenwertausdruck auch ein Ergebnis liefert, über OUTER APPLY werden alle Zeilen zurückgegeben, wobei für nicht existierende Zeilen NULL als Spaltenwert eingetragen wird. Die folgende Abfrage gibt die drei letzten Bestellungen des Vertriebsmitarbeiters mit der 284 zurück:
Wenn Sie nun für alle Angestellten eine Liste der letzten drei initiierten Bestellungen erhalten möchten, so können Sie die obige Abfrage für jeden Mitarbeiter ausführen, wobei Sie jeweils die des Mitarbeiters als Parameter für die Abfrage verwenden. Durch den APPLY-Operator ist eine solche Abfrage sehr einfach zu schreiben:
Von der äußeren Abfrage werden alle Mitarbeiter zurückgeliefert. Für jede Zeile dieser Abfrage wird dann über CROSS APPLY eine weitere Abfrage gestartet, an die sozusagen
203
4 Transact SQL-Grundlagen als Parameter die des Mitarbeiters übergeben wird. Die über CROSS APPLY ausgeführte Abfrage liefert dann die letzten drei Bestellungen für den übergebenen Mitarbeiter. Ein Teilergebnis der Abfrage zeigt Abbildung 4.48:
Abbildung 4.48 Ergebnis der Abfrage mit CROSS APPLY
Wenn Sie in der obigen Abfrage CROSS durch OUTER ersetzen, dann werden in das Ergebnis immer alle Zeilen der äußeren Abfrage aufgenommen, und zwar unabhängig davon, ob der APPLY-Ausdruck Zeilen zurückliefert oder nicht. OUTER APPLY funktioniert also im Prinzip so wie ein OUTER JOIN: Die Spalten, für die der Tabellenwertausdruck keine Zeilen liefert, werden in der Ausgabe mit dem Wert NULL belegt. In Abbildung 4.49 sehen Sie einen Ausschnitt aus dem OUTER APPLY-Ergebnis.
Abbildung 4.49 Ergebnis der Abfrage mit OUTER APPLY
In Kapitel 7 wird der APPLY-Operator nochmals in Verbindung mit Tabellenwertfunktionen verwendet.
204
4.22 Zusammenfassung
4.22
Zusammenfassung In diesem Kapitel haben Sie eine umfassende Einführung in die Verwendung von T-SQL und die Möglichkeiten für die Abfrage sowie das Einfügen, Ändern und Löschen von Daten mittels T-SQL erhalten. Sie wissen nun, welche Datentypen T-SQL zur Verfügung stellt, wie Sie Ausdrücke zur Berechnung von einzelnen Werten oder auch kompletten Tabellen verwenden können und wie Sie in solchen Ausdrücken integrierte Funktionen einsetzen. Das Kapitel hat Ihnen gezeigt, wie Datenbanken angelegt und Tabellen innerhalb von Schemas in Datenbanken organisiert werden. Das Anlegen von Tabellen und die Festlegung von Integritätsbedingungen für diese Tabellen war ein wesentlicher Bestandteil dieses Kapitels. Ihnen ist nun auch klar, wofür Sichten eingesetzt werden können und welche Möglichkeiten Sie durch die Verwendung von Sichten als Datenzugriffsschicht haben. Eine sehr wichtige Besonderheit von SQL ist die Behandlung von NULL-Werten. Dieser Thematik wurde daher ein eigener Abschnitt innerhalb dieses Kapitels gewidmet. Zum Abschluss des Kapitels haben Sie schließlich einen Einblick in die Entwicklung komplexer Abfragen erhalten.
205
5 Transaktionen und Sperren Transaktionen sind ein sehr wesentliches Konzept für DBMS. Durch Transaktionsmechanismen stellt ein DBMS die Funktionalität zur Gewährleistung der Datenintegrität zur Verfügung. Innerhalb von Transaktionen sperrt der SQL Server Ressourcen, die von der Transaktion verwendet werden, und verweigert dadurch anderen Transaktionen den gleichzeitigen Zugriff auf diese Ressourcen. Diese Sperren ermöglichen letztlich einen konkurrierenden Zugriff auf Ressourcen, da Anforderungen an eine gesperrte Ressource in eine Warteschlange eingereiht und nacheinander abgearbeitet werden. In diesem Kapitel werden Sie erfahren, wie das SQL Server-Transaktionsmodell aufgebaut ist. Hierbei wird Ihnen deutlich werden, in welcher Weise der SQL Server Ressourcen für andere Prozesse sperrt und wie der Datenbankentwickler die Art und Weise dieser Sperren beeinflussen kann.
5.1
Eigenschaften von Transaktionen Transaktionen fassen einzelne SQL-Anweisungen zu Gruppen zusammen. Die wesentliche Eigenschaft einer Transaktion ist, dass alle in ihr enthaltenen Anweisungen entweder als Gesamtheit oder gar nicht ausgeführt werden. Das Paradebeispiel für eine bildhafte Erklärung ist hier immer wieder eine finanzielle Transaktion (in diesem Zusammenhang wird der Begriff Transaktion aus der Bankenwelt verwendet), bei der von einem Konto ein Betrag abgebucht und dann einem anderen Konto gutgeschrieben wird. Eine solche Überweisung kann durch zwei einfache INSERT-Anweisungen ausgeführt werden:
Ein Problem tritt auf, wenn zum Beispiel nach der Ausführung der ersten INSERTAnweisung ein Systemausfall zu verzeichnen ist, sodass die Ausführung der zweiten INSERT-Anweisung verhindert wird. In diesem Fall wäre zwar eine Abbuchung, nicht aber
207
5 Transaktionen und Sperren die Zubuchung auf das zweite Konto erfolgt. Um so etwas zu vermeiden, können Sie beide Anweisungen in eine Transaktion einbetten. Der SQL Server wird dann durch seine Transaktionsmechanismen sicherstellen, dass entweder beide Anweisungen ausgeführt werden oder eben keine. Stellen Sie sich nun bitte einmal vor, dass eine Benutzerin den durchschnittlichen Listenpreis aller Produkte wissen möchte. Sie führt dafür folgendes Kommando aus:
Während dieses Kommando aktiv ist, wird in der Vertriebsabteilung entschieden, dass alle Listenpreise um 3% erhöht werden sollen. Hierzu verwendet eine Mitarbeiterin dieses UPDATE-Kommando:
Die beiden Anweisungen konkurrieren im Zugriff auf die Zeilen der Tabelle Production.Product. Wenn das UPDATE-Kommando ausgeführt wird, während das SELECT-Kommando läuft, wird das SELECT auf einen inkonsistenten Datenbestand zugreifen und ein falsches Ergebnis liefern. Es muss daher dafür gesorgt werden, dass beide Anweisungen nicht gleichzeitig auf die Tabelle zugreifen, sondern gegeneinander abgeschirmt (isoliert) werden. Auch hierfür bieten Transaktionen die entsprechenden Mechanismen an. Allgemein zeichnen sich Transaktionen durch vier grundlegende Eigenschaften aus: Atomicity (Atomarität), Consistency (Konsistenz), Isolation (Isolation) und Durability (Beständigkeit). Diese vier Eigenschaften werden Ihnen oftmals einfach in der Abkürzung ACID begegnen. Hinter diesen vier Transaktionseigenschaften verbergen sich die folgenden Konzepte: Atomarität (Atomicity) Das Atomaritätsprinzip kennzeichnet das Ganz oder gar nicht-Verhalten einer Transaktion. Wir haben dieses Prinzip bereits in unserem Beispiel mit den Kontobewegungen veranschaulicht, die entweder als Gesamtheit oder gar nicht ausgeführt werden sollen. Es werden also entweder alle in der Transaktion enthaltenen Änderungen permanent gespeichert oder keine. Eine Transaktion ist nicht teilbar ganz im Gegensatz zu Atomen übrigens, die für die Namensgebung dieser Eigenschaft herhalten mussten. Konsistenz (Consistency) Mit Konsistenz ist gemeint, dass sich der Datenbestand nach Abschluss einer Transaktion in einem Zustand befindet, in dem alle existierenden Integritäts-, aber auch Geschäftsregeln gültig sind. Änderungen, die gegen bestehende Regeln verstoßen, werden nicht zugelassen. Bitte bedenken Sie, dass diese Aussage nur für abgeschlossene, also bestätigte oder abgebrochene, Transaktionen gilt. Während der Ausführung einer Transaktion kann sehr wohl gegen geltende Regeln verstoßen werden. Nach Abschluss der Transaktion allerdings darf dies nicht mehr der Fall sein. Eine Transaktion überführt also einen Datenbestand aus einem konsistenten Zustand in einen anderen.
208
5.1 Eigenschaften von Transaktionen Isolation (Isolation) Transaktionen isolieren sich gegen andere Transaktionen, indem sie benötigte Ressourcen sperren und somit anderen Transaktionen zeitweilig den Zugriff auf diese Ressourcen verweigern. Weiter oben haben wir ein Beispiel mit der gleichzeitigen Abfrage des durchschnittlichen Listenpreises und der Aktualisierung aller Listenpreise betrachtet. Diese beiden Abfragen müssen gegeneinander isoliert werden, damit eine gleichzeitige Ausführung verhindert wird. Im einfachsten Fall können Sie sich diese Isolierung wie eine Serialisierung vorstellen: Die zweite Abfrage muss einfach so lange warten, bis die erste Abfrage beendet wurde. Auf diese Art und Weise wird dem Benutzer eine quasi Parallelverarbeitung vorgegaukelt, während die Sperrmechanismen innerhalb der Transaktion dafür sorgen, dass beide Anweisungen in Wirklichkeit nacheinander abgearbeitet werden. Dies ist natürlich mit Wartezuständen verbunden, da Abfragen, die Ressourcen verwenden möchten, die gerade von einer anderen Transaktion gesperrt sind, auf die Freigabe dieser Ressourcen warten müssen. Um solche Wartezustände zu minimieren, ist die Transaktionsisolation in verschiedenen Stufen einstellbar. Durch die Einstellung der Isolierungsstufe können Sie somit das Verhalten Ihrer Transaktionen entscheidend beeinflussen. Wir werden später in diesem Kapitel noch genauer auf die Einstellung der Isolierungsstufe und die Sperrmechanismen des SQL Servers eingehen. Beständigkeit (Durability) Mit Transaktionsbeständigkeit ist gemeint, dass alle abgeschlossenen Transaktionen dauerhaft in der Datenbank gespeichert sind. Selbst ein Systemausfall kann nicht dazu führen, dass bestätigte Transaktionen in Ihrem Datenbestand fehlen. Wie der SQL Server dies unter Verwendung des Transaktionsprotokolls sicherstellt, wird später in diesem Kapitel noch erläutert. Abschließend möchte ich zu dieser Einführung noch einen Gedanken äußern: Transaktionen bieten Ihnen als Datenbankentwickler die Möglichkeit, Datenänderungen so zusammenzufassen, dass diese ganz oder gar nicht ausgeführt werden. Die Transaktionsmechanismen können aber natürlich nicht überwachen, wie semantisch sinnvoll Sie Ihre Transaktionen entworfen haben und ausführen. So könnten Sie im obigen Beispiel mit den Kontobewegungen ja auch jede der beiden INSERT-Anweisungen in eine separate Transaktion einfügen und nicht beide zusammen in eine. Aus der Sicht des DBMS wäre dies völlig korrekt, aus der Sicht Ihrer Anwendung sicherlich nicht. Es liegt also in der Verantwortung des Entwicklers, dass eine Transaktion nur bestätigt wird, wenn die Daten in einem logisch korrekten Zustand sind bei dieser Entscheidung kann Ihnen auch ein hoch entwickeltes DBMS nicht helfen.
209
5 Transaktionen und Sperren
5.2
Start und Ende einer Transaktion Generell werden Ihre SQL-Anweisungen immer innerhalb einer Transaktion ausgeführt Punkt. An dieser Tatsache können Sie nichts ändern und wollen das hoffentlich auch nicht. Durch das Ausführen aller Kommandos innerhalb von Transaktionen schützt das DBMS Ihre Daten vor Inkonsistenz und ermöglicht konkurrierenden Zugriff auf gemeinsam verwendete Daten. Sie können eine Transaktion explizit mit dem Kommando BEGIN TRANSACTION (es reicht auch einfach BEGIN TRAN) starten. Optional können Sie der Transaktion auch einen Namen geben, den Sie hinter BEGIN TRANSACTION angeben. Möchten Sie die innerhalb der Transaktion vorgenommenen Änderungen permanent speichern, so beenden Sie die Transaktion mit COMMIT TRANSACTION (auch dieses Kommando können Sie durch COMMIT TRAN oder auch einfach nur durch COMMIT abkürzen.) Falls die Änderungen zurückgenommen werden sollen, verwenden Sie ROLLBACK, ROLLBACK TRAN oder ROLLBACK TRANSACTION. Eine Transaktion kann also zum Beispiel so aussehen:
Die obige Transaktion erhöht den Listenpreis für alle vorhandenen Produkte und gibt anschließend die neue Produktliste aus. Dann wird die Transaktion allerdings nicht bestätigt, sondern zurückgenommen, der alte Listenpreis ist also weiterhin gültig. Transaktionen können geschachtelt werden. In diesem Fall gilt ein ROLLBACK oder COMMIT immer für die unmittelbar davor liegende BEGIN TRANSACTIONAnweisung. Über die globale Variable @@TRANCOUNT können Sie die aktuelle Anzahl aktiver Transaktionen abfragen.
Das innere ROLLBACK (oder COMMIT) hat für den Datenbestand keinerlei funktionelle Bedeutung. Nur ein COMMIT für die äußere Transaktion sorgt dafür, dass Ihre Daten permanent gespeichert werden. Innere COMMITs oder ROLLBACKs erniedrigen ledig-
210
5.2 Start und Ende einer Transaktion lich den Transaktionszähler, vermindern also den Wert von @@TRANCOUNT um 1, genau so, wie ein inneres BEGIN TRANSACTION diesen Zähler lediglich erhöht. Im obigen Beispiel wird also der Wert der Variablen @@TRANCOUNT in der inneren Transaktion 2 sein, das COMMIT der inneren Transaktion bewirkt hier keine permanente Datenspeicherung, da die gesamte Transaktion durch das ROLLBACK in der äußeren Transaktion zurückgesetzt wird. Sie müssen unbedingt darauf achten, dass Ihre T-SQL-Stapel nach der Beendigung keine offenen Transaktionen mehr haben; all Ihre mit BEGIN TRANSACTION gestarteten Transaktionen also auch mit ROLLBACK oder COMMIT abgeschlossen wurden. Laufende, also offene, Transaktionen halten die benötigten Sperren von Datenbank- und Serverobjekten aufrecht und schränken dadurch die Möglichkeiten des konkurrierenden Zugriffs auf diese Objekte ein, was wiederum ein teilweise beträchtlich verschlechtertes Laufzeitverhalten Ihrer Applikationen zur Folge haben kann. Über @@TRANCOUNT können Sie abfragen, ob beim Beenden Ihres T-SQL Stapels noch offene Transaktionen vorhanden sind. Ist dies der Fall, so müssen Sie entsprechend reagieren und diese Transaktionen bestätigen oder abbrechen. Weiter oben habe ich Ihnen gesagt, dass all Ihre T-SQL-Anweisungen immer innerhalb einer Transaktion ausgeführt werden. Was ist denn aber nun, wenn Sie kein BEGIN TRANSACTION verwenden, also einfach nur ein T-SQL-Kommando eingeben, so wie wir es bisher in zahlreichen Beispielen getan haben? Wie werden Ihre Anweisungen in so einem Fall in Transaktionen verpackt? Der SQL Server erlaubt Ihnen die Konfiguration dieser automatischen Transaktionen. Lesen Sie hierzu einfach den folgenden Abschnitt.
5.2.1
Transaktionsmodi des SQL Servers
SQL Server kennt die drei unterschiedlichen Transaktionsmodi, Autocommit, Implizit und Explizit, mit denen Sie steuern können, wie Ihre Anweisungen in Transaktionen eingeschlossen werden: Autocommit Dies ist das Standardverhalten des SQL Servers. Jede einzelne T-SQL-Anweisung wird als eine eigene Transaktion behandelt. Schlägt die Anweisung fehl, so wird für diese eine Anweisung ein ROLLBACK durchgeführt, anderenfalls ein COMMIT. Wichtig ist, dass Sie verstehen, dass tatsächlich jede einzelne Anweisung als separate Transaktion angesehen wird, nicht der gesamte T-SQL-Stapel. Implizite Transaktionen Eine Transaktion wird implizit durch SQL-Kommandos, die Daten verändern, gestartet Die Kommandos, die eine implizite Transaktion öffnen, sind:
211
5 Transaktionen und Sperren ALTER TABLE CREATE DELETE DROP FETCH GRANT INSERT OPEN REVOKE SELECT TRUNCATE TABLE UPDATE Wenn eine Verbindung sich bereits in einer impliziten Transaktion befindet, so wird durch die angegebenen Kommandos allerdings keine weitere Transaktion geöffnet. (Stören Sie sich bitte nicht daran, wenn Sie im Moment noch nicht alle der oben aufgelisteten Befehle verstehen, wir werden diese in späteren Kapiteln noch behandeln.) Jedes der angeführten Kommandos startet also implizit ein BEGIN TRANSACTION. Das COMMIT oder ROLLBACK müssen Sie im T-SQL-Code selber durchführen. Dies dürfen Sie auf keinen Fall vergessen, da offene Transaktionen wie bereits oben erwähnt Ressourcen sperren und somit andere Prozesse blockieren. Sie versetzen eine Verbindung in den impliziten Transaktionsmodus durch das Kommando SET IMPLICIT_TRANSACTIONS ON:
Das UPDATE-Kommando startet eine implizite Transaktion. Das abschließende ROLLBACK oder COMMIT ist hier obligatorisch! Alternativ können Sie die Einstellung des impliziten Transaktionsmodus auch über das Menü Abfrage/Abfrageoptionen
und im Fenster Abfrageoptionen dann im Bereich Ausführung/ANSI vornehmen, wie Sie in Abbildung 5.1 sehen.
212
5.3 Sperren
Abbildung 5.1 Transaktionsmodus auf Implizit setzen
Explizite Transaktionen Der explizite Transaktionsmodus wird stets automatisch aktiviert, sobald Sie das Kommando BEGIN TRANSACTION absetzen. Ein korrespondierendes ROLLBACK oder COMMIT beendet den expliziten Modus wieder.
5.3
Sperren In den vorangegangenen Abschnitten wurde bereits mehrfach erwähnt, dass der SQL Server innerhalb von Transaktionen Ressourcen sperrt und somit anderen Transaktionen den gleichzeitigen Zugriff auf diese Ressourcen verwehren kann. Transaktionen, die bereits gesperrte Ressourcen verwenden möchten, müssen eventuell auf die Freigabe dieser Ressourcen durch die sperrende Transaktion warten. Eventuell deshalb, weil es durchaus auch Sperren gibt, die einen gleichzeitigen Zugriff auf gesperrte Ressourcen gestatten. In diesem Abschnitt soll nun näher auf die Sperrmechanismen des SQL Servers eingegangen werden. Hierbei werden folgende Fragen geklärt: Welche Ressourcen sind überhaupt sperrbar? Weiter oben wurde bereits am Rande erwähnt, dass innerhalb von Transaktionen Server- und Datenbankobjekte gesperrt werden können. SQL Server kennt allerdings noch eine Reihe weiterer sperrbarer Ressourcen. Wie sperrt der SQL Server diese Ressourcen? Abhängig von der jeweiligen SQLAnweisung gibt es unterschiedliche Sperrtypen, die sogar kompatibel zueinander sein können. Wie beeinflussen Sperren Transaktionen, und wie kann der Datenbankentwickler hierauf Einfluss nehmen?
213
5 Transaktionen und Sperren
5.3.1
Sperrbare Ressourcen
SQL Server kann eine Reihe von Ressourcen sperren, die für den Datenbankentwickler wichtigsten sind in Tabelle 5.1 aufgeführt: Tabelle 5.1 Sperrbare Ressourcen des SQL Servers Ressource
Erklärung
RID
Eine Zeile in einer Tabelle über die RowID dieser Zeile
KEY
Eine Schlüsselsperre, d. h. eine Zeile in einem Index
HOBT
Heap Or BTree. Eine Sperre auf einem Heap oder Index
PAGE
Eine 8 KB große Seite in einer Tabelle oder einem Index.
EXTENT
Eine zusammenhängende Gruppe von acht Seiten
TABLE
Eine gesamte Tabelle
DATABASE
Eine gesamte Datenbank
METADATA
Sperre für den Zugriff auf Kataloginformationen
SQL Server versucht stets, mit Sperren sparsam umzugehen, also so wenige Ressourcen wie möglich zu sperren. Durch dieses Verhalten wird die Parallelverarbeitung verbessert: Je weniger gesperrt ist, umso weniger Blockierungen, also Wartezustände, können natürlich auftreten. Der SQL Server kennt allerdings auch einen Mechanismus namens Lock Escalation. Durch diesen Mechanismus können viele kleine Sperren aufgehoben und durch einige wenige Sperren der nächsthöheren Ressource ersetzt werden. Lock Escalation wird immer dann angewendet, wenn die Anzahl der aufrechtzuerhaltenden Sperren so groß wird, dass die Sperren selber zu viele Ressourcen belegen. Das Sperren und Freigeben von Elementen benötigt ja zum Beispiel auch Zeit und Speicherplatz. In so einem Fall kann SQL Server entscheiden, lieber wenige Sperren auf großen Einheiten als viele Sperren auf kleinen Einheiten zu verwenden. Schauen Sie sich bitte noch einmal unsere Anweisung zum Erhöhen des Listenpreises an:
SQL Server wird zunächst beginnen, die Zeilen in der Tabelle Production.Product zu sperren. Sind so viele Zeilen betroffen, dass hierfür zu viele SQL Server-Ressourcen benötigt werden, so können die Zeilensperren aufgehoben und stattdessen die nächsthöheren Einheit, also Seiten gesperrt werden. Sind auch hier zu viele Seiten betroffen, so mag der Server entscheiden, dass nun Extents gesperrt werden. Wenn die Anzahl der gesperrten Extents ebenfalls zu groß wird, so kann schließlich die gesamte Tabelle gesperrt werden. Bei Lock Escalation geht der SQL Server davon aus, dass die Ausführungszeit der Abfrage durch weniger Sperren auf größeren Objekten insgesamt besser ist. Sie können sich die momentan aktiven Sperren im Aktivitätsmonitor ansehen. Die dynamische Verwaltungssicht , die für jede Datenbank existiert, gibt Ihnen ebenfalls Auskunft über die aktuellen Sperranforderungen für eine Datenbank. Sie können
214
5.3 Sperren dies ausprobieren, indem Sie zunächst eine explizite Transaktion starten und dann nochmals unser UPDATE-Kommando innerhalb dieser Transaktion ausführen, ohne allerdings die Transaktion auch explizit zurückzusetzen oder zu bestätigen:
Das erste SELECT-Kommando gibt die ProzessID der Verbindung zurück, die wir später im Aktivitätsmonitor noch benötigen werden. Alternativ können Sie diese ID auch in der Statuszeile des Abfragefensters in Klammern neben Ihrem Anmeldenamen sehen:
Da in der Transaktion das abschließende COMMIT oder ROLLBACK fehlt, werden die von dieser Transaktion verwendeten Sperren aufrechterhalten und können im Aktivitätsmonitor beobachtet werden. Den Aktivitätsmonitor starten Sie über den Objekt-Explorer im Zweig Verwaltung/Aktivitätsmonitor (Abbildung 5.2).
. Abbildung 5.2 Starten des Aktivitätsmonitors
Wählen Sie im linken Teil des Aktivitätsmonitors Sperren nach Prozess und dann in der Combobox Ausgewählter Prozess Ihre Prozess-ID. Anschließend können Sie sehen, welche Sperren Ihre nicht abgeschlossene Transaktion aufrechterhält (Abbildung 5.3).
215
5 Transaktionen und Sperren
Abbildung 5.3 Aktive Sperren im Aktivitätsmonitor
In Beispiel sehen Sie eine Reihe von Seitensperren, die aus dem Lock-EscalationMechanismus hervorgegangen sind. SQL Server hat hier also entschieden, die einzelnen Zeilensperren in Seitensperren zu überführen. Alternativ können Sie die aktiven Sperren auch über die dynamische Verwaltungssicht abfragen:
Diese Sicht gibt im Prinzip genau die im Aktivitätsmonitor dargestellte Tabelle zurück. enthält die aktiven Sperren aller Verbindungen, sie können aber, wie im obigen Beispiel gezeigt, auch nur die Sperren für eine bestimmte Verbindung herausfiltern. Im Aktivitätsmonitor sehen Sie auch eine Spalte Anforderungsmodus, die Auskunft darüber gibt, welcher Sperrtyp angefordert wurde. Die verschiedenen Sperrtypen sollen nun im folgenden Abschnitt näher erläutert werden.
216
5.3 Sperren
5.3.2
Sperrtypen
Der SQL Server kann Ressourcen in verschiedenen Modi sperren und dadurch konkurrierenden Transaktionen eventuell ebenfalls Zugriff auf eine gesperrte Ressource ermöglichen. Das Ziel ist auch hier wieder eine Erhöhung der Parallelverarbeitung dadurch, dass nur so wenig wie möglich gesperrt wird. So wird zum Beispiel lesender Zugriff auf Tabellendaten, wie er von SELECT-Anweisungen benötigt wird, von mehreren Transaktionen gleichzeitig gestattet. SQL Server kann Ressourcen in den folgenden Modi sperren: Gemeinsam (Shared). Gemeinsame Sperren werden für Lesezugriffe, wie SELECT, benutzt. Diese Sperren erlauben einen konkurrierenden Zugriff auf Ressourcen. Andere Transaktionen können gemeinsam gesperrte Bereiche aber nicht verändern. Wann eine gemeinsame Sperre wieder freigegeben wird, hängt von der eingestellten Isolationsstufe ab. Die Sperre kann sowohl unmittelbar nach der Beendigung der Operation als auch erst am Ende der Transaktion aufgehoben werden. Kapitel 5.4 geht hierauf nochmals ein. Exklusiv (EXclusive). Diese Sperre wird für Datenänderungsoperationen wie INSERT, UPDATE und DELETE benötigt. Die Sperre verhindert, dass mehrere Transaktionen gleichzeitig Daten modifizieren können. Leseoperationen auf den gesperrten Bereichen sind möglicherweise erlaubt, auch dies ist wiederum abhängig von der eingestellten Transaktions-Isolationsstufe. Aktualisierung (Update). Aktualisierungssperren werden verwendet, um die Wahrscheinlichkeit von Deadlocks (siehe Abschnitt 5.3.3) zu minimieren. Wenn zwei Transaktionen gleichzeitig zunächst Daten lesen, um diese dann anschließend zu verändern, so wird für beide Transaktionen am Beginn nur eine gemeinsame Sperre benötigt. Für die Aktualisierung muss diese Sperre dann allerdings in eine exklusive Sperre überführt werden. Die exklusive Sperre kann aber von keiner Transaktion gesetzt werden, da bereits eine gemeinsame Sperre auf dem Bereich existiert (siehe auch Tabelle 5.1 weiter unten). Beide Transaktionen müssen nun gegenseitig darauf warten, dass die jeweils andere die gemeinsame Sperre wieder auflöst, was allerdings nie passiert. Die Transaktionen befinden sich in einer endlosen Warteschleife. Aktualisierungssperren verhindern dies, da eine Aktualisierungssperre auf einer Ressource nur von einer Transaktion zurzeit benutzt werden kann. Beabsichtigt (Intent). SQL Server benutzt beabsichtigte Sperren, um anzuzeigen, dass bestimmte Ressourcen von Transaktionen demnächst in bestimmten Modi gesperrt werden. Durch beabsichtigte Sperren entwickelt der SQL Server eine Art vorausschauendes Sperrverhalten. Auf diese Weise können laufende Transaktionen verhindern, dass andere Transaktionen auf Ressourcen zugreifen, die in der laufenden Transaktion benötigt werden. Beabsichtigte Sperren werden im Rahmen der Lock Escalation benutzt, um eine Sperrhierarchie zu erzeugen. SQL Server kann so zum Beispiel anzeigen, dass demnächst Seiten gesperrt werden, wo jetzt noch Zeilensperren verwendet werden. Beabsichtigte Sperren können in drei unterschiedlichen Kategorien auftreten:
217
5 Transaktionen und Sperren Beabsichtigt gemeinsam (Intent Shared). Zeigt an, dass eine gemeinsame Sperre auf einem Bereich geplant ist, und verhindert so den Zugriff auf die diesem Bereich untergeordneten Elemente. Beabsichtigt exklusiv (Intent EXclusive). Zeigt an, dass eine exklusive Sperre auf einem Bereich geplant ist. Gemeinsam, aber exklusiv beabsichtigt (Shared with Intent EXclusive). Zeigt an, dass eine gemeinsame Sperre auf einem untergeordneten Bereich demnächst in eine exklusive Sperre in einem übergeordneten Bereich konvertiert wird. Katalog. Eine Katalogsperre wird während der Ausführung von Operationen, die von der Kataloginformation abhängen, benötigt. Es gibt zwei Typen von Katalogsperren: Katalogänderung (Schema-Modification). DDL-Operationen wie zum Beispiel ALTER TABLE erfordern diese Sperre, um zum Beispiel zu verhindern, dass andere Transaktionen auf die gerade in der Änderungsoperation befindliche Tabelle zugreifen. Katalogstabilität (Schema-Stability). Diese Sperre wird während der Übersetzung von Abfragen benötigt. Sie blockiert keine anderen Sperren, auch keine exklusiven Sperren. Lediglich DDL-Operationen, die auf den gesperrten Bereich zugreifen möchten, werden nicht zugelassen. Die fett hervorgehobenen Buchstaben in der englischsprachigen Bezeichnung kennzeichnen hier jeweils die offizielle Abkürzung, wie sie zum Beispiel in der SQL ServerDokumentation, in der Spalte request_mode der dynamischen Verwaltungssicht und auch in der Spalte Anforderungsmodus des Aktivitätsmonitors (siehe Abbildung 5.3) verwendet wird. Die unterschiedlichen Sperrtypen haben alle zum Ziel, gemeinsame Sperren auf Ressourcen zuzulassen und somit mehreren Transaktionen gleichzeitig Zugriff auf dieselbe Ressource zu gestatten. Dadurch werden Wartezustände minimiert und die Parallelverarbeitung von Transaktionen verbessert. Hierzu wird der Begriff der Sperrkompatibilität eingeführt. Die Sperrkompatibilität gibt Auskunft darüber, welche Sperren gemeinsam verwendet werden können. Tabelle 5.2 enthält eine Übersicht hierzu. Dort können Sie zum Beispiel entnehmen, dass gemeinsame (S) Sperren, die von SELECT-Anweisungen aufrechterhalten werden, zueinander kompatibel sind. Dadurch sind lesende Zugriffe durch SELECTs von unterschiedlichen Transaktionen gleichzeitig möglich. Die Tabelle zeigt auch, dass exklusive Sperren, die zum Beispiel während UPDATE, INSERT und DELETE verwendet werden, zu keinem anderen Modus kompatibel sind (darum ja auch der Name Exklusiv). Somit können zum Beispiel während einer UPDATE-Operation keine anderen Datenänderungen auf demselben Bereich stattfinden, Datenänderungsoperationen auf denselben Daten werden immer nacheinander ausgeführt.
218
5.3 Sperren Tabelle 5.2 Kompatibilität von Sperren Existierende Sperre Anforderungsmodus
IS
S
U
IX
SIX
X
Beabsichtigt gemeinsam Intent Shared (IS)
Ja
Ja
Ja
Ja
Ja
Nein
Gemeinsam Shared (S)
Ja
Ja
Ja
Nein
Nein
Nein
Aktualisierung Update (U)
Ja
Ja
Nein
Nein
Nein
Nein
Beabsichtigt exklusiv Intent Exclusive (IX)
Ja
Nein
Nein
Ja
Nein
Nein
Gemeinsam, aber exklusiv beabsichtigt Shared with Intent Exclusive (SIX)
Ja
Nein
Nein
Nein
Nein
Nein
Exklusiv Exclusive (X)
Nein
Nein
Nein
Nein
Nein
Nein
Jede Operation, also jedes SQL-Kommando, wird letztlich Ressourcen in der Datenbank in einem bestimmten Modus sperren. Hierbei sind den unterschiedlichen Kommandos, wie zum Beispiel SELECT, UPDATE und INSERT, standardmäßig sowohl Sperrtypen als auch die Dauer der Sperre zugeordnet. Wann immer Sie also ein SELECT-Kommando verwenden, wird zum Beispiel eine gemeinsame Sperre benutzt, die den abgefragten Bereich vor Änderungen während der SELECT-Ausführung schützt. Diese Sperre wird sofort nach Beendigung des SELECT wieder aufgehoben. Als Datenbankentwickler können Sie aber das Standardverhalten bezüglich Dauer und Art und Art der Sperre in einem gewissen Rahmen modifizieren. Hierzu können Sie sowohl die Isolationsstufe für Transaktionen verändern als auch für einzelne Abfragen Sperrhinweise verwenden. Mit den Isolationsstufen beschäftigt sich Abschnitt 5.4. Wie Sie Sperrhinweise verwenden, erfahren Sie dann im Abschnitt 5.5. Zunächst aber müssen wir noch eine Besonderheit besprechen, die im Zusammenhang mit Sperrmechanismen auftreten können: die sogenannten Deadlocks. Der folgende Abschnitt beschäftigt sich hiermit.
5.3.3
Deadlocks
Ein Deadlock bezeichnet einen Zustand, an dem mehrere Transaktionen beteiligt sind, die gegenseitig auf die Freigabe von gesperrten Ressourcen von anderen Transaktionen warten. Abbildung 5.4 veranschaulicht dies am Beispiel von drei Transaktionen.
219
5 Transaktionen und Sperren
Abbildung 5.4 Ein Deadlock mit drei beteiligten Transaktionen
Die Transaktion T1 sperrt eine Ressource A und wartet ihrerseits auf eine Ressource B, die derzeit von Transaktion T2 gesperrt wird. T2 wiederum wartet auf die Ressource C, die aber derzeit von T3 gesperrt ist. Schließlich kann auch T3 nicht beendet werden, da die Ressource A benötigt wird, die aber gerade von T1 gesperrt ist. Die drei Transaktionen warten somit in einer Schleife gegenseitig auf die Freigabe von Ressourcen. Deadlocks können generell nicht vermieden werden. SQL Server hat allerdings einen Deadlock-Monitor, der Deadlocks erkennt. Dieser Deadlock-Monitor wird aus den an einem Deadlock beteiligten Transaktionen nach einer bestimmten Zeit ein sogenanntes Deadlock-Opfer auswählen und diese Transaktion abbrechen. Die initiale Wartezeit hierfür ist fünf Sekunden. Erkennt der Deadlock-Monitor allerdings viele Deadlocks, so kann diese Zeit auf bis zu 100 ms abgesenkt werden. Treten dann wieder weniger Deadlocks auf, so wird diese Wartezeit schrittweise wieder erhöht, bis sie erneut fünf Sekunden beträgt. Sie können den Deadlock-Monitor einmal in Aktion erleben, indem Sie die folgenden beiden Abfragen in jeweils einer eigenen Verbindung zur Datenbank AdventureWorks kurz nacheinander starten: Listing 5.1 Einen Deadlock erzeugen
220
5.3 Sperren
Die erste Transaktion sperrt die Zeile für , die zweite sperrt die Zeile für . Die erste Transaktion wartet dann auf die Freigabe der Sperre für , während die zweite Transaktion auf die Zeile wartet. Beide Transaktionen warten somit auf die Freigabe von gegenseitig gesperrten Ressourcen ein Deadlock tritt auf. Der Deadlock-Monitor erkennt dies und bricht eine der beiden Transaktionen ab. Nach einer Weile werden Sie im Ergebnisbereich des Abfragefensters einer der beiden Transaktionen folgende Fehlermeldung erhalten:
Sie können Deadlocks auch im SQL Server Profiler grafisch darstellen. Hierzu starten Sie den Profiler und öffnen eine neue Ablaufverfolgung. Auf der Registerkarte Allgemein wählen Sie in der Combobox Vorlage verwenden als Vorlage Leer aus. Wechseln Sie dann in die Registerkarte Ereignisauswahl, und markieren Sie sowohl die Optionen Alle Ereignisse anzeigen als auch Alle Spalten anzeigen. Unter Events wählen Sie dann bitte die Ereignisklasse Locks und dort das Ereignis Deadlock Graph aus (Abbildung 5.5):
Abbildung 5.5 Ablaufverfolgung mit Deadlock Graph
Starten Sie anschließend die Ablaufverfolgung, und führen Sie die beiden Transaktionen aus Listing 5.1 erneut aus. Nach einer Weile sollten Sie in Ihrer Ablaufverfolgung das in Abbildung 5.6 gezeigte Bild sehen.
221
5 Transaktionen und Sperren
Abbildung 5.6 Deadlock Graph für Listing 5.1
Die grafische Darstellung zeigt sehr schön den Zyklus in den Wartezuständen. Es ist auch zu sehen, dass die linke Transaktion mit der Prozess-ID 54 als Deadlock-Opfer auserkoren und abgebrochen wurde. Der Deadlock-Monitor ist ein Schutzmechanismus von SQL Server. Durch ihn wird verhindert, dass Sperren, die sowieso niemals mehr nützlich sein können, wieder freigegeben werden. Auf diese Weise wird dafür gesorgt, dass Ressourcen nicht unnötig blockiert werden. Für den Datenbankentwickler ist dieser Schutzmechanismus allein nicht ausreichend. Der Mechanismus sorgt ja nur dafür, dass Sie stets damit rechnen müssen, dass Transaktionen, die sich aus welchen Gründen auch immer in einer Deadlock-Situation befinden, einfach abgebrochen werden. Als Datenbankentwickler müssen Sie eventuell auf derartige Situationen vorbereitet sein und entsprechend reagieren. Im T-SQL-Programmcode können Sie das unter Verwendung der Möglichkeit zur strukturierenden Ausnahmebehandlung folgendermaßen erledigen: Listing 5.2 Deadlocks im Programmcode abfangen
Der obige Code verpackt den durch den Deadlock gefährdeten Bereich einfach in einen BEGIN TRY
END TRY-Block. Im CATCH-Bereich wird zunächst einmal die Transaktion zurückgesetzt. Über die Funktion ERROR_NUMBER(), die jeweils den letzten aufgetretenen Fehler zurückliefert, dann überprüft, ob der aufgetretene Fehler 1205 ist. Dies ist die Fehlernummer für Transaktionen, die als Deadlock-Opfer ausgewählt wurden (siehe die Fehlermeldung weiter oben bei unserem ersten Versuch). Ist dies der Fall, so wird die
222
5.3 Sperren Transaktion über das GOTO-Kommando einfach nochmals begonnen, nachdem der Text der Fehlermeldung zur Information ausgegeben wurde. Diesen Text könnten Sie natürlich ebenso zur Protokollierung des aufgetretenen Deadlocks in einem eigenen Logbuch verwenden. Wenn Sie beide Transaktionen aus Listing 5.1 derart in TRY/CATCH-Blöcke einbetten, wie oben gezeigt, wird der entstehende Deadlock nun aufgefangen. Beide Transaktionen werden dadurch erfolgreich beendet. Falls Sie die oben gestartete Ablaufverfolgung noch geöffnet hatten, können Sie übrigens auch für den letzten Versuch den Deadlock in grafischer Form sehen. Dieser ist ja auch tatsächlich aufgetreten, er wurde eben nur im Programmcode aufgefangen und entsprechend behandelt. Die seit dem letzten Start von SQL Server aufgetretenen Deadlocks werden übrigens auch vom Server selbst protokolliert. Sie können sich diese über die dynamische Verwaltungssicht anzeigen lassen:
Über die WHERE-Bedingung werden hier nur die Ereignisse herausgefiltert, die in ihrem Namen die Zeichenkette dead enthalten. Als Ergebnis erhalten Sie ungefähr so etwas:
Abbildung 5.7 Deadlock-Protokoll des Servers
In der markierten Zeile 13 sehen Sie die Anzahl der seit dem Start von SQL Server aufgetretenen Deadlocks. Generell können Deadlocks nicht verhindert werden. Sie können Ihren T-SQL-Code aber so entwerfen, dass Sie die Möglichkeit für das Auftreten von Deadlocks minimieren. Kapitel 5.7 wird Ihnen hierzu einige Hinweise geben. Zunächst jedoch wollen wir uns mit den Isolationsstufen beschäftigen, mit denen eingestellt werden kann, wie Transaktionen gegeneinander abgegrenzt werden.
223
5 Transaktionen und Sperren
5.4
Isolationsstufen Die unterschiedlichen Isolationsstufen ermöglichen es Ihnen, das Isolationsverhalten Ihrer Transaktionen zu kontrollieren. Die Isolationsstufen bestimmen die Abgrenzung von Leseund Schreiboperationen gegeneinander, steuern also den konkurrierenden Zugriff von Lese- und Schreiboperationen auf gemeinsame Datenbestände. Nur Schreiboperationen sind immer voneinander isoliert, genauso wie nur Leseoperationen immer gleichzeitig durchgeführt werden es sei denn, Sie verwenden für Ihre Abfragen spezielle Optionen, um ein anderes Verhalten zu erzwingen. Kapitel 5.5 geht auf Sperrhinweise für Abfragen näher ein. SQL Server kennt fünf unterschiedliche Isolationsstufen, mit denen Sie das Verhalten von Transaktionen beeinflussen können. Diese Isolationsstufen und ihre Auswirkungen auf Transaktionen werden in den folgenden Abschnitten erörtert. Die einzelnen Isolationsstufen werden jeweils durch das Kommando
eingestellt, wie Sie in den aufgeführten Beispielen erkennen werden.
5.4.1
READ UNCOMMITTED
Dies ist die niedrigste Isolationsstufe. Eine Transaktion kann noch nicht bestätigte Schreibvorgänge einer anderen Transaktion lesen. Dadurch ist es möglich, dass die erste Transaktion Daten liest, die nach Abschluss der zweiten Transaktion nicht mehr existieren. Dies sind sogenannte Dirty Reads. Tabelle 5.3 veranschaulicht dieses Verhalten, wobei Sie sich bitte vorstellen, dass die Zeitachse von oben nach unten läuft. Die SELECT-Anweisung in Transaktion 1 wird nach dem UPDATE-Kommando durchgeführt. Durch die Isolationsstufe READ UNCOMMITTED wird dieses SELECT keine gemeinsame Sperre anfordern. Die exklusive Sperre, die durch das UPDATE für aufrechterhalten wird, kollidiert daher nicht mit dem SELECT. Das SELECT gibt dadurch den Wert Betty zurück. Transaktion 2 wird nun zurückgesetzt, dadurch existiert in der Tabelle Person.Contact wieder der vorherige Wert für . Transaktion 1 verwendet nun einen Wert, der nicht in der Tabelle Person.Contact existiert. Auch die durch das INSERT-Kommando aus Transaktion 2 hinzugefügte Zeile kann in Transaktion 1 verwendet werden, was ebenfalls zu Dateninkonsistenzen führen kann. Für ein eventuell ausgeführtes DELETE gilt diese Aussage analog: Für Transaktion 1 würden die gelöschten Zeilen nicht existent sein, obwohl diese nach Abschluss von Transaktionen durch ROLLBACK wieder vorhanden sind. Dieses Phänomen mit hinzugefügten oder nicht vorhandenen Zeilen wird auch als Phantom Read bezeichnet.
224
5.4 Isolationsstufen Tabelle 5.3 Transaktion 1
Transaktion 2
5.4.2
READ COMMITTED
Dies ist die Standardeinstellung für SQL Server-Verbindungen. Solange Sie die Isolierungsstufe nicht explizit ändern, arbeitet Ihre Verbindung im READ COMMITTEDModus. In diesem Modus können lesende Transaktionen nur Daten von abgeschlossenen Transaktionen erhalten. Anders gesagt: Eine Leseoperation muss warten, bis die Datenänderungen von der anderen Transaktionen bestätigt wurden. Schauen Sie sich bitte hierzu das Beispiel in Tabelle 5.4 an. Das UPDATE-Kommando in Transaktion 2 errichtet eine exklusive Sperre für die Zeile . Das SELECT-Kommando von Transaktion 1 kann nun keine gemeinsame Sperre für diese Zeile erhalten. Transaktion 1 muss warten, bis Transaktion 2 abgeschlossen wurde. Erst nach dem ROLLBACK von Transaktion 2 wird das SELECT-Kommando von Transaktion 1 ausgeführt, denn erst jetzt kann dieses SELECT eine gemeinsame Sperre für die Zeile erhalten. Das SELECT liest nun den nach dem ROLLBACK aktuellen Wert aus der Spalte FirstName aus. Dirty Reads sind im Modus READ COMMITTED somit nicht möglich. Allerdings erkauft man diesen Vorteil in Bezug auf die Datenkonsistenz durch den Nachteil, dass hier Wartezustände auftreten Transaktion 1 muss ja auf die Beendigung von Transaktion 2 warten.
225
5 Transaktionen und Sperren Tabelle 5.4 Beispiel zu READ COMMITTED Transaktion 1
Transaktion 2
5.4.3
REPEATABLE READ
Die beiden oben erläuterten Isolierungsstufen erlauben beide nicht wiederholbare Lesevorgänge. Schauen Sie sich hierzu bitte zunächst den Code in der folgenden Tabelle an. Tabelle 5.5 Nicht wiederholbare Lesevorgänge Transaktion 1
Transaktion 2
226
5.4 Isolationsstufen Transaktion 1 liest den Wert der Spalte FirstName für die Zeile . Das SELECT-Kommando fordert hierzu eine gemeinsame Sperre an, die aber nur für die Dauer dieses Kommandos gehalten wird. Das anschließende UPDATE in Transaktion 2 kann daher ausgeführt werden, da die hierfür erforderliche exklusive Sperranforderung erfolgreich ist. Wenn Transaktion 1 nun, nach dem COMMIT von Transaktion 2, die SELECT-Anweisung wiederholt, so wird ein anderer Wert zurückgegeben. Um in einer Transaktion wiederholbare Lesevorgänge zu ermöglichen, kann die Isolationsstufe auf REPEATABLE READ gesetzt werden. Hierzu ändern Sie die erste Codezeile für Transaktion 1 einfach in:
Wenn Sie den obigen Code nun erneut ausführen, so wird durch das erste SELECTKommando in Transaktion 1 eine gemeinsame Sperre für die Zeile erzeugt. Diese Sperre wird nun aber nicht unmittelbar nach dem Beenden der SELECT-Anwesung wieder aufgehoben, sondern bis zum Ende der Transaktion aufrechterhalten. Das UPDATE von Transaktion 2 kann daher erst ausgeführt werden, nachdem Transaktion 1 abgeschlossen wurde, Transaktion 2 muss also warten.
5.4.4
SNAPSHOT
Diese Isolationsstufe ist neu im SQL Server 2005. Die SNAPSHOT-Isolation wird durch eine Zeilenversionsverwaltung ermöglicht. Aktualisierungskommandos erzeugen in dieser Isolationsstufe zunächst Kopien der von der Aktualisierung betroffenen Zeilen. Alle Kommandos, die Daten lesen, also im Allgemeinen SELECTs, holen die Daten dann nicht aus den Zeilen der betroffenen Tabellen, sondern aus den angelegten Kopien der Zeilen. Dadurch sind Blockierungen zwischen Lese- und Schreibvorgängen prinzipiell nicht möglich. Betrachten Sie hierzu bitte noch einmal Tabelle 5.4. In dem dort gezeigten Beispiel muss das SELECT in Transaktion 1 auf das Ende von Transaktion 2 warten, bevor es ausgeführt werden kann. In der Isolierungsstufe SNAPSHOT wird dieser Wartezustand vermieden. Das UPDATE in Transaktion 2 legt eine Kopie der betroffenen Zeilen an, das SELECT erhält dann die Daten dieser Kopie. Diese Kopien enthalten die Daten für die Zeilen, wie sie vor dem UPDATE waren. Das SELECT liest also konsistente Daten, ohne warten zu müssen. Sie müssen die SNAPSHOT-Isolierungsstufe allerdings auch bezahlen. Die hierfür erforderliche Zeilenversionsverwaltung benötigt natürlich Ressourcen. Die Zeilenkopien werden in der tempdb gespeichert, die ja auf jedem Server nur einmal vorhanden ist. Alle auf einem SQL Server existierenden Datenbanken müssen sich also die tempdb teilen. Wenn Sie viele Datenbanken haben, welche die SNAPSHOT-Isolierung verwenden, so kann die gemeinsam genutzte tempdb ein echter Performance-Engpass werden. Auch aus diesem Grund können Sie die SNAPSHOT-Isolierungsstufe erst benutzen, wenn Sie Ihre Datenbank hierfür konfiguriert haben. Ein Skript wie zum Beispiel:
227
5 Transaktionen und Sperren wird daher eventuell diesen Fehler zurückliefern:
Sie müssen also, um die SNAPSHOT-Isolationsstufe verwenden zu können, zunächst die Datenbank hierfür konfigurieren. Dafür benutzen Sie ALTER DATABASE:
Erst danach können Sie die Isolierungsstufe SNAPSHOT verwenden. Erinnern Sie sich bitte noch einmal daran, dass die Isolierungsstufe READ_COMMITTED die Standardeinstellung für SQL Server-Verbindungen ist. Sie können READ COMMITTED auch in Zusammenhang mit SNAPSHOT als Standard festlegen. Auch hierfür verwenden Sie ALTER DATABASE:
Wenn Sie Ihre Datenbank so konfigurieren, dann verwendet diese nach wie vor die READ COMMITTED-Isolationsstufe, erweitert diese allerdings um die SNAPSHOTZeilenversionsverwaltung. Hierdurch haben Sie die Möglichkeit, Blockierungen von Leseund Schreibvorgängen zu reduzieren, ohne dass Sie dafür irgendetwas programmieren müssen. Dies dürfte vor allem für den Umstieg von einer Vorgängerversion des SQL Servers interessant sein, da in diesem Fall wie gesagt keinerlei Änderungen am Anwendungscode erforderlich sind. Schauen Sie sich bitte noch einmal Tabelle 5.4 an. Nach der Ausführung der beiden oben gezeigten ALTER DATABASE-Kommandos muss Transaktion 1 nicht mehr auf die Beendigung von Transaktion 2 warten. Sie können den Code genau so wie in der Tabelle dargestellt ausführen, es treten nun keine Blockierungen mehr auf.
5.4.5
SERIALIZABLE
Wie der Name bereits sagt, wird durch diese Isolationsstufe eine serielle Verarbeitung erzwungen. Alle Sperren werden grundsätzlich bis zum Ende der jeweiligen Transaktion aufrechterhalten. Dadurch können Lese- und Schreibzugriffe auf gemeinsamen Datenbereichen niemals auftreten lediglich Leseoperationen, die ja nur den Sperrtyp Gemeinsam benutzen, können gleichzeitig ausgeführt werden. Diese Isolationsstufe bietet die größte Datensicherheit, ist aber natürlich auch mit den meisten Wartezuständen verbunden.
5.4.6
Abschließende Betrachtungen zu Isolationsstufen
Isolationsstufen sind ein wesentliches Konzept, das Sie als Datenbankentwickler verstehen und beherrschen sollten. Generell können Sie für Ihre Verbindungen eine niedrige oder eine hohe Isolationsstufen wählen beide haben jeweils ihre Vor und Nachteile. Eine niedrige Isolationsstufe erzeugt weniger Blockierungen, erhöht aber auch die Gefahr möglicher Inkonsistenzen. Eine hohe Isolationsstufe gewährleistet einen hohen Grad an Konsistenz,
228
5.5 Sperren explizit anfordern Sperrhinweise führt aber auch zu vielen Blockierungen und den damit verbundenen Wartezuständen. Zusätzlich erhöht sich in diesem Fall auch die Wahrscheinlichkeit von Deadlocks. Tabelle 5.6 fasst noch einmal die in den unterschiedlichen Isolationsstufen möglichen nomalien zusammen. Tabelle 5.6 Mögliche Anomalien der unterschiedlichen Isolationsstufen Mögliche Anomalien Dirty Read
Nicht wiederholbarer Lesevorgang
Phantomwerte
Update-Konflikt
READ TED
Ja
Ja
Ja
Ja
READ COMMITTED
Nein
Ja
Ja
Ja
REPEATABLE READ
Nein
Nein
Ja
Nein
SNAPSHOT
Nein
Nein
Nein
Ja
SERIALIZABLE
Nein
Nein
Nein
Nein
Isolationsstufe
Die Spalte Update-Konflikt in der obigen Tabelle bezieht sich darauf, dass eine Transaktion Datenänderungen durchführt, die auf zuvor gelesenen Daten basieren. Dies kann zunächst einmal immer dann passieren, wenn ein nicht wiederholbarer Lesevorgang möglich ist. Zusätzlich ist eine solche Anomalie aber auch in der Isolationsstufe SNAPSHOT möglich. Da in der SNAPSHOT-Isolationsstufe die Lesevorgänge aus einer Kopie geholt werden, kann es vorkommen, dass die Daten dieser Kopie nicht mehr aktuell sind, wenn sie irgendwann im Verlauf der Transaktion für eine Datenänderung benutzt werden Es ist ja denkbar, dass eine andere Transaktion genau die der Kopie zugrunde liegenden Daten inzwischen verändert hat. Die Version der Zeilenkopie, die zur Änderung herangezogen wird, würde dann nicht mehr mit dem aktuellen Datenbestand übereinstimmen. In so einem Fall würden die anhand der Zeilenkopien durchgeführten Änderungen eventuell nicht korrekt sein.
5.5
Sperren explizit anfordern Sperrhinweise In den vorangegangenen Abschnitten haben Sie gesehen, wie der SQL ServerSperrmechanismus im Rahmen von Transaktionen funktioniert. Sie haben erfahren, dass SQL-Anweisungen angemessene Sperren nach sich ziehen, die automatisch angefordert werden. Art und Dauer der Sperre sind hierbei abhängig von der eingestellten Isolationsstufe der Transaktion. Falls Sie innerhalb einer Transaktion von dem durch die Isolationsstufe festgelegten Standard für Sperren abweichen möchten, so können Sie für einzelne SQL-Anweisungen
229
5 Transaktionen und Sperren Sperrhinweise angeben. Hierzu kennt T-SQL die WITH Klausel für die Anweisungen SELECT, UPDATE, INSERT und DELETE, die Sie wie folgt verwenden können:
Ein Sperrhinweis kann also für jede beteiligte Tabelle angegeben werden. Der Hinweis wird direkt nach dem Tabellennamen oder, wenn die Abfrage einen Aliasnamen für die Tabelle verwendet, nach dem Aliasnamen angegeben. Das Schlüsselwort WITH ist für die hier im Weiteren besprochenen Sperrhinweise übrigens optional, muss also nicht angegeben werden. Es reicht auch aus, die Sperrhinweise (es können mehrere angegeben werden) einfach im Klammern, durch Komma getrennt, anzugeben. Ein Sperrhinweis kann nützlich sein, um die Isolationsstufe temporär heraufzusetzen. Stellen Sie sich z .B. vor, dass für Ihre Anwendung die Isolationsstufe READ COMMITTED generell ausreichend ist, Sie aber innerhalb einer Transaktion eine SELECT-Anweisung verwenden, die ein wiederholbares Lesen, also die REPEATABLE READ-Isolationsstufe, benötigt. Anstatt nun für die gesamte Transaktion die Isolationsstufe hochzusetzen, was ja nur unnötige Sperren und somit Blockierungen erzeugen würde, können Sie für dieses einzelne SELECT-Kommando den Sperrhinweis REPEATABLE READ verwenden und somit die angeforderten Sperren minimieren. Sperrhinweise können in zwei Kategorien unterteilt werden: Granularitätshinweise. Durch diese Hinweise bestimmen Sie die Elemente, die gesperrt werden. Hierfür stehen Ihnen die folgenden Optionen zur Verfügung: ROWLOCK. Für die Tabelle werden Sperren auf Zeilenebene angefordert. PAGLOCK. Für die Tabelle werden Seitensperren angefordert. TABLOCK. Für die gesamte Tabelle wird eine gemeinsame Sperre angefordert. TABLOCKX. Für die gesamte Tabelle wird eine exklusive Sperre angefordert. Bitte beachten Sie, dass Granularitätshinweise nicht den Lock-EscalationMechanismus des SQL Servers außer Kraft setzen. Wenn Sie also zum Beispiel für eine Tabelle ROWLOCK angeben, so wird SQL Server zunächst mit Zeilensperren beginnen, kann aber dann in weiteren Verlauf durchaus auch Seiten oder die gesamte Tabelle sperren. Isolationsstufenhinweise. Isolationsstufenhinweise ermöglichen Ihnen die Einstellung der TransaktionsIsolationsstufe für einzelne Tabellen in SQL-Anweisungen. Hierfür haben Sie die folgenden Möglichkeiten:
230
5.5 Sperren explizit anfordern Sperrhinweise READUNCOMMITTED. Durch diese Option wird die Isolationsstufe für die Tabelle auf READ UNCOMMITTED gesetzt. NOLOCK. NOLOCK ist lediglich ein Synonym für READUNCOMMITTED. READCOMMITTED. Hierdurch wird die Isolationsstufe für die Tabelle auf READ COMMITTED gesetzt. REPEATABLEREAD. Die Option stellt die Isolationsstufe auf REPEATABLE READ. SERIALIZABLE. Dieser Sperrhinweis setzt die Isolationsstufe auf SERIALIZABLE. HOLDLOCK. HOLDLOCK ist ein Synonym für SERIALIZABLE. Generell können Sie in einem Sperrhinweis jeweils eine Option aus jeder der beiden Kategorien angeben, die Sie durch ein Komma trennen.1 Hierbei sind jedoch nicht alle Kombinationen sinnvoll und daher auch nicht erlaubt. Wenn Sie widersprüchliche Sperrhinweise angeben, wie zum Beispiel die Kombination von READUNCOMMITTED und TABLOCKX, so erhalten Sie eine entsprechende Fehlermeldung:
Außerdem sind nicht alle Sperrhinweise für alle SQL-Kommandos zulässig. Aktualisierungskommandos können zum Beispiel nicht mit READUNCOMMITTED oder NOLOCK verwendet werden. Versuchen Sie dies, so erhalten Sie ebenfalls eine Fehlermeldung:
Betrachten wir noch einmal das UPDATE-Kommando, das alle Listenpreise um 3% heraufsetzt. Dieses Kommando verändert Daten in jeder Tabellenzeile, daher könnten wir SQL Server hier auch gleich mitteilen, dass die gesamte Tabelle gesperrt werden soll:
Möglicherweise würde dies den automatischen Sperrmechanismus von SQL Server entlasten. Aber ist dies auch wirklich so? Was ist zum Beispiel mit Zeilen, in denen der Wert für ListPrice NULL oder 0 ist? Diese Zeilen würden durch das UPDATE ja nicht verändert. Sie könnten also aus dem UPDATE ausgeschlossen und bräuchten daher auch niemals gesperrt zu werden. Möglicherweise bietet die aktuelle Version von SQL Server dieses Sperrverhalten noch nicht, aber zukünftige Versionen könnten ja eine solche Sperrverwaltung enthalten. Durch den TABLOCKX-Hinweis würden Sie in so einem Fall die Granularität unnötig heraufsetzen.
1
Alternativ können Sie die Optionen auch durch Leerzeichen voneinander trennen. Dies wird jedoch nur noch aus Gründen der Abwärtskompatibilität zu früheren SQL Server-Versionen unterstützt. Für neuen T-SQL-Code sollten Sie daher die Kommatrennung verwenden.
231
5 Transaktionen und Sperren Abschließend möchte ich Ihnen daher noch einen Rat mit auf den Weg geben: Sperrhinweise sind etwas für wirklich erfahrene Datenbankentwickler. Versuchen Sie nach Möglichkeit, Sperrhinweise zu vermeiden, und verwenden Sie diese nur in einigen Ausnahmefällen, in denen Sie wirklich genau wissen, was Sie anrichten. Die vorangegangenen Abschnitte haben gezeigt, wie Sie Transaktionen in Ihrem Anwendungscode einsetzen können. SQL Server verwendet die Transaktionsverwaltung aber auch intern, um die Datenkonsistenz zu gewährleisten und sich gegen Systemausfälle zu schützen. Wie dies funktioniert, erfahren Sie im folgenden Kapitel.
5.6
Das Transaktionsprotokoll Wodurch ist eigentlich ein ROLLBACK möglich? Wenn in einer laufenden Transaktion ein Fehler auftritt und die Transaktion abgebrochen wird, können Sie über eine ROLLBACK-Anweisung ja genau den Zustand wieder herstellen, wie er vor dem Beginn der fehlerhaften Transaktion war. Wie verarbeitet der SQL Server Transaktionen, um so etwas sicherzustellen? SQL Server führt hierzu Protokoll über alle durchgeführten Transaktionen. Dies ist das Sie haben es sicher bereits erraten Transaktionsprotokoll des SQL Servers. In diesem Protokoll werden Beginn und Ende einer jeden Transaktion vermerkt. Das Protokoll enthält außerdem Einträge für alle während der Transaktion vorgenommenen Änderungen, und zwar jeweils den Zustand vor und nach der Änderung. SQL Server benutzt ein Write Ahead-Protokoll, um sicherzustellen, dass nur diejenigen Datenänderungen in die Datendateien einer Datenbank geschrieben werden, für die auch ein Protokolleintrag existiert. SQL Server verwaltet hierzu einen speziellen Pufferspeicher. Der erste Schritt bei Datenänderungsoperationen lädt die betroffenen Datenseiten in diesen Pufferspeicher. Die Änderung selber findet dann zunächst auf der im Puffer existierenden Kopie der Daten statt. Jede Änderung im Pufferspeicher erzeugt dann auch einen Eintrag in der Protokolldatei. Hierbei wird jeder Protokolleintrag mit einer eindeutigen Protokollsequenznummer, der LSN (Log Sequence Number), versehen. Einträge werden seriell in das Protokoll geschrieben, wobei jeder neue Eintrag eine höhere LSN erhält als der vorherige. Erst nachdem dieser Protokolleintrag erstellt wurde, können die im Puffer vorgenommenen Änderungen auch in die Datendatei übertragen werden. Anderenfalls könnte es vorkommen, dass die Datendatei Änderungen enthält, für die kein Protokolleintrag existiert. Für diese Änderungen wäre somit kein Rollback möglich. Das Schreiben der im Pufferspeicher geänderten Seiten in die Datendateien erfolgt erst bei Auftreten eines sogenannten Prüfpunktes (Checkpoint). Ein solcher Prüfpunkt wird von SQL Server automatisch in periodischen Abständen initiiert. Er wird aber zum Beispiel auch durch das Beenden einer SQL Server-Instanz, das Erstellen einer vollständigen Da-
232
5.6 Das Transaktionsprotokoll tenbanksicherung oder das Kommando ALTER DATABASE erreicht. Zusätzlich können Sie einen Prüfpunkt auch manuell durch Eingabe des Kommandos
erzwingen. Durch das Schreiben in die Datendatei werden die entsprechenden Seiten im Puffer geleert. Ein COMMIT bewirkt schließlich, dass die im Transaktionsprotokoll enthaltenen Protokolleinträge in die Datendatei geschrieben werden. Im Transaktionsprotokoll existieren somit drei unterschiedliche Typen von Protokolleinträgen: Vollständig verarbeitete Transaktionen. Alle erforderlichen Änderungen sind bereits in die Datendatei geschrieben worden. Der Eintrag in der Protokolldatei besteht aber weiterhin. Abgeschlossene Transaktionen. Dies sind diejenigen Transaktionen, für die im Protokoll bereits das Ende der Transaktion vermerkt wurde, die aber noch nicht in die Datendatei übertragen wurden. Nicht abgeschlossene Transaktionen. Dies sind Transaktionen, die gerade in Bearbeitung sind. Dieser Teil des Transaktionsprotokolls wird auch als aktiver Teil bezeichnet. Da das Transaktionsprotokoll seriell geschrieben wird, wobei jeder Eintrag durch eine LSN gekennzeichnet wird, ist der aktive Teil des Protokolls derjenige mit den größten LS. Anders gesagt: Es lässt sich eine minimale LSN bestimmen, an welcher der aktive Teil des Protokolls anfängt. Durch die oben geschilderte Verfahrensweise wird letztlich sichergestellt, dass nur abgeschlossene Transaktionen in die Datendatei geschrieben werden können. Dies ist sicherlich die hauptsächliche Aufgabe der Transaktionsprotokollverwaltung. Das Transaktionsprotokoll kann jedoch auch für andere Zwecke verwendet werden. Da es alle durchgeführten Datenbankänderungen enthält, kann es auch benutzt werden, um eben diese Änderungen in einer anderen als der ursprünglichen Datenbank einzuspielen. Die sich hieraus ergebenden Einsatzmöglichkeiten betreffen die Datensicherheit und die Verfügbarkeit: Einrichten von Replikationen. Eine Replikation kann mehrere Datenbanken aus unterschiedlichen Servern umfassen, die wechselseitig ihre Datenbestände synchronisieren. Als Basis für diese Synchronisation können die Transaktionsprotokolle der beteiligten Datenbanken dienen. Einrichten von Stand-by-Servern. SQL Server bietet hierzu den Transaktionsprotokollversand oder auch die Einrichtung von Datenbankspiegelungen auf der Basis des Transaktionsprotokolls.
5.6.1
Abschneiden des Protokolls
Die Verwaltung des Transaktionsprotokolls nach der gerade geschilderten Vorgehensweise lässt das Protokoll ständig anwachsen. SQL Server schneidet das Protokoll daher bei Auf-
233
5 Transaktionen und Sperren treten bestimmter Ereignisse ab. Wann das Abschneiden des Transaktionsprotokolls genau passiert, hängt vom gewählten Wiederherstellungsmodell ab: Beim einfachen Wiederherstellungsmodell wird das Protokoll beim Auftreten eines Prüfpunktes abgeschnitten, allerdings nur dann, wenn gerade keine BACKUPAnweisung aktiv ist. Das Protokoll wird immer abgeschnitten, wenn es über BACKUP LOG gesichert wird und hier nicht die Option NO TRUNCATE verwendet wurde. Das Abschneiden des Protokolls verkleinert übrigens nicht die Protokolldatei, sondern gibt nur den verwendeten Speicherplatz zur Benutzung durch andere Protokolleinträge wieder frei. Möchten Sie auch die Datei verkleinern, so müssen Sie dies über die Task Datenbank verkleinern explizit durchführen.
5.6.2
Verhalten im Fehlerfall
SQL Server verwendet das Transaktionsprotokoll auch, um den Durability-Aspekt des ACID-Prinzips zu gewährleisten. Hierzu wird bei jedem Start einer SQL Server-Instanz eine automatische Wiederherstellung aller Datenbanken ausgeführt. Für jede im Transaktionsprotokoll aufgezeichnete vollständige Datenänderung (also abgeschlossene Transaktion), die noch nicht in die Datendateien übernommen wurde, wird zunächst ein Rollforward ausgeführt, diese Änderungen werden also während der Wiederherstellung aus dem Transaktionsprotokoll in die Datendateien übertragen. Anschließend wird für den aktiven Teil des Transaktionsprotokolls, der ja alle Transaktionen, die noch nicht abgeschlossen werden konnten, enthält, ein Rollback ausgeführt. Durch dieses Verhalten ist sichergestellt, dass der Datenbestand sich stets in einem konsistenten Zustand befindet. Es können keine verlorenen Transaktionen existieren, die Daten in einem undefinierten oder teilweise veränderten Zustand zurücklassen.
5.7
Hinweise zur Verwendung von Transaktionen Nach dem bisher Gesagten können wir zu Transaktionen zwei grundlegende Erklärungen abgeben: 1. Transaktionen sind erforderlich und hilfreich, denn sie stellen die Datenkonsistenz sicher. 2. Transaktionen sind ein Problem, denn sie erzeugen Blockierungen und Wartezustände. Da Sie nun wissen, wie die Transaktionsverarbeitung von SQL Server funktioniert, werden Sie der ersten Aussage sicherlich uneingeschränkt zustimmen. Es ist die zweite Aussage, die Sie als Datenbankentwickler in die Verantwortung nimmt. Sie sollten stets versuchen, Ihren T-SQL-Code so zu entwerfen, dass Blockierungen und Wartezustände möglichst minimal sind. Um dies zu erreichen, versuchen Sie bitte die folgenden Hinweise zu beherzigen:
234
5.7 Hinweise zur Verwendung von Transaktionen Halten Sie Ihre Transaktionen möglichst kurz. Wenn möglich, spalten Sie große Transaktionen, die viele Zeilen verändern, in mehrere kleinere Transaktionen auf. Statt also zum Beispiel zu schreiben:
könnten Sie auch so vorgehen:
Eine solche Verfahrensweise ist allerdings nur dann möglich, wenn es aus Datenkonsistenzsicht auch erlaubt ist. Die obige Änderung des Listenpreises in drei hintereinander ablaufende Transaktionen kann natürlich zu Inkonsistenzen führen. Vermeiden Sie auch Transaktionen, in deren Verlauf Benutzerinteraktionen erforderlich oder möglich sind. Eine Benutzerin könnte entscheiden, nach Transaktionsstart zunächst in eine Besprechung zu gehen und erst danach weiterzuarbeiten. Dies hätte zur Folge, dass alle Sperren so lange aufrechterhalten werden, bis die Benutzerin ihre Arbeit beendet. Halten Sie Verbindungen zur Datenbank nur so lange aufrecht, wie Sie diese tatsächlich zum Lesen oder Ändern von Daten benötigen. Minimieren Sie die Wahrscheinlichkeit von Deadlocks. Deadlocks können grundsätzlich nicht vermieden werden, Sie können Ihren Code aber so gestalten, dass Deadlocks minimiert werden. Hierzu sollten Sie zunächst Rat Nummer 1 befolgen: Halten Sie die Transaktionen kurz. Zusätzlich sollten Sie die Anweisungen innerhalb Ihrer Transaktionen immer in derselben Reihenfolge verwenden. Betrachten Sie bitte noch einmal unser Beispiel aus Listing 5.1. Hätten wir hier die UPDATE-Anweisungen in beiden Transaktionen in derselben Reihenfolge abgegeben, so wäre der Deadlock nicht aufgetreten. Wählen Sie die Isolationsstufe nur so hoch wie nötig und so niedrig wie möglich. Eine hohe Isolationsstufe erzeugt mehr Sperranforderungen und somit auch mehr Blockierungen. Im Allgemeinen ist die Isolationsstufe READ_COMMITTED mit ALLOW_SNAPSHOT_ISOLATION ON eine gute Wahl. Verwenden Sie keine Sperrhinweise auf Tabellenebene. Sperrhinweise auf Tabellenebene setzen die automatische Sperrverwaltung von SQL Server außer Kraft. Setzen Sie diese daher sparsam ein, und verzichten Sie nach Möglichkeit ganz darauf. Arbeiten Sie mit der automatischen Sperrverwaltung des SQL Servers und nicht gegen sie! Hinterlassen Sie keine verlorenen Transaktionen. Explizite Transaktionen, die einen Fehler erzeugen, werden nicht zurückgesetzt. Denken Sie daran, dass Sie in einem sol-
235
5 Transaktionen und Sperren chen Fall ein ROLLBACK ausführen müssen! Ansonsten werden die von der Transaktion erzeugten Sperren nicht wieder freigegeben. Diese Aussage gilt auch für TRY/CATCH-Blöcke. Wenn aufgrund eines aufgetretenen Fehlers in den CATCHBlock verzweigt wird, wird die betroffene Transaktion, innerhalb derer der Fehler aufgetreten ist, nicht automatisch zurückgenommen! Dies müssen Sie explizit durch eine ROLLBACK-Anweisung im CATCH-Block erledigen. Dieses ROLLBACK sollte möglichst am Anfang des CATCH-Blockes stehen, damit die verlorene Transaktion so bald wie möglich beendet wird. Überwachen Sie die tempdb. Falls Sie mit der Isolationsstufe SNAPSHOT arbeiten, kann die Systemdatenbank tempdb eine Ursache für verschlechterte Performance sein.
5.8
Zusammenfassung In diesem Kapitel haben Sie erfahren, wie der SQL Server Transaktionen verwendet, um konkurrierenden Zugriff auf Ressourcen zu ermöglichen, ohne dabei die Datenkonsistenz zu verletzen. Hierbei wurde erläutert, wie die Sperrmechanismen von SQL Server funktionieren. Sie haben auch gesehen, wie Sie aktive Sperren beobachten können, welchen Einfluss Sie als Datenbankentwickler auf das Sperrverhalten von SQL Server nehmen können und welche Möglichkeiten Ihnen zur Verfügung stehen, um Blockierungen durch gesperrte Ressourcen zu minimieren. In einem weiteren Abschnitt wurde Ihnen gezeigt, wie SQL Server die Transaktionsverarbeitung intern organisiert, um gegen Systemausfälle gefeit zu sein. Abschließend haben Sie dann noch einige nützliche Hinweise für den Entwurf von Transaktionen in Ihrem Anwendungscode erhalten.
236
6 Administration für Datenbankentwickler In der Regel wird eine Einteilung in Datenbankentwickler und Datenbankadministrator zu einem großen Teil einfach willkürlich vorgenommen. So wie in jedem Datenbankadministrator auch ein wenig von einem Entwickler stecken muss, wird auch der Datenbankentwickler nicht umhinkommen, zumindest in die Grundlagen der Administration von SQL Server und Datenbanken einzusteigen. Und dies also die Vermittlung von Grundlagenwissen für die Administration ist auch genau das Ziel dieses Kapitels. Bitte erwarten Sie an dieser Stelle also keine umfassende Einführung in die SQL Server-Administration; hierfür gibt es eigene Bücher, die sich ausschließlich mit dieser Thematik auseinandersetzen (siehe zum Beispiel [BAUD06]). Dieses Kapitel wird Ihnen die Grundlagen der Administration vermitteln, auf denen Sie aufbauen können und die es Ihnen ermöglichen, bei Bedarf tiefer in die Administration einzusteigen. Aus diesem Grund werden Sie im vorliegenden Kapitel auch an ungewöhnlich vielen Stellen von mir auf die Online-Dokumentation verwiesen. Glauben Sie mir bitte, dass ich dies wirklich nicht gern tue. Wer ein Buch erwirbt, möchte schließlich dort auch alle relevanten Informationen vorfinden. Nur ist dies eben ein Buch über Datenbankentwicklung, in dem nicht die Administration im Mittelpunkt steht. Wir werden uns daher auf drei wesentliche Punkte konzentrieren, die auch ein Datenbankentwickler beherrschen sollte: Sicherung und Wiederherstellung von Datenbanken Steuerung von Zugriffserlaubnissen auf SQL Server und Datenbanken Überwachung wesentlicher Zustände von SQL Server und Datenbanken
6.1
Datensicherung und -wiederherstellung Als ein DBMS bietet SQL Server natürlich auch komfortable Möglichkeiten für die Sicherung und Wiederherstellung von System- und Benutzerdatenbanken. Eine Sicherung von Datenbanken kann online erfolgen, d. h., Benutzer können mit der Datenbank weiterarbei-
237
6 Administration für Datenbankentwickler ten, während eine Datenbanksicherung läuft. Der SQL Server kennt drei unterschiedliche Arten der Datensicherung: Vollständige Sicherung einer Datenbank. Hierbei werden alle Daten der Datenbank sowie weiterführende Informationen über Datenbankstrukturen (die Kataloginformationen) gesichert. Die vollständige Sicherung spreichert auch diejenigen Teile des Transaktionsprotokolls, die während der Sicherung durch Benutzeraktionen hinzugekommen sind. Auf diese Weise wird eine Online-Sicherung ermöglicht. Differenzielle Sicherung einer Datenbank. Eine differenzielle Datenbanksicherung enthält nur die nach der letzten vollständigen Datensicherung veränderten Daten. Sicherung des Transaktionsprotokolls. Das Transaktionsprotokoll enthält Einträge über alle nach der letzten vollständigen Datensicherung veränderten Daten. In dieser Sicherung sind auch Verweise zu eventuell vorhandenen differenziellen Sicherungen gespeichert, die es ermöglichen, für die Wiederherstellung auf einer solchen differenziellen Sicherung aufzusetzen. Zusätzlich besteht auch die Möglichkeit, nur einzelne Dateien oder Dateigruppen zu sichern eine Variante, die wir in dieser Einführung nicht weiter betrachten wollen. SQL Server kann als Sicherungsmedien sowohl Bandlaufwerke als auch Festplatten verwenden. Es existiert außerdem die Möglichkeit, für SQL Server sogenannte Sicherungsmedien zu erzeugen. Ein Sicherungsmedium ermöglicht die Verwendung eines logischen Namens für ein physikalisches Medium, wie Band oder Festplatte, innerhalb von SQL Server. Wir werden für unsere Beispiele ein solches Sicherungsmedium verwenden, müssen es also zunächst erzeugen. Dies erledigen wir einfach mit dem Management Studio. Im Bereich Serverobjekte kann über das Kontextmenü für Sicherungsmedien ein Medium hinzugefügt werden (Abbildung 6.1).
Abbildung 6.1 Anlegen eines Sicherungsmediums
Im Fenster Sicherungsmedium vergeben Sie für das Medium einen Namen und legen den physikalischen Speicherort fest (Abbildung 6.2). Für den Fall, dass Sie eine Datei verwenden möchten (so wie im Beispiel dargestellt), muss diese Datei noch nicht existieren. Allerdings ist darauf zu achten, dass SQL Server oder genauer: die Anmeldung, unter welcher der SQL Server-Dienst läuft entsprechende Schreibrechte für das angegebene
238
6.1 Datensicherung und -wiederherstellung Verzeichnis besitzt. Die Sicherungsdatei kann auch auf einer Freigabe im Netzwerk erzeugt werden, dann ist auf die Zugriffsberechtigung natürlich besonders zu achten. Das Sicherungsmedium wird auch (nach einer entsprechenden Rückfrage) erzeugt, wenn kein Zugriff auf den angegebenen Pfad besteht. In so einem Fall bekommen Sie erst beim Versuch, eine Datensicherung auf dem Medium durchzuführen, eine Fehlermeldung.
Abbildung 6.2 Konfigurieren eines Sicherungsmediums
Wenn Sie das Fenster mit OK verlassen, wird das Sicherungsmedium im ObjektExplorer auch angezeigt (Abbildung 6.3). Wir werden dieses Sicherungsmedium in der Folge verwenden, um Datensicherungen von Benutzer- und Systemdatenbanken zu erstellen.
Abbildung 6.3 Das erzeugte Sicherungsmedium im Objekt-Explorer
239
6 Administration für Datenbankentwickler
6.1.1
Wiederherstellungsmodelle
Für jede Benutzerdatenbank (genau genommen auch für die Systemdatenbanken) kann ein sogenanntes Wiederherstellungsmodell gewählt werden, das Auswirkungen auf die Verwaltung der Protokolldatei der Datenbank und somit auch auf die Möglichkeiten zur Wiederherstellung einer Datenbank hat. SQL Server kennt drei verschiedene Wiederherstellungsmodelle: Einfaches Wiederherstellungsmodell Im einfachen Wiederherstellungsmodell wird das Transaktionsprotokoll beim Erreichen eines Prüfpunktes abgeschnitten. Dies bedeutet, dass alle bereits bestätigten und in die Datendatei übernommenen Transaktionen aus dem Protokoll entfernt werden. Das Transaktionsprotokoll wird hierbei nicht verkleinert, der durch das Abschneiden freigegebene Platz steht jedoch für weitere Protokolleinträge wieder zur Verfügung. In diesem Wiederherstellungsmodell können keine Transaktionsprotokollsicherungen (und natürlich auch keine entsprechenden Rücksicherungen) durchgeführt werden. Im Falle eines Datenverlustes kann nur auf den Stand der letzten kompletten bzw. differenziellen Datensicherung wiederhergestellt werden. Vollständiges Wiederherstellungsmodell Für eine Datenbank im vollständigen Wiederherstellungsmodell werden alle Datenänderungen im Transaktionsprotokoll aufgezeichnet. Diese Protokolleinträge bleiben so lange erhalten, bis das Protokoll gesichert wird. Dies gilt auch für bereits bestätigte Transaktionen, diese werden ebenso im Protokoll gespeichert. Daraus folgt vor allem zweierlei: 1. Das Transaktionsprotokoll wächst ständig an. Sie müssen das Protokoll sichern, um die bestätigten Transaktionen hieraus zu entfernen. Anderenfalls wird irgendwann Ihre Festplatte an die Grenzen der Kapazität gelangen. 2. Die im Transaktionsprotokoll aufgezeichneten Datenänderungen können basierend auf der letzten vollständigen oder auch differenziellen Datensicherung wieder eingespielt werden; ein Prozess, der als Roll Forward bezeichnet wird. Dadurch besteht die Möglichkeit, dass bei einem Datenverlust bis zu dem Zeitpunkt unmittelbar vor der Störung eine Wiederherstellung erfolgen kann. Insbesondere kann ein Transaktionsprotokoll auch bis zu einem beliebigen Zeitpunkt in der Vergangenheit wieder eingespielt werden etwa für den Fall, dass Datenänderungsoperationen rückgängig gemacht werden sollen. Massenprotokolliertes Wiederherstellungsmodell Im massenprotokollierten Wiederherstellungsmodell werden abgeschlossene Transaktionen ebenfalls im Transaktionsprotokoll belassen. Allerdings werden nicht alle Datenänderungen im Transaktionsprotokoll gespeichert. Es sind gerade die Massen-Einfügeoperationen, wie zum Beispiel SELECT INTO oder BULK INSERT, die nicht protokolliert
240
6.1 Datensicherung und -wiederherstellung werden, von daher ist die Bezeichnung Massenprotokolliert vielleicht etwas unglücklich gewählt. Eine Datenbank in diesem Wiederherstellungsmodus kann unter Verwendung des Transaktionsprotokolls nur dann bis zu einem beliebigen Zeitpunkt wiederhergestellt werden, wenn nach jeder (nicht protokollierten) Massen-Einfügeoperation eine vollständige oder differenzielle Datensicherung durchgeführt wird. Einstellen des Wiederherstellungsmodells für eine Datenbank Um für eine Datenbank ein Wiederherstellungsmodell festzulegen, kann die Anweisung ALTER DATABASE wie folgt verwendet werden:
Für dürfen Sie die folgenden drei Werte angeben: FULL. Die Datenbank wird in das vollständige Wiederherstellungsmodell versetzt. BULK_LOGGED. Die Datenbank verwendet das massenprotokollierte Wiederherstellungsmodell. SIMPLE. Die Datenbank verwendet das einfache Wiederherstellungsmodell. Alternativ können Sie das Wiederherstellungsmodell natürlich im Objekt-Explorer einstellen. Hierzu wählen Sie für eine existierende Datenbank die Eigenschaften aus und gehen in den Bereich Optionen (Abbildung 6.4).
Abbildung 6.4 Auswahl der Wiederherstellungsmodells
241
6 Administration für Datenbankentwickler Im folgenden Teil wird davon ausgegangen, dass für die Datenbank AdventureWorks das vollständige Wiederherstellungsmodell ausgewählt wurde.
6.1.2
Prüfen der Konsistenz einer Datenbank
Bevor Sie eine Sicherung einer Datenbank durchführen, ist es ratsam, die betreffende Datenbank zu überprüfen. Hierzu existiert das T-SQL-Kommando DBCC (DataBase Consistency Checker). Dieses Kommando kennt eine Vielzahl von Optionen, unter anderem kann es auch zur Konsistenzprüfung einer kompletten Datenbank verwendet werden. Hierzu verwenden Sie DBCC in der folgenden Form:
wobei Sie für ' den Namen der zu prüfenden Datenbank angeben. Die von DBCC erzeugte Ausgabe ist im Normalfall derart umfangreich, dass eine Lokalisierung eventueller Fehlermeldungen innerhalb dieser Ausgabe relativ schwierig ist. Aus diesem Grund wird das Kommando mit der Option aufgerufen, wodurch dann nur noch Fehlermeldungen ausgegeben werden. Bitte überprüfen Sie Ihre Datenbanken stets vor einer vollständigen Datenbanksicherung. Eine Datenbank, die in einem fehlerhaften Zustand gesichert wurde, ist für eine Wiederherstellung natürlich wenig nützlich.
6.1.3
Vollständige Datenbanksicherung
Nun aber zunächst einmal genug der Theorie zu Datensicherungen und Wiederherstellungsmodellen. Wir wollen jetzt eine vollständige Sicherung der Datenbank AdventureWorks in unser angelegtes Sicherungsmedium aufnehmen. Natürlich kann auch diese Aufgabe wieder wahlweise mit dem Management Studio oder durch entsprechende T-SQL-Kommandos erledigt werden. Wir werden in diesem Beispiel das Management Studio verwenden, um ein entsprechendes T-SQL-Skript erzeugen zu lassen. Dieses Skript soll dann zur Erstellung eines Auftrags für den SQL Server Agent verwendet werden. Dadurch wollen wir erreichen, dass die Sicherung der Datenbank zyklisch erfolgt, etwa jede Nacht um 03:00 Uhr. Es ist in jedem Fall sinnvoll, Datensicherungen so zu planen, dass diese bei nur geringer Benutzeraktivität durchgeführt werden wir gehen davon aus, dass dies um 03:00 Uhr in der Nacht der Fall sein wird. Zwar sind Online-Datensicherungen möglich, wenn eine Sicherung allerdings während starker Aktivität von Benutzern durchgeführt wird, so wird eine solche Sicherung sehr groß werden und auch entsprechend lange dauern schließlich werden in der vollständigen Datensicherung auch alle während der Sicherung vorgenommenen Datenänderungen protokolliert. Die Datensicherung wird über das Kontextmenü der Datenbank gestartet. Wählen Sie dort den Menüeintrag Tasks/Sichern
(Abbildung 6.5).
242
6.1 Datensicherung und -wiederherstellung
Abbildung 6.5 Starten der Datenbanksicherung
Im Fenster Datenbank sichern AdventureWorks (Abbildung 6.6) können Sie anschließend die Optionen für die zu erstellende Sicherung einstellen.
Abbildung 6.6 Einstellen der Sicherungsparameter
243
6 Administration für Datenbankentwickler Unsere Sicherung soll auf das gerade angelegte Sicherungsmedium erfolgen. Hierzu entfernen Sie bitte zunächst alle Einträge in der unteren Liste durch Auswahl und Klicken auf Entfernen. Anschließend klicken Sie auf Hinzufügen, und wählen Sie im Dialog Sicherungsziel auswählen (Abbildung 6.7) das angelegte Sicherungsmedium aus.
Abbildung 6.7 Auswahl des Sicherungsmediums als Ziel
Abbildung 6.6 zeigt die Auswahl dieses Mediums in der Liste. In der Abbildung ist auch zu erkennen, dass für die Sicherung eine Lebensdauer von sieben Tagen angegeben wurde. Dadurch kann diese Datensicherung erst nach Ablauf dieses Zeitraums überschrieben werden, die erzeugte Datensicherung wird also mindestens eine Woche lang aufbewahrt. Im Bereich Optionen können weitere Einstellungen vorgenommen werden, zum Beispiel für die Initialisierung des Mediums vor der Datensicherung. Diese Einstellungen belassen wir auf den Standardwerten. Nachdem nun alles konfiguriert ist, soll die Datensicherung nicht ausgeführt werden. Stattdessen werden wir ein Skript erzeugen lassen und dieses direkt in einen Auftrag übertragen. Hierzu lassen wir uns über das Menü Skript/Skript für Aktion in Auftrag schreiben des Fensters einen Auftrag erzeugen. Das daraufhin erscheinende Fenster (Abbildung 6.8) enthält bereits Einstellungen für einen Auftrag des SQL Server Agents, die allerdings noch ein wenig angepasst werden müssen. Wie bereits gesagt, soll der Auftrag so gestaltet werden, dass unsere Datenbanksicherung jede Nacht um 03:00 Uhr ausgeführt wird. Zunächst einmal sollte die Bezeichnung für den Auftrag entsprechend geändert werden. Wenn Sie möchten, können Sie auch eine zusätzliche Beschreibung für diesen Auftrag vergeben. Wichtig ist aber vor allem die Konfiguration der Auftragsschritte und des Zeitplanes für die automatische Auftragsausführung. Wählen Sie zunächst die Seite Schritte, um den einzigen Auftragsschritt die Sicherung der Datenbank anzusehen. Markieren Sie den Auftragsschritt, und wählen Sie Bearbeiten aus. Es öffnet sich ein Fenster, in dem der Schritt konfiguriert werden kann (Abbildung 6.9).
244
6.1 Datensicherung und -wiederherstellung
Abbildung 6.8 Erzeugen eines Auftrags für die Sicherung
Abbildung 6.9 Der (einzige) Auftragsschritt für die Sicherung
245
6 Administration für Datenbankentwickler Der T-SQL-Code für diesen Auftragsschritt wurde ebenso wie der Schritt selber automatisch erzeugt. Der Schritt enthält das Kommando BACKUP DATABASE für die Erstellung einer vollständigen Datenbanksicherung auf dem Medium BackupServer mit allen erforderlichen Optionen. Auf der Seite Erweitert können weitere Einstellungen für diesen Auftragsschritt vorgenommen werden. Dies ist vor allem interessant, wenn ein Auftrag aus mehreren Schritten besteht. Für unseren Auftrag müssen hier keine Änderungen an den Standardeinstellungen durchgeführt werden. Bestätigen Sie die Einstellungen mit OK, um zurück zum Fenster Auftragseigenschaften zu gelangen. Was nun noch fehlt, ist die Konfiguration eines entsprechenden Zeitplanes. Gehen Sie hierzu auf die Seite Zeitpläne, und wählen Sie Neu
aus. Es öffnet sich das Fenster zur Konfiguration eines Zeitplanes für die automatische Auftragsausführung (Abbildung 6.10).
Abbildung 6.10 Konfigurieren des Zeitplans für die nächtliche Sicherung der Datenbank
Im Fenster Neuer Auftragszeitplan werden nun alle Einstellungen vorgenommen, die dafür sorgen, dass der Auftrag jede Nacht um 03:00 Uhr automatisch gestartet wird, so wie in Abbildung 6.10 zu sehen. Bestätigen Sie diesen Dialog und anschließend auch den Dialog Auftragseigenschaften mit OK. Der Auftrag ist damit fertig konfiguriert. Wichtig ist, dass Sie das Fenster für die Erstellung der Datensicherung nun nicht mit OK bestätigen, sondern Abbrechen wählen. Die Datensicherung wird ja über den angelegten Auftrag erstellt, soll also nicht sofort durchgeführt werden. Im Ordner Aufträge des SQL Server Agents wird der erzeugte Auftrag angezeigt (Abbildung 6.11).
246
6.1 Datensicherung und -wiederherstellung
Abbildung 6.11 Der erzeugte Auftrag für die Sicherung der Datenbank
In der kommenden Nacht wird nun also eine Sicherung der Datenbank AdventureWorks erstellt werden. Wenn Sie nicht so lange warten möchten, um zu sehen, ob Ihr Auftrag auch funktioniert, dann können Sie den Auftrag auch manuell starten. Wählen Sie hierzu aus dem Kontextmenü für den Auftrag die Option Auftrag starten bei Schritt
. Da unser Auftrag nur aus einem Schritt besteht, wird er unmittelbar ausgeführt. Abbildung 6.12 zeigt die Meldung nach erfolgreicher Auftragsausführung.
Abbildung 6.12 Erfolgreiche Auftragsausführung
Abschließend sollte nun noch kontrolliert werden, ob die Sicherung auch auf unserem Sicherungsmedium existiert. Dies kann zum Beispiel über den Eigenschaftsdialog des Mediums geschehen. Dort finden Sie auf der Seite Medieninhalt alle im Medium enthaltenen Datensicherungen (Abbildung 6.13 auf der nächsten Seite). Im Moment enthält unser Medium nur eine einzige Sicherung, wir werden in Kürze einige weitere hinzufügen. Um den Auftrag für die Datensicherung zu komplettieren, sollten Sie nun noch in einem ersten Auftragsschritt also vor der eigentlichen Datensicherung eine Konsistenzprüfung der AdventureWorks-Datenbank mit DBCC vornehmen, so wie weiter oben beschrieben. Hierzu bietet der Auftragseditor Möglichkeiten zur Ablaufsteuerung einzelner Auftragsschritte. Insbesondere ist es möglich anzugeben, dass ein nachfolgender Auftragsschritt nur ausgeführt werden darf, wenn der vorherige Schritt erfolgreich war genau das, was wir benötigen. Versuchen Sie also ruhig einmal, einen entsprechenden Schritt zum Auftrag hinzuzufügen und den Ablauf entsprechend zu konfigurieren.
247
6 Administration für Datenbankentwickler
Abbildung 6.13 Inhalt des Sicherungsmediums
6.1.4
Sicherung des Transaktionsprotokolls
Nachdem nun dafür gesorgt ist, dass jede Nacht eine vollständige Sicherung der Datenbank erstellt wird, wollen wir nun noch jede Stunde eine Transaktionsprotokollsicherung der AdventureWorks-Datenbank zum Sicherungsmedium hinzufügen. Auch dies kann auf dieselbe Weise konfiguriert werden, wie im vorangegangenen Abschnitt bei der vollständigen Datensicherung geschehen. Um zu zeigen, dass es auch anders geht, soll hier jedoch der Auftrag direkt erzeugt werden. Zunächst benötigen wir die BACKUP-Anweisung zur Erstellung der Protokollsicherung. Diese Anweisung wird später in einem Auftragsschritt benötigt und sieht so aus:
Zum Erstellen einer Protokollsicherung wird die Aneisung BACKUP LOG verwendet. Diese Anweisung erwartet den Namen der Datenbank und des Sicherungsmediums bzw. der Sicherungsdatei (in unserem Fall wird ein Sicherungsmedium benutzt). Über die Option RETAINDAYS wird angegeben, wie lange die Sicherung aufgehoben wird, bevor sie überschrieben werden kann. Optional ist es möglich, einen Namen für die Sicherung zu vergeben, dies wird in der obigen Anweisung durch die Option NAME erledigt. Natürlich kennt die BACKUP-Anweisung noch eine Vielzahl weiterer Klauseln und Optionen, die in dieser Einführung jedoch außen vor gelassen werden (müssen). Das Kommando in der obigen Form ist für unsere Zwecke ausreichend.
248
6.1 Datensicherung und -wiederherstellung
Abbildung 6.14 Einen Auftrag hinzufügen
Um nun einen Auftrag zu erzeugen, der dieses Kommando stündlich ausführt, kann im Objekt-Explorer über das Kontextmenü für den Zweig SQL Server Agent/Aufträge ein Auftrag hinzugefügt werden (Abbildung 6.14). Daraufhin öffnet sich zunächst das Fenster zur Konfiguration der allgemeinen Auftragsoptionen (siehe nochmals Abbildung 6.8). Vergeben Sie hier einen Namen für den Auftrag und, wenn Sie möchten, auch eine Beschreibung. Wählen Sie anschließend die Seite Schritte, um den Auftragsschritt für die Konfiguration der Protokollsicherung anzulegen (Abbildung 6.15). Dort klicken Sie auf Neu
, um einen Schritt hinzuzufügen. Im Auftragsschritt selber wird einfach nur das oben gezeigte BACKUP LOG-Kommando als Befehl eingetragen. Achten Sie bitte darauf, dass als Schritttyp Transact-SQL-Skript ausgewählt ist, und vergeben Sie auch einen Namen für diesen Schritt. Bestätigen Sie den Dialog mit OK, und gehen Sie anschließend auf die Seite Zeitpläne. Klicken Sie auch dort auf Neu..., um einen Zeitplan hinzuzufügen.
Abbildung 6.15 Den Auftragsschritt für die Sicherung des Protokolls konfigurieren
249
6 Administration für Datenbankentwickler Abbildung 6.16 zeigt, wie der Zeitplan für die stündliche Ausführung der Protokollsicherung konfiguriert wird.
Abbildung 6.16 Einen Zeitplan erstellen, der jede Stunde aktiviert wird
Abbildung 6.17 Inhalt des Sicherungsmediums mit Protokollsicherungen
250
6.1 Datensicherung und -wiederherstellung Sie können nun alle noch offenen Fenster durch OK bestätigen, der Auftrag zur stündlichen Erstellung einer Protokollsicherung der AdventureWorks-Datenbank ist damit fertig. Wenn Sie sich nach einigen Stunden den Inhalt des Sicherungsmediums nochmals ansehen, wird dieser in etwa so aussehen, wie in Abbildung 6.17 gezeigt. (Falls Sie nicht bis zum Ablauf einer vollen Stunde warten möchten, ist es natürlich auch hier wieder möglich, den Auftrag manuell zu starten.) Sichern des Transaktionsprotokolls einer beschädigten Datenbank Erinnern Sie sich bitte daran, dass Daten und Protokolle für jede Datenbank in unterschiedlichen Dateien gespeichert werden. In diesem Szenario ist es durchaus vorstellbar, dass nur eine Datendatei einer Datenbank defekt, das Transaktionsprotokoll aber trotzdem nach wie vor intakt ist. Wenn SQL Server erkennt, dass eine Datendatei beschädigt ist, so wird diese Datenbank als fehlerverdächtig eingestuft. Ein Zugriff auf diese Datenbank ist dann nicht mehr möglich mit einer Ausnahme: Das Transaktionsprotokoll dieser Datenbank kann gesichert werden. Allerdings wird das Transaktionsprotokoll bei der Sicherung standardmäßig abgeschnitten, d. h., alle bereits bestätigten Transaktionen werden aus dem Protokoll entfernt. Für eine fehlerverdächtige Datenbank ist das Abschneiden des Protokolls nicht möglich SQL Server geht verständlicherweise davon aus, dass eine fehlerhafte Datenbank nicht dadurch repariert werden kann, dass die Dateien dieser Datenbank verändert werden. Es ist aber möglich, das Protokoll zu sichern, ohne es abzuschneiden. Hierzu verwenden Sie die Option WITH NO_TRUNCATE der BACKUP LOG-Anweisung:
Ein so gesichertes Protokoll kann dann ebenfalls für eine Rücksicherung verwendet werden, wodurch in den meisten Fällen eine Wiederherstellung bis unmittelbar vor dem Systemausfall möglich ist. Aus dieser Möglichkeit ergeben sich vor allem zwei Konsequenzen: 1. Speichern Sie Daten- und Protokolldateien nach Möglichkeit auf unterschiedlichen physikalischen Laufwerken. 2. Das Transaktionsprotokoll ist für eine Wiederherstellung immens wichtig. Erstellen Sie Duplikate Ihrer Transaktionsprotokollsicherungen, wenn möglich sofort nach dem Sichern eines Protokolls
6.1.5
Sichern der Systemdatenbanken
Vergessen Sie bitte nicht, die Systemdatenbanken in Ihre Sicherungsstrategie einzubeziehen. SQL Server verwendet diese Datenbanken, um Informationen für die Systemverwaltung zu speichern. Auf jeden Fall sollten Sie die master-Datenbank und die Datenbank msdb sichern. Eine Sicherung der model-Datenbank können Sie ebenfalls vornehmen, wenn Sie diese Datenbank als Vorlage für neu anzulegende Datenbanken verwenden. Die Sicherung der Datenbank tempdb ist nicht erforderlich, diese Datenbank wird bei jedem Systemstart von SQL Server neu erzeugt.
251
6 Administration für Datenbankentwickler
6.2
Wiederherstellung von Datenbanken Die beste Datensicherung ist natürlich nutzlos, wenn eine Wiederherstellung nach dem Systemausfall nicht funktioniert. In diesem Abschnitt werden wir einen Systemausfall annehmen und die Datenbank AdventureWorks aus der Datensicherung wiederherstellen. Jede Wiederherstellung einer Datenbank muss immer mit einer vollständigen Datensicherung begonnen werden. Aufbauend auf dieser vollständigen Wiederherstellung können dann zugehörige differenzielle und/oder Protokollsicherungen wiederhergestellt werden. Die Wiederherstellung einer Datenbank kann einfach über das Kontextmenü der Datenbank durch Auswahl von Tasks/Wiederherstellen/Datenbank
gestartet werden (Abbildung 6.18).
Abbildung 6.18 Wiederherstellen einer Datenbank
Der Sicherungsverlauf für alle Datenbanken wird in der msdb gespeichert. Wenn Ihre msdb intakt ist, so werden Ihnen alle seit der letzten vollständigen Sicherung existierenden Datensicherungen für die ausgewählte Datenbank in einer Liste angeboten (Abbildung 6.19). In der Auswahlliste ist bereits die letzte Protokollsicherung als Punkt für die Wiederherstellung gekennzeichnet. Für eine Wiederherstellung bis zu diesem Punkt muss in unserem Fall zunächst die letzte vollständige Sicherung eingespielt werden. Daran anschließend müssen dann alle nach der vollständigen Sicherung vorgenommen Protokollsicherungen in der exakten Reihenfolge eingespielt werden. Sie können diese Wiederherstellung Schritt für Schritt vornehmen, also jede Sicherung einzeln einspielen. Der Dialog für die Wiederherstellung der Datenbank ermöglicht es Ihnen aber auch, dass Sie die für den Punkt der gewünschten Wiederherstellung benötigte Protokollsicherung einfach auswählen. Wenn der Sicherungsverlauf in der msdb korrekt gespeichert ist, so werden dann automatisch alle
252
6.2 Wiederherstellung von Datenbanken
Abbildung 6.19 Datenbank wiederherstellen
weiteren Protokolle und auch vollständige bzw. differenzielle Sicherungen in der Liste markiert. Es ist dann möglich, alle für die Wiederherstellung erforderlichen Sicherungen einfach durch einen Klick auf OK einzuspielen. Der Dialog bietet Ihnen auch die Möglichkeit, ein bestimmtes Sicherungsmedium auszuwählen, von dem die Wiederherstellung erfolgen soll. In unserem Fall verzichten wir auf diese Möglichkeit, weil eine Wiederherstellung bis zum spätest möglichen Zeitpunkt erfolgen soll und die dafür erforderlichen Sicherungen bereits in der Auswahlliste markiert sind. Falls Sie eine Wiederherstellung zu einem früheren Zeitpunkt also einem Punkt, der vor der letzten vollständigen Datenbanksicherung liegt wünschen oder falls in Ihrer msdb kein Sicherungsverlauf für die Datenbank gespeichert ist (etwa weil Sie das Sicherungsmedium von einem anderen Server einspielen müssen), so können Sie ein bestimmtes Sicherungsmedium auswählen und dann die erforderlichen Sicherungen aus diesem Medium selektieren. Hierbei müssen Sie dann selbst darauf achten, dass Sie die Rücksicherung der einzelnen Datensicherungen in der erforderlichen Reihenfolge durchführen. Sollen Transaktionsprotokolle eingespielt werden, so muss auf der Seite Optionen für die Wiederherstellung die Option Vorhandene Datenbank überschreiben ausgewählt werden, ansonsten kann die Rücksicherung des letzten Protokolls nicht durchgeführt werden (Abbildung 6.20).
253
6 Administration für Datenbankentwickler
Abbildung 6.20 Auswahl der Optionen für die Wiederherstellung
Wenn die wiederherzustellende Datenbank das vollständige Wiederherstellungsmodell verwendet, so können Sie diese Datenbank bis zu einem beliebigen Zeitpunkt in der Vergangenheit wiederherstellen eine sehr nützliche Möglichkeit für den Fall versehentlich durchgeführter Datenänderungen. Hierzu öffnen Sie über den Button mit den drei Punkten neben dem Text Bis zu Zeitpunkt einen weiteren Dialog zur Einstellung des gewünschten Zeitpunktes der natürlich durch die ausgewählten Protokolle auch abgedeckt sein muss. Die SQL Server-Version mit Service Pack 1 hat hierbei allerdings noch ein Problem mit europäischen Datumsformaten. Sobald Sie einen entsprechenden Zeitpunkt auswählen und den Dialog mit OK bestätigen, erhalten Sie die in Abbildung 6.21 gezeigte Fehlermeldung.
Abbildung 6.21 Fehlermeldung bei Einstellung eines Zeitpunktes
Das Problem wird mit dem Service Pack 2 behoben sein. Wenn Sie das Service Pack 2 noch nicht installiert haben, so müssen Sie die Wiederherstellung in diesem Fall durch entsprechende RESTORE LOG-Anweisungen unter Verwendung der STOPAT-Option vornehmen. Ich gehe davon aus, dass das Service Pack 2 bei Erscheinen dieses Buchs verfügbar sein wird. Daher möchte ich an dieser Stelle auf eine Erläuterung von RESTORE LOG verzichten und Sie an die Online-Dokumentation verweisen. Wenn alle Einstellungen vorgenommen wurden, kann die Wiederherstellung durch einen Klick auf OK gestartet werden. Während der laufenden Wiederherstellung wird in der unteren linken Ecke des Fensters eine Statusanzeige über den Fortschritt der Rücksicherung ausgegeben. Nach erfolgreichem Abschluss der Wiederherstellung erhalten Sie eine entsprechende Information (Abbildung 6.22).
254
6.2 Wiederherstellung von Datenbanken
Abbildung 6.22 Erfolgsmeldung nach Abschluss der Wiederherstellung
Im Gegensatz zur Sicherung die online erfolgen kann muss eine Datenbank für eine Wiederherstellung exklusiv gesperrt werden. Da jede bestehende Verbindung zur Datenbank diese Datenbank im gemeinsamen Modus sperrt, ist eine exklusive Sperre für eine Datenbank, zu der noch offene Verbindungen existieren, nicht möglich. Die Datenbank kann in so einem Fall also wenn noch Verbindungen aufrechterhalten werden nicht wiederhergestellt werden. Dann nämlich erhalten Sie beim Versuch der Wiederherstellung eine Fehlermeldung. Offene Verbindungen müssen zunächst beendet werden. Dies können Sie zum Beispiel über ALTER DATABASE erledigen, so wie in Kapitel 4 beschrieben.
6.2.1
Automatische Wiederherstellung bei Systemstart
SQL Server beginnt nach jedem Start mit einem automatischen Wiederherstellungsprozess für alle Datenbanken. Diese automatische Wiederherstellung kann nicht unterbunden werden. Sie sorgt dafür, dass sich alle Datenbanken in einem Zustand befinden, in dem keine anhängigen Transaktionen mehr existieren. In Kapitel 5 wurde bereits erklärt, wie SQL Server dies unter Verwendung des Transaktionsprotokolls bewerkstelligt. Die automatische Wiederherstellung ist im Übrigen unabhängig vom eingestellten Wiederherstellungsmodus. Sie funktioniert gleichermaßen in allen drei Modi.
6.2.2
Wiederherstellung der Systemdatenbanken
Die Systemdatenbanken von SQL Server können auf die gleiche Weise wiederhergestellt werden wie oben beschrieben mit einer Ausnahme: Eine Wiederherstellung der masterDatenbank ist im Normalbetrieb von SQL Server nicht möglich. Hierfür muss SQL Server zunächst beendet und im Einzelbenutzermodus neu gestartet werden. Falls die Struktur der master-Datenbank noch intakt ist, startet der Server, und die Datenbank kann restauriert werden. Das Starten von SQL Server im Einzelbenutzermodus können Sie für die Standardinstanz von SQL Server über die Anweisung
erreichen. Das Dienstprogramm finden Sie im Programmverzeichnis des SQL Servers unter .\MSSQL.1\MSSQL\Binn. Wie der Name bereits vermuten lässt, gestattet SQL Server im Einzelbenutzermodus nur eine einzige Verbindung. Es ist möglich, diese Verbindung durch das Management Studio zu öffnen, indem der Objekt-Explorer für den Server geöffnet wird. Aus dem Objekt-Explorer kann dann eine Wiederherstellung der
255
6 Administration für Datenbankentwickler master-Datenbank durchgeführt werden. Eine Wiederherstellung durch Sqlcmd mit einer entsprechenden RESTORE DATABASE-Anweisung ist natürlich ebenso möglich. Nach der Wiederherstellung der master-Datenbank wird der SQL Server-Dienst automatisch beendet. Das Management Studio reagiert hierauf mit einer Fehlermeldung, die in Abbildung 6.23 gezeigt wird.
Abbildung 6.23 Meldung nach erfolgreicher Wiederherstellung der master-Datenbank
Dieses Verhalten ist sicherlich etwas eigenartig. Die Fehlermeldung aus Abbildung 6.23 ist ja letztlich die Erfolgsmeldung für eine abgeschlossene Wiederherstellung der masterDatenbank. Der SQL Server Dienst kann anschließend wieder normal gestartet und ausgeführt werden. Für den Fall, dass Ihre master-Datenbank defekt ist, gestaltet sich die Angelegenheit etwas komplizierter. SQL Server wird mit einer defekten master-Datenbank nicht starten. In einem solchen Fall müssen Sie zunächst dafür sorgen, dass die beim Start von SQL Server benötigte master-Datenbank intakt ist. Hierzu können Sie aus der Installation von SQL Server die Systemdatenbanken wiederherstellen. (Bitte schlagen Sie dies in der OnlineDokumentation unter REINSTALLMODE nach.) Alternativ ist es auch möglich, eine zuvor angefertigte Kopie der Dateien master.mdf und mastlog.ldf in das Datenverzeichnis von SQL Server zu übertragen und den Server dann im Einzelbenutzermodus zu starten, um eine Wiederherstellung der master-Datenbank aus einer vorhandenen Sicherung durchzuführen so wie oben beschrieben.
6.3
Zugriffskontrolle SQL Server bietet umfangreiche Möglichkeiten für die Zugriffskontrolle sowohl auf den Server selbst als auch auf die zugeordneten Datenbanken und Datenbankobjekte. Um dies zu verwirklichen, existiert ein Konzept, das die Zugriffserlaubnis auf zwei unterschiedlichen Ebenen kontrolliert. Zunächst einmal müssen Benutzer, die auf Datenbanken zugreifen wollen, sich gegenüber dem Server authentifizieren. Ein Benutzer muss also die Berechtigung besitzen, sich überhaupt am Server anmelden zu dürfen. Wie diese Berechtigung erteilt, entzogen und überprüft wird, zeigt der folgende Abschnitt. Für den Zugriff
256
6.3 Zugriffskontrolle auf Objekte einer bestimmten Datenbank muss ein Benutzer dann noch explizit die erforderlichen Berechtigungen erteilt bekommen. Stellen Sie sich dies zum Beispiel wie eine Tiefgarage vor, in die Ihnen der Zutritt gewährt wurde. Um ein bestimmtes Auto zu öffnen und damit loszufahren, reicht die Zutrittsberechtigung allein nicht aus. Hierfür benötigen Sie in der Regel zumindest noch einen Fahrzeugschlüssel. Abbildung 6.24 verdeutlicht noch einmal das Sicherheitskonzept von SQL Server. Die Darstellung für den Zugriff auf unterschiedliche Datenbanken ist in der Abbildung der Einfachheit halber so dargestellt, dass der Zugriff komplett gestattet oder verboten ist. Solche Einstellung ist natürlich möglich, auch wenn in der Realität sicherlich die Zugriffserlaubnisse deutlich feinkörniger basierend auf einzelnen Objekten einer Datenbank vergeben werden.
Abbildung 6.24 Sicherheitskonzept von SQL Server
6.3.1
SQL Server-Anmeldungen
Benutzer müssen sich gegenüber SQL Server authentifizieren, sich also am Server anmelden, um überhaupt Zutritt zu Datenbankobjekten zu erhalten. In der SQL ServerDokumentation wird in diesem Zusammenhang übrigens nicht von Benutzern gesprochen. Dort werden SQL Server-Anmeldungen unter dem allgemeinen Begriff Prinzipale zusammengefasst. Prinzipale können wie Sie gleich bzw. in späteren Kapiteln sehen werden nicht nur Benutzer, sondern zum Beispiel auch Windows-Gruppen, DomänenAccounts oder Prozesse sein. SQL Server bietet grundsätzlich zwei unterschiedliche Verfahrensweisen zur Authentifizierung: die integrierte Windows-Anmeldung sowie eine SQL Server-eigene Authentifizierung. Existierende SQL Server-Anmeldungen (Logins) können im Objekt-Explorer, dort im Ordner Sicherheit/Anmeldungen aufgelistet werden (Abbildung 6.25). Dort existiert auch die Möglichkeit, Anmeldungen zu bearbeiten. Nach der SQL Server-Installation sind bereits diverse Anmeldeinformationen vorhanden, von denen einige weiter unten erläutert werden.
257
6 Administration für Datenbankentwickler
Abbildung 6.25 SQL Server-Anmeldungen (Logins)
Integrierte Windows-Anmeldung SQL Server bietet die Möglichkeit, die existierenden Windows-Anmeldungen für die Authentifizierung zu verwenden. Dies hat vor allem folgende Vorteile: SQL Server kann sich auf die Windows-Anmeldung verlassen. Einstellungen wie Kennwortrichtlinien oder Ähnliches sind automatisch auch für SQL Server-Anmeldungen gültig. Bei einer Anmeldung an SQL Server werden keine Benutzerinformationen mehr über das Netzwerk übertragen. Der Aufbau einer Verbindung zu SQL Server unter Verwendung der integrierten Windows-Anmeldung ist schneller, da die Identität des Benutzers bei der Anmeldung nicht nochmals von SQL Server überprüft werden muss. Es besteht die Möglichkeit, Windows-Gruppen als SQL Server-Anmeldungen zu verwenden. Alle Mitglieder einer solchen Gruppe haben dann ebenfalls die Anmeldeberechtigung an SQL Server. Eine Verwendung von Domänen-Accounts ist ebenfalls möglich. Dies ermöglicht eine Vereinfachung der Verwaltung von SQL ServerAnmeldungen. Als Windows-Gruppen angelegte Anmeldungen werden in der Liste der Anmeldungen mit einem entsprechenden (Gruppen-)Symbol dargestellt (siehe Abbildung 6.25). Der Nachteil dieser Verfahrensweise ist, dass für Benutzer, denen die Berechtigung zur SQL Server-Anmeldung erteilt werden soll, eine entsprechende Windows-Anmeldung oder -Gruppe existieren muss. Dies kann zum Beispiel für Datenbankserver, die über das Internet zugänglich sein sollen durchaus nicht erwünscht sein. Die integrierte Windows-Anmeldung an SQL Server kann nicht deaktiviert werden.
258
6.3 Zugriffskontrolle SQL Server-eigene Anmeldung SQL Server bietet auch die Möglichkeit einer eigenen Authentifizierung. Die Verwaltung von Benutzern, Kennwörtern und entsprechenden Kennwortrichtlinien wird in so einem Fall von SQL Server selbst durchgeführt. Einstellung des Authentifizierungsmodus Sie haben die Möglichkeit zu wählen, ob Ihr SQL Server nur die reine Windows-Authentifizierung oder aber sowohl die Windows- als auch die SQL Server-Authentifizierung unterstützen soll. Hierzu haben Sie bereits bei der Installation von SQL Server die Gelegenheit. Der Authentifizierungsmodus kann aber auch im Nachhinein jederzeit über die Servereigenschaften geändert werden. Hierzu öffnen Sie aus dem Kontextmenü für den Server die Servereigenschaften und stellen auf der Seite Sicherheit den gewünschten Modus ein (Abbildung 6.26).
Abbildung 6.26 Auswahl der SQL Server-Authentifizierung
Wenn Sie den Modus ändern, so muss der SQL Server-Dienst neu gestartet werden, damit die Änderung wirksam wird.
6.3.2
Anmeldungen hinzufügen
Für das Hinzufügen von Anmeldekonten haben Sie wie so oft die Wahl zwischen der Verwendung von T-SQL und der grafischen Benutzeroberfläche des Management Studios. Um eine Anmeldung mit dem Management Studio hinzuzufügen, wählen Sie im Ordner Sicherheit des Servers den Punkt Neue Anmeldung (Abbildung 6.27).
Abbildung 6.27 Eine Anmeldung hinzufügen
259
6 Administration für Datenbankentwickler
Abbildung 6.28 Eine SQL Server-Anmeldung konfigurieren
Im Dialog Anmeldung Neu können Sie anschließend die Parameter für die Anmeldung konfigurieren (Abbildung 6.28). Die Abbildung zeigt die Konfiguration einer reinen SQL Server-Anmeldung. Hierbei können Sie auch Kennwortrichtlinien verwenden allerdings nur, wenn das verwendete Betriebssystem dies auch unterstützt. Dies ist zum Beispiel für Windows 2003 Server, aber nicht für Windows XP der Fall. Fall Sie eine Windows-Anmeldung für SQL Server erstellen, so muss diese bereits in Windows existieren. SQL Server verwalten nur eine Referenz zu dieser Anmeldung, die Sie in der Form DOMÄNE\BENUTZR oder auch DOMÄNE\GRUPPE angeben müssen. Im unteren Bereich haben Sie noch die Möglichkeit, eine Standarddatenbank und eine Standardsprache für die Anmeldung anzugeben. Beim Bestätigen des Dialogs mit OK wird die Anmeldung erzeugt, sofern das Kennwort korrekt angegeben wurde bzw. die Windows-Anmeldung existiert. Einige der weiterführenden Einstellmöglichkeiten werden wir weiter unten noch besprechen. Anmeldungen können selbstverständlich auch durch T-SQL-Anweisungen erstellt werden. Hierzu verwenden Sie CREATE LOGIN:
260
6.3 Zugriffskontrolle
6.3.3
Anmeldungen löschen
Eine existierende Anmeldung kann über das Kontextmenü für die Anmeldung gelöscht werden. Natürlich gibt es auch hierfür ein entsprechendes T-SQL-Kommando. DROP LOGIN ermöglicht das Entfernen existierender Anmeldungen, zum Beispiel so:
Beim Entfernen einer Anmeldung sollten Sie beachten, dass existierende Verbindungen zu Datenbankbenutzern ebenfalls entfernt werden. In einer Datenbank angelegte Benutzer bleiben allerdings erhalten. Für diese Benutzer ist dann aber keine Anmeldung mehr möglich. Auch das Neuanlegen der Anmeldung hilft hier nicht weiter, da jede Anmeldung im SQL Server unter einer eindeutigen Security ID (SID) geführt wird. Auch die Verknüpfung von Anmeldungen zu Datenbankbenutzern wird über diese SID angelegt. Wenn Sie eine Anmeldung löschen und anschließend neu anlegen, so bekommt sie eine neue SID, die Verbindung zu Datenbankbenutzern muss dann neu erzeugt werden. Dies können Sie erreichen, indem Sie die existierenden Datenbankbenutzer löschen und neu anlegen. Alternativ ist es auch möglich, die Zuordnung durch die Verwendung der gespeicherten Prozedur quasi zu reparieren. Wie dies genau funktioniert, erklärt die Online-Dokumentation.
6.3.4
Berechtigungen vergeben
Erzeugte Anmeldungen haben zunächst nur die Berechtigung, sich zu SQL Server zu verbinden. Normalerweise ist dies für die meisten Anmeldungen auch ausreichend. Wichtig ist die Vergabe von Zugriffsberechtigungen auf Datenbankebene, die weiter unten besprochen wird. Es ist aber natürlich möglich, Anmeldungen weiterführende Berechtigungen für Aktionen auf dem Server zu erteilen. Im Eigenschaftsdialog einer Anmeldung können Sie auf der Seite Sicherungsfähige Elemente (siehe nochmals Abbildung 6.28) eine Vielzahl von Aktionen erlauben. So ist es zum Beispiel möglich, für eine Anmeldung die Berechtigung zum Erzeugen von Datenbanken zu erteilen. Dies können Sie wieder wahlweise über die grafische Benutzeroberfläche oder über T-SQL erledigen. Das entsprechende TSQL-Kommando sieht so aus:
In den meisten Fällen werden Sie Berechtigungen für Anmeldungen allerdings über vordefinierte Serverrollen vergeben. Serverrollen Serverrollen fassen erlaubte Aktionen zusammen. Eine Anmeldung kann zu verschiedenen Serverrollen hinzugefügt werden (Abbildung 6.29).
261
6 Administration für Datenbankentwickler
Abbildung 6.29 Berechtigungen über Serverrollen vergeben
SQL Server kennt insgesamt acht unterschiedliche Rollen, die nicht geändert werden dürfen. Sie dürfen also den in vordefinierten Serverrollen enthaltenen Berechtigungssatz nicht verändern. Es ist auch nicht möglich, Serverrollen hinzuzufügen oder zu löschen. Für eine Beschreibung der in den einzelnen Rollen enthaltenen Berechtigungssätze konsultieren Sie bitte die Online-Dokumentation. Die sicherlich wichtigste Rolle ist sysadmin. Anmeldungen, die zu dieser Rolle hinzugefügt werden, haben die volle Berechtigung für alle möglichen Aktionen auf dem Server. In vielen Fällen werden Ihre Anmeldungen in nur zwei Kategorien eingeteilt werden: Anmeldungen, die der Rolle sysadmin zugeordnet wurden, und Anmeldungen, die in keine Serverrolle aufgenommen wurden.
6.3.5
Vordefinierte Anmeldekonten
Nach der Installation von SQL Server existieren bereits einige vordefinierte Anmeldungen. Hierzu zählen auch die zwei folgenden wichtigen Anmeldungen, die beide der sysadmin-Rolle zugeordnet sind: VORDEFINIERT\Administratoren. Dies ist eine Windows-Anmeldung für die Gruppe der lokalen Administratoren des Computers. Durch diese Anmeldung sind alle Computer-Administratoren auch gleichzeitig SQL Server-Administratoren. Die Anmeldung kann entfernt werden, wenn Sie dies nicht möchten.
262
6.3 Zugriffskontrolle sa. Dies ist eine reine SQL Server-Anmeldung. Diese Anmeldung kann nicht gelöscht, sondern nur deaktiviert werden. Hierzu verwenden Sie einfach das Kommando ALTER LOGIN:
Im Authentifizierungsmodus der ausschließlichen Windows-Sicherheit ist diese (reine SQL Server-)Anmeldung natürlich nicht verwendbar.
6.3.6
Datenbankbenutzer
Datenbankbenutzer ermöglichen die Konfiguration der Zugriffsberechtigungen auf Datenbankobjekte. Jeder Datenbankbenutzer muss zunächst mit einer SQL Server-Anmeldung verknüpft werden. Für das Erstellen dieser Verknüpfung gibt es verschiedene Möglichkeiten. Am einfachsten kann dies gleich beim Erzeugen der SQL Server-Anmeldung geschehen. Auf der Seite Benutzerzuordnung haben Sie die Möglichkeit, für alle auf dem Server existierenden Datenbanken auch gleich einen Benutzer anzulegen (Abbildung 6.30). Hierbei können Sie auch gleich ein entsprechendes Standardschema für den Benutzer angeben. Es ist möglich, dass der Benutzername in der Datenbank vom Anmeldenamen abweicht. Für reine SQL Server-Logins ist diese Verfahrensweise nicht zu empfehlen, da man dadurch schnell die Übersicht verlieren kann. Für Windows-Anmeldungen kann es allerdings durchaus sinnvoll sein, zum Beispiel den Teil mit dem Domänennamen für den Namen des Datenbankbenutzers wegzulassen.
Abbildung 6.30 Datenbankbenutzer anlegen
263
6 Administration für Datenbankentwickler Bitte stören Sie sich zunächst nicht an der unteren Liste mit den Datenbankrollen, hierauf werden wir in Kürze zurückkommen. Nach dem Bestätigen des Dialogs mit OK existieren in den ausgewählten Datenbanken die entsprechenden Benutzer, die Sie sich im Ordner Sicherheit für die Datenbank auch anzeigen lassen können (Abbildung 6.31).
Abbildung 6.31 Benutzer der Datenbank OmasKochbuch
Vordefinierte Benutzer So wie bei der Installation von SQL Server bereits vordefinierte Anmeldungen erstellt werden, werden auch beim Erzeugen von Datenbanken Standardbenutzer angelegt. Insbesondere enthält jede Datenbank die folgenden beiden Benutzer (siehe auch Abbildung 6.31): guest. Dies ist der Gastbenutzer einer Datenbank. Er erfüllt eine ähnliche Funktion wie der Benutzer Gast oder Jeder im Betriebssystem bzw. den IIS. Alle Anmeldungen, die nicht explizit einem Datenbankbenutzer zugeordnet wurden, greifen über den Benutzer guest bzw. dessen Berechtigungen auf die Datenbank zu. Der Benutzer muss in jeder Datenbank vorhanden sein, er kann also nicht gelöscht werden. Nach dem Erzeugen einer Datenbank ist der Benutzer allerdings zunächst deaktiviert. Wenn Sie den Zugriff über den guest-Benutzer ermöglichen wollen, so muss diesem Benutzer die Berechtigung zur Verbindung explizit erteilt werden:
dbo. Die Abkürzung steht für database owner. Dieser Benutzer ist immer mit der Administratoranmeldung verknüpft und besitzt stets alle Berechtigungen auf der Datenbank. Dieser Benutzer kann nicht gelöscht oder deaktiviert werden. Ebenso ist es nicht möglich, diesem Benutzer Berechtigungen zu entziehen.
264
6.3 Zugriffskontrolle
6.3.7
Datenbankrollen
Datenbankrollen ermöglichen die Zusammenfassung von Berechtigungen. Ein Datenbankbenutzer kann Mitglied in mehreren Rollen sein. Es ist sogar möglich, Datenbankrollen als Rollenmitglieder einer anderen Rolle aufzunehmen. Jede neu angelegte Datenbank enthält bereits eine Reihe von Standardrollen, die bestimmte Berechtigungen bündeln. Diese Rollen werden in Abbildung 6.32 gezeigt.
Abbildung 6.32 Standardrollen einer Datenbank
Für eine genaue Beschreibung der in den einzelnen Rollen enthaltenen Berechtigungen sehen Sie bitte in der Online-Dokumentation nach. Die in diesen Rollen enthaltenen Berechtigungen dürfen nicht verändert werden. Ebenso ist es nicht möglich, die Standardrollen zu löschen. Wichtig sind insbesondere die folgenden beiden Rollen: db_owner. Dies ist die Rolle für den Besitzer der Datenbank. Alle in dieser Rolle direkt oder indirekt (über andere Rollen) enthaltenen Benutzer besitzen alle Berechtigungen auf der Datenbank. public. Alle Datenbankbenutzer sind immer Mitglied in dieser Gruppe. Die Gruppenmitgliedschaft kann nicht außer Kraft gesetzt werden. Datenbankbenutzer erben Berechtigungen, aber auch den Entzug von Rechten über die Rollenmitgliedschaft. Um Datenbankbenutzer oder andere Rollen zu einer Rolle hinzuzufügen, öffnen Sie den Eigenschaftsdialog für die Rolle (Abbildung 6.33). Über den Button Hinzufügen können Benutzer und Rollen gesucht und zur ausgewählten Rolle hinzugefügt werden. Im Gegensatz zu Serverrollen können Sie selber Datenbankrollen mit entsprechenden Berechtigungssätzen definieren. Sie werden sofort erfahren, wie das funktioniert. Zuvor sind aber noch einige grundlegende Anmerkungen zur Vergabe von Zugriffsrechten notwendig.
265
6 Administration für Datenbankentwickler
Abbildung 6.33 Rollenmitgliedschaft festlegen
6.3.8
Rechte erteilen und entziehen
Die Vergabe von Rechten ist im SQL Server grundsätzlich durch die zwei folgenden Verfahrensweisen möglich: Anweisungsberechtigungen. Wie der Name vermuten lässt, kann durch diese Art von Berechtigungen das Recht zur Ausführung von T-SQL-Anweisungen erteilt werden. Anweisungsberechtigungen werden auf Datenbankebene vergeben. Wenn Sie dies über die grafische Benutzeroberfläche erledigen möchten, öffnen Sie das Eigenschaftsfenster für die Datenbank und wählen die Seite Berechtigungen aus (Abbildung 6.34). Beispiele für Anweisungsberechtigungen sind etwa CREATE TABLE, CREATE VIEW, CREATE SCHEMA oder auch SELECT, INSERT, DELETE und UPDATE. Objektberechtigungen. Über Objektberechtigungen können Zugriffsrechte auf Datenbankobjekte erteilt oder entzogen werden. Datenbankobjekte sind zum Beispiel Tabellen, Sichten, Schemas und gespeicherte Prozeduren. Für diese unterschiedlichen Objekte stehen auch jeweils unterschiedliche Berechtigungen zur Verfügung. So kann zum Beispiel für Tabellen und Sichten eine SELECT-Berechtigung erteilt bzw. entzogen werden. Für gespeicherte Prozeduren ist dies nicht möglich, hier steht die EXECUTEBerechtigung zur Verfügung, mit der Benutzern die Ausführung von Prozeduren gestattet oder verboten werden kann. Eine Besonderheit an dieser Stelle sind Schemas als
266
6.3 Zugriffskontrolle
Abbildung 6.34 Erteilung von Anweisungsberechtigungen auf Datenbankebene
Container für andere Datenbankobjekte. Eine auf einem Schema erteilte Berechtigung für Benutzer oder Rollen wie zum Beispiel SELECT oder EXECUTE wird automatisch an alle im Schema enthaltenen Objekte weitergegeben. Berechtigungshierarchie Lassen Sie sich bitte einmal die folgenden Aussagen durch den Kopf gehen, und denken Sie über die Konsequenzen nach: Ein Benutzer kann Mitglied in mehreren Datenbankrollen sein. Eine Datenbankrolle kann ebenfalls Mitglied im mehreren Datenbankrollen sein. Berechtigungen können für Rollen und Benutzer auf jeder Ebene erteilt bzw. entzogen werden. Berechtigungen können auch für T-SQL-Anweisungen erteilt und entzogen werden Die interessante Frage ist: Was passiert, wenn eine Berechtigung auf einer Ebene erteilt, auf einer anderen Ebene jedoch entzogen wurde? Sehen Sie sich hierzu bitte Abbildung 6.35 an. Die Darstellung zeigt eine geschachtelte Struktur von Rollen und Benutzern. In einigen Rollen und für den Benutzer A sehen Sie ein Symbol, das dafür steht, dass der Zugriff auf ein Objekt explizit erteilt oder entzogen wurde. Diese Berechtigung soll in allen Fällen dieselbe sein, also zum Beispiel eine UPDATE-Berechtigung auf eine bestimmte Tabelle. Interessant ist nun die Frage, ob Benutzer A, dem diese Berechtigung erteilt wurde, die Berechtigung auch tatsächlich besitzt. Die Antwort ist: Nein. Der Benutzer A ist über die Rol-
267
6 Administration für Datenbankentwickler
Abbildung 6.35 Beispiel für eine Rollenhierarchie
le Marketing auch Mitglied der Rolle Produktion, und für diese Rolle wurde die Berechtigung explizit verwehrt. Die grundlegende Aussage ist diese: Eine auf einer beliebigen Ebene entzogene Berechtigung kann niemals durch Erteilung auf einer anderen Ebene wieder erlangt werden. Somit würden also alle direkten oder indirekten Mitglieder der Rolle Produktion die dort entzogene Berechtigung nicht erhalten können egal wie. Auch die Mitglieder der Rolle Einkauf, denen das Recht ja erteilt wurde, haben keinen Zugriff. Benutzerin B besitzt allerdings die Berechtigung, die sie über die Rollenmitgliedschaft in der Rolle Verkauf bekommt. Letztlich ist diese Verfahrensweise ein Schutzmechanismus von SQL Server, der dafür sorgt, dass bei einer Ausuferung der Hierarchie von Rollen und Benutzern nicht versehentlich eine Berechtigung erteilt wird. Der Rat ist natürlich, dass Sie Ihre Benutzer und Rollen möglichst gradlinig und eindeutig organisieren sollten, also eine Struktur, wie Sie in Abbildung 6.35 dargestellt ist, möglichst vermeiden. Empfehlenswert ist auf jeden Fall auch die Benutzung von Schemas zur Vergabe von Berechtigungen auf Objektebene. Vergabe von Berechtigungen auf Objektebene Nun, Sie werden es wahrscheinlich bereits vermuten: Berechtigungen können durch entsprechende T-SQL-Anweisungen oder auch über die grafische Benutzeroberfläche des Management Studios erteilt und entzogen werden. Wenn Sie das Management Studio benutzen, so gibt es verschiedene Möglichkeiten des Einstiegs in die Konfiguration von Rechten. Hierfür können Sie den Eigenschaftsdialog für Rollen oder Benutzer öffnen und dort auf der Seite Sicherungsfähige Elemente Datenbankobjekte auswählen und Zugriffe erlauben bzw. verbieten. Möglich ist dies auch über den Eigenschaftsdialog eines Daten-
268
6.3 Zugriffskontrolle bankobjektes also zum Beispiel einer Tabelle oder eines Schemas. In diesem Dialog existiert eine Seite Berechtigungen, welche die Konfiguration der Zugriffe für Benutzer und Rollen auf dieses Objekt ermöglicht. Bevor wir dies an einem Beispiel betrachten, soll aber zunächst gezeigt werden, wie Zugriffe unter Verwendung von T-SQL eingestellt werden. Hierbei gibt es eine Besonderheit, die zu beachten ist. SQL Server stellt grundsätzlich drei Kommandos für die Einstellung von Berechtigungen zur Verfügung: GRANT. Durch die Anweisung GRANT wird einem Benutzer oder einer Rolle eine Berechtigung explizit gewährt. Hier zwei Beispiele für die Verwendung von GRANT:
DENY. Dies ist die Anweisung zum Entziehen einer Berechtigung. Wie bereits weiter oben gesagt, kann eine einmal entzogene Berechtigung nicht auf einer anderen Ebene durch GRANT wieder erteilt werden. Es ist aber natürlich möglich, das Recht auf der gleichen Ebene wieder zu erteilen. Eine Berechtigung wird zum Beispiel so entzogen:
REVOKE. Über REVOKE wird eine erteilte oder entzogene Berechtigung wieder zurückgenommen. Dies bedeutet, dass an der Stelle keine Aussage über die Berechtigung getroffen wird. Ein Benutzer oder eine Rolle wird dann die entsprechende Berechtigung über die Mitgliedschaft in einer Rolle sozusagen erben.
Wenn Sie nochmals Abbildung 6.35 betrachten, so wurde die Berechtigung dort für diejenigen Rollen bzw. Benutzer explizit erteilt, bei denen Sie das Zutrittssymbol vorfinden. Den dort dargestellten Rollen und Benutzern mit dem Verbotssymbol wurde die Berechtigung entzogen. Für die Rollen und Benutzer ohne Symbol wurde keine Aussage getroffen, diese befinden sich sozusagen im Zustand Revoked. Abschließend soll nun noch ein Beispiel für die Konfiguration von Berechtigungen mit dem Management Studio präsentiert werden. Wir werden hierfür das Schema Kochen unserer Datenbank OmasKochbuch verwenden. Nach dem Öffnen des Eigenschaftsformulars für das Schema wird zunächst auf die Seite Berechtigungen gewechselt. Wenn Sie die obigen GRANT-, DENY- und REVOKE-Anweisungen ausgeführt haben, sehen Sie eine Darstellung wie in Abbildung 6.36. Die für den Benutzer DavidBowman erteilte UPDATE-Berechtigung für das Schema ist in der Spalte Erteilen für die Berechtigung Update zu erkennen. Im oberen Bereich des Fensters können Sie über den Button Hinzufügen weitere Rollen oder Benutzer, für die eine Konfiguration vorgenommen werden soll, in die Liste der Benutzer oder Rollen aufnehmen. Die untere Liste bietet eine Auswahl aller verfügbaren Berechtigungen für das aus-
269
6 Administration für Datenbankentwickler
Abbildung 6.36 Zugriffsberechtigungen für ein Schema konfigurieren
gewählte Objekt in diesem Fall ein Schema an. Über die Spalten Erteilen oder Verweigern haben Sie die Möglichkeit, die entsprechende Berechtigung zu vergeben bzw. zu entziehen. Erteilen entspricht hierbei GRANT, Verweigern bedeutet DENY. Wenn weder Erteilen noch Verweigern ausgewählt ist, so ist diese Berechtigung im Status REVOKE. Wenn Sie die Spalte Mit Erteilung auswählen, so darf der entsprechende Benutzer eine erteilte Berechtigung weitergeben. Wenn es sich um eine Rolle handelt, so gilt dies für alle Mitglieder dieser Rolle. Seht nützlich ist der Button Effektive Berechtigungen. Beim Klick auf diesen Button wird die Berechtigungshierarchie durchsucht. Anschließend werden in einem Fenster alle tatsächlichen Berechtigungen für den ausgewählten Benutzer oder die Rolle aufgelistet.
6.4
Überwachung von SQL Server SQL Server bietet für den Administrator umfangreiche Möglichkeiten zur Systemüberwachung an. Einen ersten Einblick in die hierfür vorhandenen Mittel haben Sie bereits in Kapitel 3 erhalten, in dem gezeigt wurde, wie Berichte für Server bzw. Datenbanken erzeugt werden können. In diesem Abschnitt werden Sie eine Auswahl der zur Verfügung stehenden Möglichkeiten zur Überwachung von SQL Server kennenlernen. Hierbei werden wir wie auch sonst an
270
6.4 Überwachung von SQL Server vielen Stellen sowohl das Management Studio als auch T-SQL für die Abfrage von Informationen verwenden. Die Thematik ist enorm spannend, interessant und überdies entsprechend umfangreich. Daher ist auch hier wieder die Beschränkung auf einige wesentliche Aspekte erforderlich. Mit den an dieser Stelle präsentierten Informationen werden Sie aber in die Lage versetzt, nach Belieben tiefer in die Materie einsteigen zu können. In Kapitel 12 werden wir das Thema Überwachung dann nochmals aufgreifen und untersuchen, wie die in SQL Server gespeicherten Informationen für eine Optimierung von TSQL-Abfragen interpretiert werden können.
6.4.1
Was kann überwacht werden?
SQL Server enthält Systemobjekte, in denen der Verlauf der Serveraktivitäten gespeichert wird. Diese Systemobjekte sind in den meisten Fällen Sichten, aber auch Funktionen und gespeicherte Prozeduren. Insgesamt können Sie unter Verwendung dieser Systemobjekte Informationen zu den folgenden Bereichen erhalten: Datenbankobjekte. Hierunter fallen einerseits statische Informationen wie zum Beispiel die Struktur von Tabellen. Auf der anderen Seite können auch dynamische Informationen, wie beispielsweise die Verwendung von Indizes, abgefragt werden. Spiegelungen. Wenn Sie eine Datenbankspiegelung für eine Hochverfügbarkeit Ihrer Datenbank eingerichtet haben, so können Sie die Parameter dieser Konfiguration natürlich auch abfragen. Common Language Runtime (CLR). Die Integration der CLR in SQL Server erlaubt die Erstellung von Datenbankobjekten in einer .NET-Programmiersprache. SQL Server ermöglicht die Ausgabe von Informationen über die entsprechenden Datenbankobjekte und Assemblies. Wir werden die Thematik CLR Integration in Kapitel 10 behandeln. Endpunkte. SQL Server kann Endpunkte für die Kommunikation mit der Außenwelt bereitstellen. Solche Endpunkte werden zum Beispiel bei der Spiegelung oder beim Einsatz des Service Brokers verwendet. In diesem Buch finden Sie in Kapitel 11 weiterführende Informationen über die Erstellung von http-Endpunkten mit SQL Server. Partitionen. Für große Tabellen oder Indizes können Partitionen erstellt werden, wodurch eine Aufspaltung dieser Objekte in mehrere physikalische Dateien möglich ist. Falls Sie Partitionierung verwenden, bekommen Sie von SQL Server auch Informationen zum Aufbau dieser Partitionen und der zugehörigen Steuerungsmechanismen. Ausführungsumgebung. Hierunter fällt einerseits die Ausführung von T-SQL-Anweisungen, andererseits können auch Informationen zu Verbindungen erhalten werden. Datenbanken und Speicher. Es ist möglich, den Speicherplatzbedarf von Datenbanken, Datenbankdateien oder auch von einzelnen Tabellen bzw. Indizes abzufragen. Sie können weiterhin Informationen über den E/A-Durchsatz je Datenbank erhalten. Service Broker. Bei Einsatz des Service Brokers werden spezielle Datenbankobjekte wie beispielsweise Nachrichtentypen, Warteschlangen oder Verträge erzeugt, über
271
6 Administration für Datenbankentwickler die der Server natürlich auch Auskunft geben kann. Der Service Broker ist Gegenstand des Kapitels 9. Sicherheit. Hiermit sind zum einen Informationen über angelegte Anmeldungen oder Datenbankbenutzer gemeint. Erteilte bzw. entzogene Berechtigungen auf Anweisungsund Objektebene können ebenso abgefragt werden. Transaktionen. Dieser Bereich betrifft zum Beispiel Informationen zu Sperren und Blockierungen oder zu aktiven Snapshots (in der entsprechenden Isolierungsstufe) Server und Betriebssystem. Hierunter fallen zum Beispiel Wartezustände des Servers oder auch dessen Hauptspeicherverwendung. Ebenso können (insgesamt über 800) Leistungsindikatoren abgefragt werden, die zum Beispiel Auskunft über durchschnittliche Wartezeiten zum Erlangen einer bestimmten Sperre oder die Anzahl von Deadlocks geben. Ablaufverfolgung und Ereignisse. SQL Server enthält Sichten, Funktionen und Prozeduren zur Erstellung und Auswertung von Ablaufverfolgungen. In Kapitel 12 wird diese Thematik eingehender behandelt.
6.4.2
Überwachung mit dem SQL Server Management Studio
Das SQL Server Management Studio ist das zentrale Arbeitsinstrument für den Administrator. Als solches bietet es natürlich Möglichkeiten sowohl zur Konfiguration des Servers als auch für die Überwachung desselben. Über das Kontextmenü der SQL Server-Instanz können Servereinstellungen angezeigt und geändert werden. Dies haben wir bereits in Abschnitt 6.3.1 für die Konfiguration der SQL Server-Authentifizierung getan. Schauen Sie sich bitte nochmals Abbildung 6.26 an, um sich zu erinnern. Das Management Studio bietet weitere Möglichkeiten zur Überwachung der aktuellen oder vergangenen Serveraktivitäten: SQL Server-Protokolle SQL Server protokolliert bestimmte Ereignisse in Textdateien. Diese Dateien finden Sie im Verzeichnis:
Jede auf einem Computer installierte SQL Server-Instanz besitzt hierbei ein eigenes Protokollverzeichnis. Die Angabe von .1 im obigen Pfad steht gerade für die erste SQL Server-Instanz oder die Standardinstanz. Bei jedem Neustart von SQL Server wird eine neue Protokolldatei mit dem Namen ERRORLOG in diesem Verzeichnis erzeugt, in die dann von der laufenden Instanz Einträge aufgenommen werden. Dies ist also das aktuelle Protokoll. Darüber hinaus archiviert SQL Server sechs weitere Protokolldateien mit Namen ERRORLOG.1
ERRORLOG.6. Der Name ERRORLOG ist hierbei etwas verwirrend, denn die Dateien enthalten nicht nur Fehler, sondern auch Informationen. Sie können die Dateien einfach mit einem Texteditor öffnen. Komfortabler ist die im Ordner Verwaltung\SQL Server-Protokolle des Objekt-Explorers angebotene Möglichkeit zur Betrach-
272
6.4 Überwachung von SQL Server
Abbildung 6.37 SQL Server-Protokolle
tung der Protokolle (Abbildung 6.37). Dort können Sie sowohl die archivierten als auch das aktuelle Protokoll zur Ansicht auswählen. Nach einem Doppelklick auf ein Protokoll öffnet sich der Protokolldatei-Viewer (Abbildung 6.38). In diesem Fenster können Sie zum Beispiel auch andere Protokolldateien (die Sie eventuell archiviert haben) laden oder bestimmte Protokolleinträge suchen bzw. nach diesen Filtern. Die Abbildung zeigt die Protokolleinträge für die automatische Wiederherstellung der Datenbanken beim Serverstart.
Abbildung 6.38 Das aktuelle SQL Server-Protokoll
Aktivitätsmonitor Der Aktivitätsmonitor gibt Informationen über alle derzeit offenen Verbindungen zu einer SQL Server-Instanz aus. Sie finden den Aktivitätsmonitor von SQL Server im Ordner Verwaltung (siehe nochmals Abbildung 6.37). Im Monitor selber können Sie zwischen
273
6 Administration für Datenbankentwickler
Abbildung 6.39 Prozessinformation im Aktivitätsmonitor
drei unterschiedlichen Sichten auf die aktuellen Aktivitäten wählen, indem Sie die entsprechende Seite im linken Bereich des Fensters auswählen (Abbildung 6.39). Die Abbildung zeigt die aktuelle Aktivität der existierenden Verbindungen. Die Prozess-ID in der linken Spalte enthält nur Prozesse, deren ID größer als 50 ist. Dies sind gerade alle Benutzerverbindungen. IDs bis 50 sind für die interne Verwendung durch SQL Server-eigene Prozesse reserviert. In der Spalte Status ist zu sehen, dass keiner der Prozesse aktiv ist alle befinden sich im Status sleeping. Die Prozess-ID 51 ist die vom Objekt-Explorer verwendete Verbindung. Auch der Aktivitätsmonitor selber taucht natürlich in der Liste der Prozesse auf, er hat hier die Prozess-ID 52. Der Aktivitätsmonitor kann auch Sperren auf Datenbankobjekten und Serverressourcen darstellen. Für eine entsprechende Anzeige können Sie eine der anderen beiden Seiten Sperren nach Prozess oder Sperren nach Objekt öffnen. Dies ist besonders dann nützlich, wenn bestimmte Verbindungen nicht mehr reagieren, um herauszufinden, warum bzw. von wem eine solche Verbindung gerade blockiert wird. Berichte Bereits in Kapitel 3 haben Sie die Möglichkeiten der Berichtserzeugung für unterschiedlich Serverbereiche und Datenbanken kennengelernt. Für eine SQL Server-Instanz selber stehen ebenfalls Berichte zur Verfügung. Falls Sie eine SQL Server-Instanz überwachen müssen, so kann ich Ihnen nur nahelegen, sich mit den bereitgestellten Berichten auseinanderzusetzen.1 Diese Berichte sind wirklich sehr nützlich und informativ. Abbildung 6.40 zeigt einige der verfügbaren Berichte. 1
274
Ab dem Service Pack 2 von SQL Server haben Sie auch die Möglichkeit, eigene Berichte hinzuzufügen.
6.4 Überwachung von SQL Server
Abbildung 6.40 Verfügbare Berichte für eine SQL Server-Instanz (Auszug)
Mit der Bezeichnung erste Abfragen sind hier stets diejenigen Abfragen gemeint, die den meisten Ressourcenverbrauch jeweils bezogen auf die erwähnte Ressource haben. Abbildung 6.41 zeigt ein Beispiel für den Bericht Leistung Erste Abfragen nach durchschnittlicher EA. Dieser Bericht enthält im oberen Bereich eine grafische Übersicht über die erzeugten logischen und physikalischen Lese- bzw. Schreibvorgänge. Unterhalb der beiden Diagramme finden Sie dann eine detaillierte Übersicht.
Abbildung 6.41 Beispielbericht: Leistung Erste Abfragen nach durchschnittlicher EA
275
6 Administration für Datenbankentwickler
6.4.3
Überwachung mit dem SQL Server Profiler
Der bereits in Kapitel 3 erwähnte SQL Server Profiler ist für den Datenbankentwickler ein wichtiges Instrument für die Fehlersuche. Der Profiler ermöglicht es Ihnen, genau zu beobachten, in welcher Form vom Client abgeschickte Anfragen tatsächlich am Server ankommen. Mit dem Profiler können Sie unter anderem die Ausführungsdauer von Abfragen oder die von Abfragen erzeugte CPU-Last protokollieren. Beim Start von SQL Server wird übrigens immer eine Ablaufverfolgung ausgeführt, die allerdings nur minimale Informationen sammelt sozusagen als Flugschreiber. Die Daten dieser Protokollierung finden Sie ebenfalls im LOG-Verzeichnis der SQL Server-Instanz. Dies sind die Dateien mit der Endung .trc (für Trace). Beim Doppelklick auf eine solche Datei wird diese im Profiler geöffnet und kann dann dort durchsucht werden. Wir werden den Profiler an dieser Stelle nicht weiter behandeln, in Kapitel 12 aber nochmals auf ihn zurückkommen.
6.4.4
Überwachung mit dem Windows-Systemmonitor
Nach der Installation von SQL Server stehen Ihnen im Windows-Systemmonitor über 800 zusätzliche Indikatoren in 23 Kategorien zur Verfügung, die Sie überwachen und protokollieren können. Hierzu zählen zum Beispiel die Größe von Datenbankdateien, die Nutzung der CPU, die Anzahl von Sperren und Deadlocks oder die Trefferquote im Datencache von SQL Server. Abbildung 6.42 zeigt den Systemmonitor in Aktion.
Abbildung 6.42 Der Windows-Systemmonitor zur Überwachung von SQL Server-Indikatoren
276
6.4 Überwachung von SQL Server Etwas weiter unten werden Sie sehen, wie Sie die Leistungsindikatoren auch ohne Verwendung des Systemmonitors abfragen können. In Kapitel 12 werden wir den Systemmonitor zusammen mit dem Profiler verwenden, um Abfragen zu finden, die eine hohe Serverlast erzeugen.
6.4.5
Verwendung von T-SQL zur Überwachung
SQL Server enthält insgesamt einige hundert Systemobjekte, mit denen Informationen zum Server abgefragt, aber auch geändert werden können. Diesbezüglich gibt es eine gute und eine schlechte Nachricht. Die gute Nachricht ist, dass Sie bei Beherrschung dieser Objekte ein enorm mächtiges Instrument zur Untersuchung von Server- und Datenbankobjekten in der Hand halten. Die schlechte Nachricht ist natürlich, dass diese Thematik genauso komplex wie nützlich ist. Ich kann Ihnen aber nur wärmstens empfehlen, sich mit den zur Verfügung stehenden Systemsichten und Prozeduren einmal auseinanderzusetzen. Für die Systemsichten finden Sie eine sehr gute Übersicht im PDF-Format auf der Microsoft Web Site (Googeln Sie einfach SQL Server 2005 System Views). Das entsprechende Dokument wird auch auf der Website zu diesem Buch zu finden sein. Allerdings können sich mit der Auslieferung von Service Packs natürlich Änderungen ergeben, sodass Sie die jeweils aktuelle Version vorzugsweise von der Microsoft-Website herunterladen sollten. Im folgenden Teil kann wieder nur eine Einführung in die Verwendung der Systemsichten bzw. -prozeduren gegeben werden. Später im Kapitel 12 werden wir dann nochmals Systemsichten einsetzen, um Abfragen zu untersuchen. Kataloginformationen Natürlich kann SQL Server auch Auskunft über Datenbanken und Datenbankobjekte geben. Hierzu zählen zum Beispiel die Auflistung der überhaupt existierenden Datenbankobjekte oder der Aufbau von Tabellen, also etwa Informationen über Tabellenspalten oder Einschränkungen. Für die Abfrage dieser sogenannten Metadaten oder Kataloginformationen stehen Ihnen in jeder Datenbank spezielle Systemsichten in zwei unterschiedlichen Schemas zur Verfügung: INFORMATION_SCHEMA. Der ANSI-Standard schreibt für die Abfrage von Metadaten die Implementierung dieses Schemas vor. In dem Schema finden Sie entsprechende Sichten für derartige Abfragen (Abbildung 6.43).
277
6 Administration für Datenbankentwickler
Abbildung 6.43 INFORMATION_SCHEMA-Sichten
Die INFORMATION_SCHEMA.TABLES-Sicht gibt zum Beispiel Informationen für die in der Datenbank enthaltenen Tabellen zurück, INFORMATION_SCHEMA.COLUMNS liefert diese Information für die Tabellenspalten. Beide Sichten können durch einen JOIN verknüpft werden:
Einen Ausschnitt aus dem Ergebnis der Abfrage zeigt Abbildung 6.44.
278
6.4 Überwachung von SQL Server
Abbildung 6.44 Spalten der Tabelle Extras.Kalender
Das Schema sys. Ebenfalls in jeder Datenbank finden Sie das Schema sys. In diesem Schema ist unter anderem eine Reihe von Systemsichten enthalten, die eine SQL Server-eigene Sicht auf die Metainformationen liefern. So gibt es dort zum Beispiel die Sichten und , die auch zur Abfrage von Informationen über Tabellen und die enthaltenen Spalten benutzt werden können. Darüber hinaus liefern die in diesem Schema enthaltenen Sichten auch Informationen über physikalische Parameter, wie zum Beispiel die Verwendung von Betriebssystemdateien von Datenbanken, was mit den INFORMATION_SCHEMA-Sichten die nur eine logische Sicht auf Datenbankobjekte gestatten nicht möglich ist. Systemprozeduren In Kapitel 2 wurden bereits kurz gespeicherte Prozeduren erwähnt, die T-SQL-Code unter einem Namen im Server ablegen. Der enthaltene Code kann dann einfach durch den Aufruf des Prozedurnamens ausgeführt werden. Im anschließenden Kapitel 7 werden wir eigene gespeicherte Prozeduren entwickeln. SQL Server enthält auch eine Reihe von Systemprozeduren, die ebenfalls zur Abfrage von Serverinformationen, aber auch zum Verändern von Parametern, also zur Serverkonfiguration verwendet werden können. Diese Prozeduren sind ebenfalls im Schema sys einer Datenbank enthalten. Die folgende Abfrage legt eine Datenbank mit den Standardeinstellungen an und gibt anschließend die Anzahl der enthaltenen gespeicherten Systemprozeduren zurück:
279
6 Administration für Datenbankentwickler Das Ergebnis der obigen Abfrage (SQL Server Developer Edition mit Service Pack 2) ist 1261! Es erübrigt sich wohl fast zu erwähnen, dass an dieser Stelle nicht alle gespeicherten Systemprozeduren behandelt werden, sondern dass wir uns auf eine kleine Auswahl beschränken müssen. Ehrlich gesagt, bin ich bislang auch noch niemandem begegnet, der von sich selber behaupten konnte, alle dieser Prozeduren zu kennen. Es gibt natürlich Prozeduren, die in der täglichen Arbeit sehr häufig eingesetzt werden. Hierzu zählen vor allem die folgenden (in alphabetischer Reihenfolge): sp_configure. Mit dieser Prozedur können serverweite Optionen eingestellt und abgefragt werden. Die Prozedur wird folgendermaßen aufgerufen:
Der Parameter option bezeichnet die Option, für welche die Einstellung geändert oder abgefragt werden soll. Über den zweiten Parameter wert wird die neue Einstellung für die Option angegeben. Beide Parameter sind optional. Wird wert nicht angegeben, dann erhalten Sie die aktuelle Einstellung für die Option. Wenn Sie option ebenfalls weglassen, so bekommen Sie als Ergebnis die Einstellung für alle Optionen. Abbildung 6.45 zeigt einen Ausschnitt aus dem Ergebnis des Prozeduraufrufs ohne Parameter.
Abbildung 6.45 Auszug aus dem Ergebnis des Aufrufs von sp_configure
In der Spalte config_value sehen Sie den jeweils konfigurierten Wert. Die Spalte run_value zeigt den gerade gültigen Wert. Nach dem Ändern einer Option muss die Funktion aufgerufen werden, damit die Änderung wirksam wird. config_value wird dadurch sozusagen in run_value übernommen. Etwas weiter unten finden Sie ein Beispiel hierfür. Bei einigen wenigen Änderungen ist es sogar erforderlich, den SQL Server-Dienst anschließend neu zu starten, damit der konfigurierte Wert wirksam wird. kennt auch eine Option show advanced options, mit der festgelegt
werden kann, dass erweiterte Optionen ebenfalls angezeigt oder geändert werden dürfen. Die obige Abbildung zeigt das Ergebnis des Prozeduraufrufs mit dem Wert 1 für show advanced options. Ist diese Option auf 0 gesetzt, so würde nur eine einge-
280
6.4 Überwachung von SQL Server schränkte Liste angezeigt. Die Bezeichnung show advanced options ist ein wenig irreführend, da auch keine erweiterten Optionen geändert werden dürfen, wenn der Wert für diese Option 0 ist. sp_help. Mit dieser Prozedur können Informationen zu Datenbankobjekten abgefragt werden. Die Prozedur erwartet hierfür als einzigen Parameter den Namen des Datenbankobjektes. Dieser Name kann zum Beispiel eine Tabelle, ein Schema oder auch eine gespeicherte Prozedur sein. Die von der Prozedur erzeugte Ausgabe variiert je nach Objekttyp. So werden zum Beispiel für eine Tabelle deren Struktur, der Primärschlüssel, Informationen über existierende Indizes und existierende Referenzen ausgegeben. Wenn das Datenbankobjekt eine Prozedur ist, so erhalten Sie Auskunft über den Besitzer der Prozedur, ihre Erstellungszeit sowie die von der Prozedur erwarteten Parameter. Da ebenso eine Prozedur ist, können Sie diese Informationen natürlich auch für bekommen:
Das Ergebnis des Prozeduraufrufs sieht so aus, wie in Abbildung 6.46 gezeigt.
Abbildung 6.46 Ergebnis des Aufrufs von sp_help 'sp_help'
Es gibt ungefähr noch 90 weitere sp_help-Prozeduren zur Abfrage von Informationen zu Datenbankobjekten, aber auch Servereinstellungen Hierzu gehören zum Beispiel die folgenden:
Liefert Informationen zu Datenbanken.
Liefert Informationen über den Code von gespeicherten Prozeduren, benutzerdefinierten Funktionen oder Sichten.
Gibt Informationen über Indizes zurück.
Liefert Informationen über registrierte Server.
Es lohnt sich auf jeden Fall, in der Online-Dokumentation nach sp_help zu suchen und die gefundenen Prozeduren einfach einmal auszuprobieren. sp_spaceused. Diese Prozedur gibt Informationen über den von einer Tabelle oder Sicht verwendeten Speicherplatz zurück. Eine Sicht belegt nur dann Speicherplatz, wenn ein Index für diese Sicht erzeugt wurde. Indizes werden wir in Kapitel 12 behandeln. Abbildung 6.47 zeigt die Ausgabe der Prozedur für den Aufruf:
281
6 Administration für Datenbankentwickler
Abbildung 6.47 Ergebnis des Aufrufs von
sp_who. Die Prozedur gibt Informationen zu existierenden Verbindungen zu SQL Server zurück. Der Prozedur kann auch ein Anmeldename als Parameter übergeben werden, um nur Informationen für Verbindungen zu erhalten, die unter dieser Anmeldung geöffnet wurden. Wenn Sie ACTIVE als Anmeldenamen übergeben, dann werden nur die gerade aktiven Verbindungen ausgegeben. Abbildung 6.48 auf der nächsten Seite zeigt einen Ausschnitt aus dem Ergebnis für den Aufruf ohne Parameter. Es ist zu erkennen, dass auch SQL Server-interne Prozesse in der Ausgabe enthalten sind. Dies sind die Zeilen mit einem Wert kleiner 50 in der Spalte spid. xp_cmdshell. Nein, dies ist kein Schreibfehler. Die Prozedur heißt tatsächlich so. SQL Server stellt auch eine Reihe von sogenannten erweiterten gespeicherten Prozeduren zur Verfügung, deren Namen allesamt mit xp_ beginnen. Diese Prozeduren sind nicht in T-SQL, sondern in einer Programmiersprache wie C oder C++ implementiert.
Abbildung 6.48 Ausschnitt aus dem Ergebnis des -Aufrufs
Die Entwicklungsumgebung Visual Studio 6.0 enthält eine entsprechende Projektvorlage für die Entwicklung solcher Prozeduren, die innerhalb von DLLs existieren und vom Server ausgeführt werden. Mit können Sie Kommandos auf der Windows-Systemebene ausführen, also zum Beispiel so etwas:
Als Ergebnis bekommen Sie eine Liste aller Dateien im Verzeichnis C:\. ermöglicht auch dies:
kann also auch für Angriffe auf den Server bzw. sogar das gesamte Netzwerk missbraucht werden. Aus diesem Grund ist die Verwendung von nach der In-
282
6.4 Überwachung von SQL Server stallation von SQL Server zunächst nicht gestattet. Wenn Sie einsetzen möchten, so müssen Sie dieses Feature explizit aktivieren. Dies kann zum Beispiel über den folgenden Aufruf von geschehen:
Alternativ kann die Aktivierung auch mit der SQL Server-Oberflächenkonfiguration vorgenommen werden. Erweiterte gespeicherte Prozeduren werden nur noch aus Gründen der Abwärtskompatibilität unterstützt. Für Neuentwicklungen sollten Sie in jedem Fall auf die Möglichkeit der .NET-Integration zurückgreifen, so wie dies in Kapitel 10 besprochen wird. Die recht kurze Aufzählung soll an dieser Stelle zunächst abgeschlossen werden. In den folgenden Kapiteln werden wir noch das eine oder andere Mal auf Systemprozeduren zurückkommen und dann auch einige weitere kennenlernen. Dynamische Verwaltungssichten (Dynamic Management Views) SQL Server protokolliert sämtliche Aktionen und führt dadurch eine Historie, zum Beispiel über durchgeführte Abfragen, den Ressourcenverbrauch innerhalb von Verbindungen oder etwa aufgetretene Blockierungen innerhalb von Transaktionen. Diese Protokolldaten können über sogenannte dynamische Verwaltungssichten abgefragt werden. SQL Server stellt insgesamt über 70 dieser Sichten zur Verfügung von denen manche in Wirklichkeit allerdings keine Sichten, sondern Funktionen sind. Die dynamische Verwaltungssichten ermöglichen die Abfrage von Informationen aus allen im Punkt 6.4.1 aufgezählten Bereichen. Bevor wir einige dieser Sichten und Funktionen betrachten, soll aber noch Folgendes erwähnt werden: 1. Bei jedem Neustart von SQL Server beginnt die Protokollierung der dynamischen Informationen von vorne. Anders gesagt: Die existierenden Protokolle werden gelöscht, wenn der SQL Server-Dienst beendet wird. 2. SQL Server sorgt dafür, dass durch die dynamische Protokollierung der Server nicht übermäßig belastet wird. Viele der Protokolle belegen ihrerseits Serverressourcen, wie zum Beispiel Hauptspeicher. Wenn die Protokollierung zu viele dieser Ressourcen benötigt, so werden ältere Einträge aus dem Protokoll entfernt. Die verfügbare Historie wird dadurch also nicht unbedingt bis zum letzten Serverstart zurückreichen. 3. Die bereits an mehreren Stellen erwähnten Berichte, die Ihnen im Management Studio für die Überwachung zur Verfügung stehen, benutzen ebenfalls sehr häufig die dynamischen Verwaltungssichten. Die Namensgebung für dynamische Verwaltungssichten folgt einem bestimmten Muster. Zunächst einmal sind alle dynamischen Verwaltungssichten im Schema sys jeder Datenbank gespeichert. Alle Namen beginnen stets mit dm_. Schauen Sie sich die Systemsichten einer beliebigen Datenbank ruhig einmal an, und filtern Sie den Ordner nach dm_ (Abbildung 6.49 auf der nächsten Seite). Der nächste Teil des Namens (also derjenige Teil
283
6 Administration für Datenbankentwickler nach dem ersten Unterstrich) steht dann für den Bereich, für den die Sicht Informationen liefert. Beispiele für diesen Teil sind etwa db für Datenbank, os für Betriebssystem, exec für Abfrageausführung oder auch tran für Transaktionen. Wir werden auf die dynamischen Verwaltungssichten in den verbleibenden Kapiteln dieses Buches noch des Öfteren zurückkommen. An dieser Stelle sollen nur einige Beispiele aufgeführt werden, um Ihnen das Potenzial dieser Sichten zu zeigen. So gibt es zum Beispiel die Sicht , in der alle Leistungsindikatoren von SQL Server protokolliert werden. Wie bereits gesagt, sind dies insgesamt etwa 800. In Kapitel 5 haben wir diese Sicht bereits verwendet, um die Anzahl der aufgetretenen Deadlocks abzufragen. Um
Informationen
über
den
Datendurchsatz
zu
erhalten,
kann
die
Sicht
verwendet werden. In dieser Sicht werden die Bytes
für Lese- und Schreiboperationen jeder Datenbankdatei summiert. Die folgende Abfrage ermittelt ein entsprechendes Ergebnis:
Abbildung 6.49 Dynamische Verwaltungssichten (Auszug)
284
6.4 Überwachung von SQL Server Die Systemsicht enthält Informationen über die Zuordnung von Datenbankdateien zu Datenbanken. Diese Sicht kann zur obigen Abfrage hinzugefügt werden, um den E/A-Durchsatz für Datenbanken und Dateien zu erhalten:
Die Abfrage gibt auch die Wartezeit zurück, die Prozesse auf den Abschluss von E/AOperationen warten mussten. Angezeigt wird weiterhin der Typ der Datenbankdatei (Protokoll oder Daten). Ein Beispiel für ein Ergebnis zeigt Abbildung 6.50.
Abbildung 6.50 E/A-Durchsatz je Datenbankdatei
Ein abschließendes Beispiel soll noch zeigen, wie dynamische Verwaltungssichten verwendet werden können, um Informationen über aktive Verbindungen abzufragen, also festzustellen, was gerade auf dem Server los ist. Hierzu kann die Sicht verwendet werden, in der Information über aktive Verbindungen geführt werden. Die Sicht enthält die Information über die Aktivitäten der aktiven Verbindungen. Beide Sichten können folgendermaßen miteinander verknüpft werden:
285
6 Administration für Datenbankentwickler Die Abfrage filtert nur Benutzerverbindungen heraus, die eine SessionID von größer 50 besitzen. Ein Beispiel für ein Ergebnis sehen Sie in Abbildung 6.51:
Abbildung 6.51 Aktivität der aktuellen Benutzerverbindungen
Die obige Abfrage kann nützlich sein, wenn der Server ausgelastet ist. Unter Umständen ist es möglich, den Prozess, der die Last erzeugt, zu identifizieren. Systemfunktionen SQL Server bietet eine Reihe von Systemfunktionen für die Überwachung von Server- und Datenbankeinstellungen. Die für diese Zwecke wichtigsten Funktionen wurden in Kapitel 4 bereits vorgestellt und werden daher an dieser Stelle nicht nochmals präsentiert.
6.5
Zusammenfassung In diesem Kapitel haben Sie eine Einführung in die Aufgaben eines SQL ServerAdministrators erhalten. Sie wissen jetzt in Grundzügen, wie Datensicherungen organisiert werden und wie Sie im Fehlerfall eine Wiederherstellung vornehmen können. Ganz nebenbei haben Sie hierbei auch gesehen, wie der SQL Server-Agent für eine zeitplangesteuerte Auftragsausführung eingesetzt werden kann. Sie kennen nun die zur Verfügung stehenden Möglichkeiten für den Schutz von SQL Server und Datenbanken vor unbefugten Zugriffen und können sowohl SQL ServerAnmeldungen als auch Datenbankbenutzer einrichten. Der letzte Teil dieses Kapitels beschäftigte sich mit der Überwachung des Servers durch das SQL Server Management Studio bzw. Abfragen der SQL Server-Systemobjekte. Noch ein abschließendes Wort zu den Systemsichten: Wenn Sie tiefer in die Thematik einsteigen möchten, so ist es eine sehr gute Idee, den Code von gespeicherten Systemprozeduren zu inspizieren. Viele dieser Prozeduren bieten letztlich eine Schnittstelle zur Abfrage oder Veränderung von Systemsichten, verwenden diese Sichten also intern. Wenn Sie sich den Code zum Beispiel von oder ansehen, so können Sie genau erkennen, wo die Informationen letztlich herkommen. Hierzu können Sie zum Beispiel einfach
aufrufen.
286
7 T-SQL-Programmierung Nachdem in Kapitel 4 die Grundlagen von T-SQL behandelt wurden, wollen wir in diesem Kapitel nun in die erweiterten Möglichkeiten von T-SQL einsteigen und sehen, welche Sprachelemente T-SQL für die Programmierung bietet.
7.1
Variablen Wie alle Programmiersprachen, so erlaubt auch T-SQL die Verwendung von Variablen in Ausdrücken. Eine T-SQL-Variable beginnt mit dem Zeichen @.
7.1.1
Deklaration von Variablen
Variablen müssen vor ihrer ersten Verwendung deklariert werden. T-SQL erlaubt nur getypte Variablen, d. h., bei der Deklaration muss der Typ der Variablen angegeben werden1. Als Datentypen stehen Ihnen hierbei die bekannten T-SQL-Typen zur Verfügung. Die Deklaration einer Variablen wird durch das Schlüsselwort DECLARE vorgenommen. Hierbei können in einer DECLARE-Anweisung auch mehrere Deklarationen stehen, die dann durch Komma getrennt sein müssen:
Eine Variable ist nur in dem T-SQL-Stapel gültig, in dem sie deklariert wurde. Das Stapeltrennzeichen GO erklärt also auch alle Variablen-Deklarationen des abgeschlossenen Stapels für ungültig.
1 Wir
wollen hier auch SQL_VARIANT als einen speziellen Datentyp ansehen.
287
7 T-SQL-Programmierung
7.1.2
Wertzuweisung an Variablen
T-SQL bietet keine Möglichkeit, einer Variablen bereits bei der Deklaration einen Standardwert zuzuweisen. Unmittelbar nach der Deklaration ist der Wert einer Variablen unabhängig vom verwendeten Datentyp NULL, die Variable ist also zunächst leer. Damit eine Variable sinnvoll verwendet werden kann, muss ihr natürlich ein Wert zugewiesen werden können. Hierfür gibt es in T-SQL prinzipiell zwei Möglichkeiten: Wertzuweisung durch SET Der Wert einer Variablen kann durch die Anweisung SET wie folgt festgelegt werden:
Es ist nicht möglich, etwa so wie bei DECLARE, mehrere Zuweisungen in einer SETAnweisung auszuführen. Jede Zuweisung muss also in einer separaten SET-Anweisung erfolgen. Möglich ist auch die Zuweisung eines Wertes aus einer Abfrage an eine Variable durch SET. Hierfür darf die Abfrage allerdings nur eine einzige Zeile und auch nur eine Spalte liefern. Hier ist ein Beispiel für eine derartige Zuweisung:
Die SELECT-Anweisung muss also in runde Klammern eingeschlossen werden und darf wie gesagt nur eine Zeile zurückliefern, ansonsten wird die Zuweisung nicht funktionieren. Wenn Sie in der obigen Anweisung die WHERE-Bedingung weglassen, erhalten Sie folgende Fehlermeldung:
Das obige T-SQL-Skript zeigt auch, wie der Wert einer Variablen über eine SELECTAnweisung im Ergebnisbereich ausgegeben werden kann. Es ist auch möglich, eine Wertzuweisung an eine Variable basierend auf dem Wert der Variablen vorzunehmen. Eine Variable kann also sowohl auf der linken als auch auf der rechten Seite des Zuweisungsoperators der SET-Anweisung stehen. Dadurch kann zum Beispiel der Wert einer Integer-Variablen erhöht werden:
Diese Möglichkeit kann in UPDATE-Anweisungen für die Vergabe einer laufenden Nummerierung genutzt werden. Eine Update-Anweisung verwendet auch eine SET-Anweisung zum Zuweisen neuer Spaltenwerte. In einer solchen SET-Anweisung können natürlich ebenso Ausdrücke mit Variablen verwendet werden. Schauen Sie sich bitte einmal das folgende Skript an, das eine Tabelle erzeugt und anschließend einige Zeilen hinzufügt:
288
7.1 Variablen
Die angelegte Tabelle Namen enthält eine Spalte Nummer, deren Werte nach der obigen INSERT-Anweisung allesamt 0 sind. Um nun diese Spalte als laufende Nummer einzurichten, kann das folgende Skript verwendet werden:
Im Skript wird eine spezielle UPDATE-Syntax benutzt, die zwei Zuweisungsoperatoren in der SET-Anweisung verwendet. Hierdurch wird der Wert der Spalte Nummer, beginnend bei 1 und dann um jeweils den Wert 1 inkrementiert, gesetzt. Einen Ausschnitt aus dem Inhalt der Tabelle zeigt Abbildung 7.1.
Abbildung 7.1 Laufende Nummer
Wertzuweisung durch SELECT Auch die SELECT-Anweisung ermöglicht die Zuweisung von Werten an Variablen. Die generelle Syntax einer solchen Anweisung sieht so aus:
289
7 T-SQL-Programmierung Es ist nicht möglich, die Zuweisung mit einer Datenabruf-Operation zu verbinden, um eine Ergebnismenge an den Client zurückzugeben. Versuchen Sie dies, so erhalten Sie eine Fehlermeldung. Wenn also Variablenzuweisungen erfolgen, so muss dies stets für alle Spalten der SELECT-Anweisung geschehen. Im Gegensatz zu SET kann die SELECT-Anweisung allerdings mehrere Zeilen zurückgeben. Die Variable enthält dann nach der Anweisung den Wert des angegebenen Ausdrucks für die letzte Zeile. Folgendes funktioniert also:
Die Variable enthält nach der Zuweisung den Wert der letzten zurückgelieferten E-Mail-Adresse. Interessant hierbei ist, dass die Variable auch während der Abarbeitung der SELECT-Anweisung auf die entsprechenden Werte des Ausdrucks gesetzt wird, also sozusagen alle Werte nacheinander durchläuft. Dies kann man sich in einigen Situationen zunutze machen, um zum Beispiel eigene Aggregate zu berechnen. Das folgende Skript nutzt ebenfalls dieses Verhalten aus, um alle Nachnamen von Angestellten, durch Semikolon getrennt, in eine Zeichenkette zu übertragen:
In der SELECT-Anweisung wird an die Variable stets der existierende Wert dieser Variablen, an denen noch der gerade abgefragte Wert der Spalte LastName angehängt wird, zugewiesen. Hierbei wird auch ein Semikolon als Separator eingefügt. Nach der Deklaration der Variablen ist der Wert dieser Variablen zunächst NULL. Die erste Berechnung des Spaltenausdrucks wird dadurch nur den Wert der Spalte LastName in die Variable eintragen, die dadurch einen Wert ungleich NULL enthält. Alle weiteren Auswertungen werden dann ein Semikolon und den Spaltenwert an den bereits existierenden Wert der Variablen anfügen. Die ersten Zeichen des Ergebnisses der abschließenden PRINT-Anweisung sehen so aus:
7.1.3
Besondere Variablentypen
Neben den bereits aus Tabellendeklarationen bekannten Variablentypen können für T-SQLVariablen auch noch weitere Datentypen verwendet werden. Hierzu zählen insbesondere die folgenden:
290
7.1 Variablen CURSOR Eine Cursor-Variable kann ein Abfrageergebnis aufnehmen, das zeilenweise durchlaufen werden kann. Wir werden diese Variablen etwas weiter unten noch behandeln und dort sehen, wie man solche Variablen erzeugt und verwendet. TABLE Eine Variable vom Typ TABLE enthält eine Tabelle. Stellen Sie sich dies einfach wie eine temporäre Tabelle vor, die Sie in Ihrem Skript mit Daten füllen und abfragen können. Dies funktioniert zum Beispiel so:
Variablen vom Typ TABLE sind insbesondere für die Verwendung mit der OUTPUTKlausel interessant. Sie haben dadurch die Möglichkeit, die von einer T-SQL Anweisung eingefügten, geänderten oder gelöschten Zeilen zwischenzuspeichern. Ein Beispiel für diese Verfahrensweise sehen Sie im folgenden Code:
Im obigen Skript wird zunächst eine Tabelle zu Testzwecken angelegt. In diese Tabelle werden dann einige Zeilen eingefügt und wieder gelöscht. Die gelöschten Zeilen werden hierbei in einer TABLE-Variablen protokolliert. Es ist möglich, TABLE-Variablen in einem Cursor zu verwenden. Cursor werden etwas weiter hinten in diesem Kapitel behandelt. Auch TABLE-Variablen werden wir später in diesem Abschnitt noch benötigen, wenn wir benutzerdefinierte Funktionen besprechen.
291
7 T-SQL-Programmierung
7.2
Dynamisches SQL In Kapitel 4 haben Sie gesehen, wie gespeicherte Prozeduren mit der Anweisung EXEC(UTE) aufgerufen werden. EXEC kann auch zum Ausführen von dynamischem SQL verwendet werden. Hierzu muss die SQL-Anweisung als Zeichenkette existieren, die dann an EXEC übergeben wird. Dies funktioniert zum Beispiel so:
Die runden Klammern sind hierbei obligatorisch, um EXEC mitzuteilen, dass es sich nicht um eine gespeicherte Prozedur, sondern um eine als Zeichenkette vorliegende SQL-Anweisung handelt. Es ist auch möglich, die Anweisung in einer Zeichenketten-Variablen zu speichern und auszuführen:
Bitte beachten Sie das zweite EXEC-Kommando. Für dieses Kommando sind die runden Klammern um die Variable optional, da es sich beim auszuführenden Kommando um eine gespeicherte Prozedur handelt. Für das erste EXEC-Kommando hingegen sind die Klammern Pflicht. Microsoft empfiehlt für die Ausführung von dynamischem SQL die Verwendung der gespeicherten Prozedur . Diese Prozedur kann für dynamisches SQL auch Parameter verarbeiten, die wie folgt angegeben werden:
Die Benutzung von ist vielleicht etwas gewöhnungsbedürftig, weil die Deklaration der für die Parameter verwendeten Variablen in einem Parameter der Prozedur vorgenommen wird (dies ist der zweite Parameter der Prozedur). Der SQL ServerAbfrageoptimierer ist aber in der Lage, für mit ausgeführtes SQL auch wieder verwendbare Abfragepläne zu erzeugen und im Prozedurcache abzulegen, was für dynamisches SQL, das mit EXEC ausgeführt wird, nicht immer gilt. Dies trifft insbesondere zu, wenn die Abfrage mit unterschiedlichen Parametern aufgerufen wird. Bei Verwendung von wird in diesem Fall eine Abfrage mit verschiedenen Parametern aufgerufen, bei EXEC müssen verschiedene Abfragen erzeugt werden.
292
7.3 Ablaufsteuerung Wir werden die Arbeitsweise des Abfrageoptimierers in Kapitel 12 nochmals etwas genauer untersuchen. Bitte beachten Sie, dass die Prozedur alle Parameter vom Typ NVARCHAR erwartet.
7.3
Ablaufsteuerung Wie jede Programmiersprache, so kennt auch T-SQL Anweisungen zur Ablaufsteuerung, also zum Beispiel zur bedingten oder wiederholten Ausführung von Anweisungen. Die zur Verfügung stehenden Möglichkeiten sind im Vergleich zu Client-Programmiersprachen wie beispielsweise C# zwar begrenzt, dennoch sollten Sie diese natürlich beherrschen.
7.3.1
Anweisungsblöcke
In T-SQL können mehrere Anweisungen zu einem Block zusammengefasst werden. In einem solchen Block enthaltene Anweisungen werden dann beispielsweise für eine bedingte Ausführung oder auch für Schleifen verwendet. Ein Anweisungsblock beginnt mit dem Schlüsselwort BEGIN und endet mit dem Wort END, sieht also zum Beispiel so aus:
Bitte beachten Sie, dass eine in einem BEGIN-END-Block deklarierte Variable nicht nur innerhalb des Blockes gültig ist. Es gilt das weiter oben Gesagte, dass eine Variable vom Beginn der Deklaration bis zum Ende des T-SQL-Stapels ihre Gültigkeit behält.
7.3.2
Bedingte Ausführung von Anweisungen
Bereits in Kapitel 4 haben Sie die CASE-Anweisung kennengelernt, welche die bedingte Ausführung von Code ermöglicht. T-SQL verfügt auch über eine IF-ELSE-Anweisung, mit der ebenfalls eine bedingte Ausführung von T-SQL-Kommandos erreicht werden kann. Wenn die in der IF-Klausel angegebene Bedingung wahr ist, so wird die nach dieser Klausel angegebene Anweisung ausgeführt, anderenfalls wird die im ELSE-Zweig enthaltene Anweisung gestartet. Eine Anweisung kann hierbei auch ein mit BEGIN/END angegebener Block sein. Außerdem gilt, dass der ELSE-Zweig optional ist. Es folgt ein einfaches Beispiel für den Einsatz von IF/ELSE:
293
7 T-SQL-Programmierung
Das obige Beispiel ist sicherlich nicht besonders sinnvoll, es soll aber auch lediglich die Verwendung von IF verdeutlichen. Die einzelnen Bedingungen werden hier in runde Klammern gesetzt, was zwar nicht notwendig ist, aber die Lesbarkeit der Anweisung doch etwas erhöht. In den einzelnen IF-Zweigen werden Blöcke verwendet, die je nach Bedingung ausgeführt werden oder nicht. Wenn ein solcher Block nur eine einzige Anweisung enthält, so darf der Einschluss dieser Anweisung in BEGIN/END auch weggelassen werden. Das Beispiel könnte also auch so geschrieben werden:
Verdeutlicht wird im Beispiel auch der Einsatz einer geschachtelten IF-Anweisung, die sich ähnlich wie eine CASE-Anweisung verhält. Tatsächlich könnte das Skript daher auch so formuliert werden:
7.3.3
Wiederholte Ausführung von Anweisungen
T-SQL kennt eine einzige Anweisung zur wiederholten Anweisungsausführung, also für die Erstellung von Schleifen. Hierfür steht Ihnen die WHILE-Anweisung zur Verfügung, die Sie wie folgt verwenden können:
Die Anweisung wird so lange ausgeführt, wie die Auswertung der Bedingung den Wert wahr ergibt. In der Regel wird statt einer einzigen Anweisung ein Anweisungsblock verwendet werden, in dem dann auch Zustände verändert werden, welche die Auswertung der
294
7.3 Ablaufsteuerung Bedingung beeinflussen. Dadurch wird dafür gesorgt, dass die Schleife endlich ist, also irgendwann abbricht. Die folgende Schleife gibt alle Datumswerte vom 01.01.2000 bis zum 31.12.2002 im Meldungsfenster aus:
Die Schleifenbedingung sorgt dafür, dass die Schleife so lange ausgeführt wird, bis der Wert von das Jahr 2003 erreicht. (Dabei wird von der impliziten Typkonvertierung Gebrauch gemacht.) Im Anweisungsblock der Schleife wird jeweils ein Tag zur Variablen addiert. Dadurch wird irgendwann der 01.01.2003 erreicht, die Schleife wird dann beendet. CONTINUE Durch die Anweisung CONTINUE innerhalb einer WHILE-Schleife werden die in der Schleife hinter CONTINUE stehenden Anweisungen nicht ausgeführt. Stattdessen wird sofort zur nächsten Prüfung der Schleifenbedingung übergegangen. Sollen zum Beispiel nur die Datumswerte von Montag bis Freitag ausgegeben werden, kann die obige Schleife so geändert werden:
CONTINUE wird hier für die Wochentage Samstag und Sonntag ausgeführt. Dadurch wird die PRINT-Anweisung übersprungen und die Ausführung der Schleife mit der Prüfung der Schleifenbedingung fortgesetzt. BREAK Die BREAK-Anweisung innerhalb einer Schleife beendet die Ausführung dieser Schleife unmittelbar. Es folgt sofort ein Beispiel hierzu. Sowohl für CONTINUE als auch für BREAK gilt, dass diese Anweisungen nur für die Schleife ausgeführt werden, in der sie aufgerufen werden. Schleifen können auch ineinan-
295
7 T-SQL-Programmierung der geschachtelt sein. In einem solchen Fall führt ein BREAK in einer inneren Schleife nicht zum Beenden aller also auch der äußeren Schleifen, sondern bricht nur die innere Schleife ab. Schleife zum Füllen der Kalender-Tabelle Die in Kapitel 4 als Beispiel erzeugte Tabelle Kalender soll nun mit Daten gefüllt werden. Hierzu kann die folgende Schleife dienen:
In der obigen WHILE-Schleife wird für jedes Datum zwischen dem 01.01.2000 und dem 31.12.2000 eine INSERT-Anweisung ausgeführt. Für ein Ende-Datum 31.12.2007 sind dies insgesamt 2922 INSERT-Anweisungen. Die folgende Schleife erledigt dasselbe, allerdings mit nur 13 INSERTs:
Der Trick ist, dass die Tabelle Kalender anhand von bereits existierenden Zeilen erweitert wird. Hierdurch wird die Zeilenanzahl in der Tabelle mit jedem INSERT innerhalb der WHILE-Schleife verdoppelt. Der Ausdruck
296
7.4 Cursor in der SELECT-Anweisung des allgemeinen Tabellenausdrucks berechnet jeweils einen Datumswert, der um genau die Anzahl von Tagen hinter dem maximalen in der Tabelle enthaltenen Datum liegt, wie das in der Zeile enthaltene Datum hinter dem minimalen in der Tabelle enthaltenen Datum liegt. Interessant ist die Abbruchbedingung der Schleife (1=1), die hier immer den Wert wahr ergibt. Dadurch wird die Schleife eigentlich zur Endlosschleife, die also niemals verlassen wird. Der Schleifenabbruch wird durch die BREAK-Anweisung am Ende der Schleife erreicht, die dann ausgeführt wird, wenn der Wert @@ROWCOUNT 0 ist. Dies ist genau dann der Fall, wenn durch die INSERT-Anweisung keine weiteren Zeilen mehr eingefügt wurden.
7.4
Cursor SQL ist eine mengenorientierte Sprache. SELECT-Anweisungen geben Ergebnismengen zurück, Anweisungen zu Datenänderungen verändern genau genommen ebenfalls Mengen, auch wenn eine solche Menge oftmals nur eine einzige Tabellenzeile ist. Durch Cursor existiert eine Möglichkeit, eine Ergebnismenge nicht als Gesamtheit zu bearbeiten, sondern zeilenweise zu durchlaufen. Hierfür kann ein Cursor eine Ergebnismenge aufnehmen, die dann in der Folge aus ihm zeilenweise ausgelesen oder auch verändert werden kann. Sie sollten nach Möglichkeit versuchen, für Ihre Abfragen zum Erhalten von Ergebnissen oder zum Ändern von Tabellendaten, mengenorientierte SQL-Anweisungen zu verwenden. Hierauf kommen wir etwas weiter unten nochmals zurück. Wenn die entsprechenden Abfragen allerdings sehr umfangreich werden, so ist es durchaus möglich, dass Sie hierbei auf Schwierigkeiten stoßen. Ursache dafür kann zum Beispiel einfach mangelnde Erfahrung im Umgang mit T-SQL sein. Natürlich ist es auch denkbar, dass die auszuführende Abfrage oder Aktualisierung nicht mit einer mengenorientierten Abfrage erledigt werden kann. Cursor bieten in so einem Fall einen Ausweg an. Sie ermöglichen eine prozedurale Datenverarbeitung, indem sie Ihnen eine Ergebnismenge zeilenweise zur Verfügung stellen. SQL Server erlaubt zum Beispiel das Versenden von E-Mails durch die in der Datenbank msdb enthaltene gespeicherte Systemprozedur . Stellen Sie sich vor, Sie sollten eine E-Mail für eine bestimmte Werbeaktion an alle Kunden aus dem Bereich Nord-Ost versenden. Die Abfrage der E-Mail-Adressen für die entsprechenden Kunden stellt sicher kein Problem dar, aber wie soll die Prozedur zum Versenden der Mails für jeden betreffenden Kunden per SQL-Skript aufgerufen werden? In so einem Fall hilft ein Cursor, über den für jeden Kunden der Prozeduraufruf gestartet werden kann.
7.4.1
Cursor erzeugen
Cursor werden in einer Variablen gespeichert und müssen durch DECLARE deklariert werden. In der DECLARE-Anweisung wird auch die dem Cursor zugrunde liegende
297
7 T-SQL-Programmierung SELECT-Anweisung angegeben. Eine einfache Cursor-Deklaration sieht zum Beispiel so aus:
Im Skript wird ein Cursor contactCursor deklariert. Auffällig ist zunächst einmal, dass der Name des Cursors nicht mit einem @ beginnt. Ein nach dem obigen Muster deklarierter Cursor ist keine echte Variable, er besitzt gegenüber einer Variablen einen entscheidenden Unterschied: Der Cursor lebt so lange, wie die Verbindung, in der er erstellt wurde, besteht. Bei Variablen ist dies nicht so, diese existieren nur innerhalb des Stapels, in dem sie deklariert wurden. Nach dem Schlüsselwort FOR folgt die SELECT-Anweisung, die später die Daten (Zeilen) des Cursors liefert. Bei der Deklaration eines Cursors können wahlweise weitere Optionen angegeben werden, die das Verhalten und die Verwendungsmöglichkeiten des Cursors spezifizieren. Diese Optionen werden zwischen CURSOR und FOR eingefügt. Es ist auch möglich, das Cursorverhalten über Optionen zu steuern, die zwischen dem Cursor-Namen und dem Schlüsselwort CURSOR angegeben werden. Klingt verwirrend? Nun, die Ursache vor diese Deklarationsvielfalt ist, dass SQL Server sowohl die Cursor-Deklaration nach dem ANSI 92-Standard als auch eine SQL Server-eigene Cursor-Deklaration ermöglicht. Und diese beiden Möglichkeiten unterscheiden sich wie folgt: ANSI 92-Syntax:
T-SQL-Syntax:
Die für beide Möglichkeiten zur Verfügung stehenden Optionen werden in der folgenden Tabelle 7.1 näher erläutert. Tabelle 7.1 Optionen für die Deklaration eines Cursors
298
Syntax
Option
Erklärung
ANSI 92
Bewirkt, dass der Cursor beim Öffnen eine Kopie der abgefragten Daten in der tempdb erzeugt. Anforderungen an den Cursor werden von dieser Kopie bearbeitet. Verwenden Sie diese Option mit Vorsicht, da die tempdb in SQL Server 2005 einen PerformanzEngpass hervorrufen kann.
7.4 Cursor Syntax
T-SQL
Option
Erklärung
Wenn angegeben, stehen alle Navigationsmöglichkeiten für den Cursor zur Verfügung. Die Position des Cursors kann also beliebig verändert werden.
Der Cursor lässt keine Aktualisierungen zu.
Über diese Klausel können aktualisierbare Spalten für den Cursor definiert werden. Cursor können über eine spezielle UPDATESyntax aktualisiert werden, die weiter unten noch besprochen wird.
Diese Option führt dazu, dass der Cursor nur innerhalb des Stapels, in dem er deklariert wurde, gültig ist.
Ein globaler Cursor bleibt in der gesamten Verbindung gültig, lebt also auch außerhalb des T-SQL-Stapels, in dem er ursprünglich deklariert wurde, weiter.
Ein mit dieser Option erstellter Cursor erlaubt nur das Abrufen von Zeilen vom Anfang zum Ende, mit der Schrittweite eins, also Zeile für Zeile. Kann nicht mit der Option SCROLL zusammen verwendet werden.
SCROLL hat hier die gleiche Bedeutung wie in der ANSI 92-Syntax. Diese Option kann nicht zusammen mit FORWARD_ONLY verwendet werden.
STATIC ist identisch mit INSENSITIVE aus der ANSI 92-Syntax.
Ein dynamischer Cursor liest die abzurufenden Daten erst beim Navigieren. Dadurch werden nach der Cursorerstellung vorgenommeine Datenänderungen auch beim Lesen von Zeilen aus dem Cursor wiedergegeben. Der Cursor verhält sich also so ähnlich wie eine Sicht.
Ein so erzeugter Cursor spiegelt nach seiner Erzeugung vorgemeinemene Aktualisierungen an den zugrunde liegenden Tabellendaten wider. Von anderen Verbindungen in dieser Zeit hinzugefügte Daten sind jedoch nicht sichtbar. Erreicht wird dieses Verhalten durch die Erzeugung einer Kopie der Schlüsseldaten des Cursors dies sind die Spalten, die eine Zeile eindeutig identifizieren in der tembdb.
Dies ist eine Kurzschreibweise für die beiden Optionen FORWARD_ONLY und READ_ONLY. Ein solcher Cursor benötigt nur wenige Ressourcen und ist relativ schnell. Diese Option können Sie nicht zusammen mit SCROLL oder FOR UPDATE verwenden.
Erstellt einen Cursor, der nicht aktualisiert werden kann.
Sorgt dafür, dass eine vom Cursor gelesene Zeile gesperrt wird, also von anderen Verbindungen nicht aktualisiert werden kann, solange der Cursor auf der Position dieser Zeile steht. Diese Option darf nicht mit FAST_FORWARD oder STATIC zusammen angegeben werden.
299
7 T-SQL-Programmierung Syntax
Option
Erklärung
Von Cursor gelesene Zeilen werden nicht gesperrt. Wenn über den Cursor Daten aktualisiert oder gelöscht werden, so wird ein entsprechender Versuch fehlschlagen, wenn die Zeile seit dem Einlesen in den Cursor von einer anderen Verbindung geändert wurde. Diese Option kann nicht zusammen mit FAST_FORWARD verwendet werden.
Erzeugt eine Warnung im Falle einer impliziten Typkonvertierung bei der Übertragung von Cursor-Spalten in Benutzervariablen.
Bewirkt dasselbe Verhalten wie in der ANSI 92-Syntax
Wie Sie im obigen Beispiel sehen können, ist die Angabe von Optionen nicht erforderlich. SQL Server erzeugt Cursor mit den folgenden Standardoptionen: Als STATIC und FAST_FORWARD deklarierte Cursor sind READ_ONLY. DYNAMIC- und KEYSET-Cursor sind OPTIMISTIC. Wenn die SELECT-Anweisung keine Aktualisierungen ermöglicht (zum Beispiel wegen fehlender Berechtigungen), so ist der Cursor READ_ONLY. Der Cursor selber kann übrigens so wie temporäre Tabellen in der tempdb erzeugt werden. Hierzu stellen Sie dem Namen des Cursors einfach ein # voran, wie dies in Kapitel 4 für temporäre Tabellen erklärt wurde. Cursor-Variablen SQL Server erlaubt eine Deklaration von Variablen des Typs CURSOR:
An eine derart deklarierte Variable kann mit der SET-Anweisung ein Cursor zugewiesen werden:
Die bei der Zuweisung verwendete Deklaration des Cursors muss hierbei der T-SQLSyntax genügen, Sie können also nicht die ANSI 92-Optionen verwenden. Außerdem ist ein in einer T-SQL-Variablen gespeicherter Cursor lokal, er gilt also nur, solange auch die Variable gültig ist: im Stapel, in dem die Variable deklariert wurde. Cursor-Variablen können zum Beispiel als Parameter an gespeicherte Prozeduren übergeben werden.
7.4.2
Einen Cursor durchlaufen Daten abrufen
Nachdem Sie nun wissen, wie ein Cursor erzeugt wird, wird sich dieser Abschnitt mit dem zeilenweisen Abrufen von Daten aus dem Cursor befassen.
300
7.4 Cursor Öffnen eines Cursors Die Deklaration eines Cursors allein reicht nicht aus, um anschließend Daten aus dem Cursor abrufen zu können. Zuvor muss der Cursor noch geöffnet werden. Erst beim Öffnen des Cursors wird die dem Cursor zugrunde liegende SQL-Anweisung ausgeführt. Die für den Cursor erforderlichen Ressourcen wie zum Beispiel eine temporäre Tabelle in der tempdb werden ebenfalls durch das Öffnen des Cursors angefordert und belegt. Die Anweisung OPEN öffnet einen Cursor:
An die OPEN-Anweisung wird einfach der Name des zu öffnenden Cursors übergeben. Daten einer Zeile abrufen Aus einem geöffneten Cursor kann eine Zeile mit der Operation FETCH abgerufen werden. Der FETCH-Operator hat die folgende allgemeine Form:
Eine FETCH-Anweisung kennt also verschiedene Optionen, von denen viele auch einfach weggelassen werden können. In der einfachsten Form können Sie FETCH so verwenden:
Die obige Anweisung wird einfach die nächste Zeile aus dem Cursor lesen und im Ergebnisbereich ausgeben. Mit der etwas weiter oben angegebenen Deklaration des Cursors contactCursor erhalten Sie dieses Ergebnis:
Wenn Sie sich vorstellen, dass die Zeilenposition im Cursor nach dem Öffnen des Cursors vor der ersten Zeile liegt, so liefert die obige FETCH-Anweisung also das Ergebnis der nächsten Zeile. Hierbei wird diese Zeile zur aktuellen Zeile. Ein erneutes FETCH liefert also das Ergebnis der zweiten Zeile:
Dies können Sie fortsetzen, bis alle Zeilen aus dem Cursor gelesen wurden. Etwas weiter unten werden wir so etwas unter Verwendung einer WHILE-Schleife programmieren. Zunächst aber sollen die zur Verfügung stehenden Optionen für die Positionierung im Cursor und das Abrufen von Daten erläutert werden. Sehen Sie sich hierzu bitte Tabelle 7.2 an.
301
7 T-SQL-Programmierung Tabelle 7.2 Positionieren mit FETCH FETCH-Option
Erklärung
Gibt die nächste Zeile zurück, positioniert also den Zeilenzeiger eine Zeile weiter. Eine Ausnahme von dieser Verfahrensweise existiert allerdings dann, wenn FETCH NEXT der erste Datenabruf vom Cursor ist. In diesem Fall wird die erste Zeile zurückgegeben. NEXT ist der Standard, wenn für FETCH keine Positionsangabe erfolgt.
Positioniert auf die vorhergehende Zeile und gibt diese Zeile zurück.
Macht die erste Zeile des Cursors zur aktuellen Zeile und gibt diese Zeile zurück.
Macht die letzte Zeile des Cursors zur aktuellen Zeile und gibt diese Zeile zurück.
n
Ist der Wert n positiv, so wird vom Anfang des Cursors beginnend die n-te Zeile zurückgegeben. Wenn n negativ ist, so beginnt die Zählung am Ende des Cursors. Die zurückgegebene Zeile wird zur aktuellen Zeile des Cursors. n kann hier auch eine Variable sein.
n
Wenn n positiv ist, so wird die n-te Zeile nach der gerade aktuellen Zeile zurückgegeben. Ist n negativ, so erfolgt die Positionierung rückwärts. Die zurückgegebene Zeile wird wiederum zur aktuellen Zeile. Auch hier kann n eine Variable sein.
Zu beachten ist hierbei, dass nicht jeder Cursor alle Positionierungen unterstützt. So erlaubt zum Beispiel ein Cursor, der in der ANSI 92-Syntax nicht mit der Option SCROLL deklariert wurde, nur die Option NEXT. Mit der FETCH-Anweisung ist es auch möglich, die aus dem Cursor gelesenen Spalten in Variablen zu übertragen. Hierzu werden die Variablen einfach in der INTO-Klausel der FETCH-Anweisung in der Reihenfolge angegeben, in der die Spalten in der SELECTAnweisung stehen. Für unseren contactCursor sieht das zum Beispiel so aus:
Zu beachten ist, dass beim Verwenden der INTO-Klausel alle in der SELECT-Anweisung enthaltenen Spalten empfangen werden müssen. Sie benötigen also genau so viele Variablen, wie Ihre SELECT-Anweisung Spalten hat. Im Meldungsbereich des Ausgabefensters sehen Sie nach der Ausführung des obigen Skriptes diesen Text:
Das Zwischenspeichern der Daten einer Zeile in Variablen und die anschließende Verwendung dieser Variablen, zum Beispiel für Berechnungen oder Datenänderungen, ist sicherlich die übliche Verfahrensweise beim Einsatz eines Cursors. Eine einfache Ausgabe von Zeilen, so wie dies über FETCH ohne INTO geschieht, kann in der Regel auch ohne einen Cursor aber dafür erheblich schneller erledigt werden.
302
7.4 Cursor Schließen eines Cursors Nachdem die Verarbeitung der Cursor-Zeilen abgeschlossen ist, wird der Cursor durch die CLOSE-Anweisung geschlossen. Durch diese Anweisung werden eventuell noch existierende Zeilensperren aufgehoben, und das Ergebnis der dem Cursor zugrunde liegenden Abfrage wird vom Cursor getrennt. CLOSE wird wie OPEN einfach durch Angabe des Cursor-Namens aufgerufen:
Ein durch CLOSE geschlossener Cursor kann durch OPEN jederzeit wieder geöffnet werden. Dies funktioniert wenn es sich um einen globalen Cursor handelt, der ja für die Dauer der Verbindung gültig ist auch außerhalb des gerade aktuellen T-SQL-Stapels. Beim erneuten Öffnen wird die zugrunde liegende Abfrage ausgeführt, und der Satzzeiger steht vor der ersten Zeile. CLOSE kann nur auf zuvor über OPEN geöffnete Cursor angewendet werden. Ist der Cursor bereits geschlossen, so erhalten Sie beim erneuten Versuch, den Cursor zu schließen, eine Fehlermeldung. Von Cursor verwendete Ressourcen freigeben Die endgültige Freigabe aller vom Cursor belegten Ressourcen erledigt die Anweisung DEALLOCATE:
Ist der Cursor noch offen, so wird er durch DEALLOCATE auch geschlossen. Die CursorVariable ist nach DEALLOCATE nicht mehr verfügbar. Status eines Cursors Durch die Anweisung FETCH kann der Zeilenzeiger im Cursor verändert werden. Es bleibt die Frage zu klären, wie man die aktuelle Position dieses Zeigers abfragen kann. Wichtig ist diese Information insbesondere für einen Fall: Wenn der Cursor vollständig durchlaufen wurde, der Zeilenzeiger also hinter dem Ende der letzten Zeile steht, soll das Durchlaufen des Cursors beendet werden. Leider ist es nicht möglich, die aktuelle Position des Zeilenzeigers abzufragen. Es gibt jedoch zwei Funktionen, welche die Statusinformationen zu einem Cursor liefern: @@CURSOR_ROWS. Nach dem Öffnen eines Cursors gibt diese Funktion die Anzahl von Zeilen im Cursor zurück. Bei einem dynamischen Cursor gibt diese Funktion immer den Wert 1 zurück. @@FETCH_STATUS. Diese Funktion liefert Informationen über den Status der letzten FETCH-Operation. Diese Funktion ist global für die Verbindung und enthält das Ergebnis für die jeweils letzte ausgeführte FETCH-Operation der Verbindung. Hierbei können die folgenden drei Werte zurückgegeben werden:
303
7 T-SQL-Programmierung @@FETCH_STATUS
Erklärung
0
Die letzte FETCH-Anweisung war erfolgreich.
-1
Die letzte FETCH-Anweisung war fehlerhaft. Möglicherweise wurde versucht, von einer Position zu lesen, die außerhalb des Ergebnisses liegt.
-2
Die Zeile ist nicht vorhanden. Dies kann zum Beispiel passieren, wenn in einen KEYSET-Cursor versucht wird, eine mittlerweile gelöschte Zeile zu lesen.
Die Funktion @@FETCH_STATUS kann also verwendet werden, um festzustellen, ob das zeilenweise Durchlaufen des Cursors beendet werden darf, weil alle Zeilen gelesen wurden. Hierzu wird eine WHILE-Schleife verwendet:
Im obigen Beispiel wird die FETCH-Operation in einer Endlosschleife ausgeführt. Unmittelbar nach jedem FETCH wird die Anweisung
ausgeführt. Dadurch wird die WHILE-Schleife beendet, sobald die FETCH-Operation fehlgeschlagen ist. Der Code geht hierbei davon aus, dass ein FETCH nur dann nicht funktioniert, wenn alle Zeilen gelesen wurden, was natürlich nicht so ganz korrekt ist. Im Beispiel ist auch zu sehen, wie ein Cursor nach dem Schließen erneut geöffnet und verwendet werden kann.
304
7.4 Cursor Nach der Ausführung des Skriptes erhalten Sie im Meldungsbereich des Ergebnisses diese Ausgabe, die beim ersten Durchlaufen des Cursors erzeugt wird:
Im Abfrageergebnis sehen Sie diese Ausgabe (vom zweiten Cursor-Durchlauf):
7.4.3
Daten aktualisieren
Wie Sie sicherlich bereits bei der Besprechung der für die Cursor-Deklaration zur Verfügung stehenden Optionen vermutet haben, ist es auch möglich, Daten über einen Cursor zu aktualisieren. Für einen solchen aktualisierbaren Cursor steht eine spezielle UPDATESyntax in der Form:
zur Verfügung. Um zum Beispiel alle Vornamen des im vorherigen Beispiel verwendeten Cursors auf Gabriel zu setzen, kann der Cursor so verwendet werden:
305
7 T-SQL-Programmierung Wenn Sie das Skript betrachten, werden Sie feststellen, dass die zu aktualisierende Spalte FirstName nicht im Cursor enthalten ist. Dies ist für ein UPDATE tatsächlich nicht erforderlich, der Cursor soll für die UPDATE-Operation lediglich die Zeile liefern. Das Interessante hierbei ist, dass hierfür keine Zeilenidentifikation wie etwa ein Primärschlüssel benötigt wird. Da die Daten aus dem Cursor zeilenweise ausgelesen werden, wird das UPDATE immer die gerade ausgewählte Zeile verwenden. Die Zeilenidentität kommt sozusagen aus dem Cursor.
7.4.4
Hinweise zur Verwendung von Cursorn
Cursor durchkreuzen den mengenorientierten Ansatz von SQL. Bitte verwenden Sie Cursor daher sparsam, als letzte Möglichkeit, wenn Ihnen keine andere Lösung einfällt. Nach Möglichkeit sollten Sie stets die mengenorientierten SQL-Anweisungen einsetzen. Dies ist vor allem auch deshalb wichtig, weil Cursor viele Ressourcen benötigen, die den SQL Server eventuell unnötig unter Stress setzen. Ist ein Cursor dennoch nicht zu vermeiden, so versuchen Sie bitte, die folgenden Hinweise zu beachten: Die SELECT-Anweisung des Cursors sollte nur die Spalten zurückgeben, die tatsächlich benötigt werden. Außerdem sollten Sie eine Einschränkung der Ergebnismenge bereits in der WHERE-Klausel der SELECT-Anweisung vornehmen und Zeilen nicht erst beim Durchlaufen des Cursors übergehen. Nach Möglichkeit verwenden Sie bitte READ ONLY-Cursor. Dies vermeidet unnötige Sperren. Für die meisten Fälle reicht ein FAST_FORWARD- oder zumindest ein FORWARD_ONLY-Cursor aus, der lediglich ein FETCH NEXT unterstützt. Dieser Cursor-Typ benötigt am wenigsten Ressourcen. Cursor, die in der tempdb erzeugt werden (STATIC, KEYSET, INSENSITIVE), können sehr langsam sein, da die tempdb auch von anderen Verbindungen gleichzeitig benutzt wird. Unter Umständen kann Ihr in der tempdb erzeugter Cursor auch die Ursache für ein verschlechtertes Laufzeitverhalten anderer Anwendungen sein, die ebenfalls temporäre Objekte in der tempdb erstellen.
7.5
Gespeicherte Prozeduren Im bisherigen Verlauf dieses Buches haben wir bereits mehrfach gespeicherte (System- Prozeduren verwendet. Sie wissen bereits, dass in diesen Prozeduren T-SQL-Code gespeichert ist und wie man diesen Code über den Aufruf der Prozedur ausführt. In diesem Abschnitt wollen wir nun eigene gespeicherte Prozeduren erstellen.
306
7.5 Gespeicherte Prozeduren
7.5.1
Warum gespeicherte Prozeduren?
Gleich zu Beginn dieses Kapitels soll die Frage geklärt werden, wozu gespeicherte Prozeduren überhaupt nützlich sind. Wofür eigentlich T-SQL-Code im Server speichern? Immerhin könnten die gespeicherten Anweisungen ja auch direkt von Client-Anwendungen ausgeführt werden. Bei der Verwendung von gespeicherten Prozeduren ergeben sich die folgenden Vorteile Gespeicherte Prozeduren verbergen die logische Struktur der Datenbank vor dem Anwender. Dies ist sicherlich der herausragende Vorteil beim Einsatz von gespeicherten Prozeduren. Durch den Aufruf einer Prozedur, die Daten ändert oder abfragt, muss eine Anwendung nicht die Struktur der Datenbank also etwa die enthaltenen Tabellen oder Sichten kennen. Es wird eine zusätzliche Abstraktionsebene, eine Datenzugriffsschicht implementiert, die zum Beispiel eine Änderung der Datenbankstruktur ermöglicht. Solange die Schnittstelle einer gespeicherten Prozedur nicht verändert wird, müssen Client-Anwendungen daraufhin nicht angepasst werden. Prozeduren können komplexe Programmlogik enthalten. Diese Programmlogik ist auf dem Server also zentral gespeichert. Bei einer Anpassung einer Prozedur sind automatisch alle Client-Anwendungen betroffen. Hierfür müssen weder die ClientAnwendungen geändert noch etwa der SQL Server neu gestartet werden. Für ClientAnwendungen gilt dies allerdings nur dann, wenn die Schnittstelle der Prozedur also die Struktur der Ein- und Ausgabe nicht verändert wird. Für jede T-SQL-Anweisung wird vom Abfrageoptimierer ein Ausführungsplan erstellt, der in einem speziellen Pufferspeicher gespeichert wird. Dieser Ausführungsplan bestimmt, wie eine Abfrage ausgeführt wird. Die Erstellung des Planes erfolgt auf der Grundlage der logischen und physikalischen Struktur der Datenbank. Ähnliche SQLAnweisungen verwenden hierbei denselben Plan. In Kapitel 12 werden wir den Abfrageoptimierer nochmals untersuchen. An dieser Stelle soll nur erwähnt werden, dass für eine gespeicherte Prozedur bei ihrem erstmaligen Aufruf ein solcher Plan erstellt wird, der dann für weitere Aufrufe verwendet wird. Da das Erstellen eines Planes ein CPUintensiver Vorgang ist, ergibt sich bei der Verwendung gespeicherter Prozeduren ein unter Umständen erheblicher Performanzgewinn gegenüber der einzelnen Ausführung der in der Prozedur enthaltenen Anweisungen, für die dann jeweils eigene Ausführungspläne verwendet werden müssten.
7.5.2
Erstellung von gespeicherten Prozeduren
Die Erstellung einer gespeicherten Prozedur erfolgt durch CREATE PROCEDURE, deren allgemeine Syntax so aussieht:
die
Anweisung
Nach der Anweisung CREATE PROCEDURE kann eine Parameterliste folgen, die Sie wahlweise in runde Klammern einschließen können. Jede Parameterdeklaration ist hierbei
307
7 T-SQL-Programmierung wie eine Variablendeklaration aufgebaut, besteht also aus einem Parameternamen und dem Datentyp. Etwas weiter unten werden wir noch erweiterte Optionen für Parameter behandeln. Nach dem Schlüsselwort AS folg dann der eigentliche Rumpf der Prozedur. Hier können Sie (nahezu) beliebige T-SQL-Anweisungen verwenden, also im Prinzip alles, was wir bisher besprochen haben. Im Anweisungsteil können Daten über SELECT abgefragt oder über UPDATE verändert werden. Es ist möglich, Cursor zu verwenden und diese in Schleifen abzuarbeiten. Die Erzeugung temporärer Tabellen für das Einsammeln von Informationen, die dann am Ende der Prozedur ausgegeben werden, ist eine weitere, gern verwendete Verfahrensweise beim Einsatz von gespeicherten Prozeduren. Natürlich stehen Ihnen in Prozeduren auch explizite Transaktionen zur Verfügung, die Sie nach Bedarf bestätigen oder bei Auftreten eines Fehlers auch zurücksetzen können. Hierzu dürfen oder besser: sollten Sie in Ihren Prozeduren entsprechende Fehlerbehandlungen, etwa mit TRY/CATCH, vorsehen. Im folgenden Abschnitt wird zunächst eine einfache gespeicherte Prozedur entworfen. Im weiteren Verlauf werden wir dann ganz allmählich zu umfassenderen Beispielen übergehen. Eine einfache Prozedur Unsere erste Prozedur soll den Namen, die Nummer und den Listenpreis aller Produkte einer bestimmten Kategorie zurückgeben. Diese Prozedur besteht aus einer einzigen SELECT-Anweisung und kann so erzeugt werden:
Die Prozedur erwartet den Namen der Produktkategorie als Parameter und verwendet diesen Parameter in der WHERE-Klausel. Der Anweisungsblock für die Prozedur wurde aus in BEGIN/END gekapselt. Dies ist nicht unbedingt erforderlich und erfolgt nur aus Gründen der besseren Lesbarkeit. Aus demselben Grund wurde der Parameter in runde Klammern eingeschlossen. Diese Konvention werden wir in allen folgenden Beispielen beibehalten. Nach dem Erstellen der Prozedur kann diese zum Beispiel so aufgerufen werden, um die Daten aller Produkte für die Kategorie Bikes zu erhalten:
Hierbei ist zu beachten, dass die Parameter nicht in Klammern gesetzt werden dürfen.
308
7.5 Gespeicherte Prozeduren Die erstellte Prozedur wird im Objekt-Explorer unter Programmierbarkeit/Gespeicherte Prozeduren angezeigt (Abbildung 7.2).
Abbildung 7.2 Gespeicherte Prozeduren der Datenbank AdventureWorks
In diesem Ordner sehen Sie auch einige weitere Prozeduren, welche die Datenbank AdventureWorks von Haus aus mitbringt. Wenn Sie eine Prozedur verändern möchten, so können Sie diese Prozedur mit dem Kommando
entfernen und anschließend über CREATE PROCEDURE neu erzeugen. Möglich ist auch die Verwendung des Kommandos ALTER PROCEDURE für die Änderung der in einer Prozedur enthaltenen Anweisungen oder der Parameterliste. Der Objekt-Explorer bietet hierfür eine recht bequeme Möglichkeit an. Aus dem Kontextmenü einer Prozedur können Sie einfach die Option Ändern auswählen (Abbildung 7.3).
309
7 T-SQL-Programmierung
Abbildung 7.3 Ändern einer gespeicherten Prozedur
Daraufhin wird der Quelltext der Prozedur in einem neuen Abfrage-Editorfenster geöffnet, das bereits eine vorbereitete ALTER PROCEDURE-Anweisung enthält (Abbildung 7.4).
Abbildung 7.4 Ändern der Prozedur getProductList
310
7.5 Gespeicherte Prozeduren Um zum Beispiel die Liste der Produkte nach Produktname sortiert auszugeben, fügen Sie eine entsprechende ORDER BY-Klausel zur Abfrage hinzu, so wie in der Abbildung dargestellt. Anschließend kann ALTER PROCEDURE einfach ausgeführt werden, um die Prozedur zu ändern. Seien Sie bitte vorsichtig, wenn Sie die Schnittstelle einer Prozedur also zum Beispiel die Parameterliste verändern. SQL Server prüft in so einem Fall nicht, ob die Prozedur an anderen Stellen aufgerufen wird. Für diese Aufrufe kann dann eventuell die Parameterliste nicht mehr korrekt sein, was erst zur Laufzeit also dann, wenn der ungültige Aufruf der Prozedur erfolgt bemerkt wird.
7.5.3
Verwenden von Parametern
Die Prozedur getProductList erwartet einen Eingabeparameter, der ausgewertet wird, um nur Produkte einer bestimmten Kategorie auszuwählen. In diesem Abschnitt wollen wir untersuchen, welche weitergehenden Möglichkeiten es für die Definition und Übergabe von Parametern gibt. Standardwerte festlegen Beim Erzeugen oder Ändern einer Prozedur können Sie bei der Angabe der Parameterliste auch Standardwerte für Parameter festlegen. Hierfür wird der Zuweisungsoperator = wie folgt verwendet:
Der Parameter besitzt dadurch den Standardwert 'Bikes'. Ein Parameter mit einem Standardwert muss beim Aufruf einer Prozedur nicht angegeben werden. Wird der Parameter weggelassen, so erhält er in der Prozedur den bei der Deklaration angegebenen Standardwert. getProductList kann nun also auch so aufgerufen werden, um eine Liste aller 'Bikes' zu erhalten:
Möglich ist auch die Angabe der Schlüsselwortes DEFAULT für einen Parameter, wenn für diesen Parameter der Standardwert verwendet werden soll, also so etwa:
Wir wollen die Prozedur getProductList nun so ändern, dass eine absteigende Sortierung nach dem Listenpreis erfolgt, die teuersten Produkte einer Kategorie also zuerst genannt werden. Außerdem soll über einen zweiten Parameter die Anzahl der auszugebenden Zeilen angegeben werden. Hierdurch können dann zum Beispiel die drei teuersten Produkte der Kategorie Components ausgegeben werden. Dieser zweite Parameter soll den Standardwert 3 erhalten. Die folgende ALTER PROCEDURE-Anweisung ändert die Prozedur entsprechend:
311
7 T-SQL-Programmierung
Übergabe von Parametern durch die Position Da die Prozedur nun für beide Parameter Standardwerte enthält, kann der Aufruf der Prozedur auf verschiedene Arten erfolgen. Beide Parameter können wahlweise angegeben oder weggelassen werden. Möglich sind zum Beispiel diese Prozeduraufrufe:
Für die beim Prozeduraufruf übergebenen Parameter gilt hierbei Folgendes: Die Position, an welcher der Parameter in der Liste steht, bestimmt den Wert des Prozedurparameters. Man kann dies auch so formulieren: Wenn der zweite Parameter angegeben werden soll, so muss auch der erste Parameter angegeben werden. Für mehr als zwei Parameter kann dies allgemeiner formuliert werden: Soll ein bestimmter Parameter angegeben werden, so müssen alle in der Parameterliste vorher stehenden Parameter ebenfalls beim Prozeduraufruf angegeben werden. Danach stehende Parameter können weggelassen werden, wenn diese einen Standardwert enthalten. Benannte Parameterübergabe Insbesondere bei Prozeduren mit vielen Parametern, die Standardwerte enthalten, kann die positionsbezogene Parameterübergabe lästig sein, wenn zum Beispiel nur ein Parameter sagen wir der neunte abweichend vom Standardwert vergeben werden soll. In diesem Fall müssen die ersten acht Parameter ebenfalls angegeben werden. Falls diese acht Parameter die Standardwerte erhalten sollen, so müssen Sie acht Mal DEFAULT beim Aufruf der Prozedur angeben. In so einem Fall kann die Übergabe von Parametern auch per Name erfolgen. Hierzu wird eine spezielle Syntax beim Prozeduraufruf verwendet, bei der die Namen der Parameter mit einem Zuweisungsoperator verwendet werden, um nur bestimmte Parameter zu setzen. Möchten Sie zum Beispiel die Prozedur getProductList mit dem Wert 5 für den zweiten Parameter aufrufen und für den ersten Parameter den Standardwert belassen, können Sie auch schreiben:
312
7.5 Gespeicherte Prozeduren Hier wird der Wert für den Parameter explizit gesetzt. Hier folgt noch einmal eine Zusammenfassung zur Parameterübergabe: Parameter ohne Standardwert müssen immer angegeben werden. Für Parameter mit Standardwert kann über DEFAULT der Standardwert festgelegt werden. Parameter mit Standardwert erhalten diesen Wert, wenn sie beim Prozeduraufruf weggelassen werden. Für Parameter mit Standardwert muss bei der positionsbezogenen Übergabe immer ein Wert angegeben werden, wenn für nachfolgende Parameter ein Wert angegeben wird. Eine Parameterliste muss also stets nach links aufgefüllt werden. Sie können die positionsbezogene Übergabe nicht mit der benannten Parameterübergabe zusammen verwenden, sondern müssen sich für eine Form entscheiden. Ausgabeparameter Die bislang verwendeten Parameter ließen nur die Übertragung von Informationen in eine Richtung zu, sie waren allesamt Eingabeparameter. SQL Server gestattet auch die Verwendung von Ausgabeparametern. Diese Parameter müssen in der Parameterliste durch die Option OUTPUT deklariert werden. Unserer Prozedur getProductList soll ein dritter Parameter hinzugefügt werden. Über diesen Parameter soll die Anzahl der tatsächlich ausgegebenen Zeilen von der Prozedur zurückgeliefert werden, die ja von der im Parameter übergebenen Anzahl abweichen kann. Hierzu wird die Prozedur so geändert:
Die abschließende SET-Anweisung setzt den Wert des Ausgabeparameters (der übrigens auch als Eingabeparameter verwendet werden kann, also genau genommen ein Ein- und Ausgabeparameter ist). Für den Empfang eines Ausgabeparameters wird eine spezielle Methode des Prozeduraufrufs verwendet:
313
7 T-SQL-Programmierung Hier wird der Wert des Ausgabeparameters in einer zuvor deklarierten Variablen empfangen. Hierzu muss auch beim Aufruf der Prozedur die OUTPUT-Klausel angegeben werden.
7.5.4
Der Rückgabewert einer gespeicherten Prozedur
Eine gespeicherte Prozedur liefert immer auch einen Rückgabewert vom Typ INT. Wenn Sie nichts anderes angeben, so ist dieser Wert einfach 0. Über die Anweisung RETURN können Sie auch einen anderen Rückgabewert setzen, der zum Beispiel Informationen zum Prozedurverlauf enthalten kann. Der zurückgegebene Wert muss vom Datentyp INT sein und kann beispielsweise verwendet werden, um einen Fehlercode zurückzugeben. Die Prozedur getProductList kann die Anzahl der ausgegebenen Zeilen auch über einen Rückgabewert liefern. Hierzu wird die Prozedur so modifiziert:
Um den Rückgabewert einer Prozedur entgegenzunehmen, muss der Prozeduraufruf so aussehen:
7.5.5
Die gespeicherte Systemprozedur sp_procoption
Bislang haben wir gespeicherte Prozeduren stets manuell aufgerufen. SQL Server bietet jedoch auch die interessante Möglichkeit, dass gespeicherte Prozeduren automatisch beim Start des SQL Server-Dienstes ausgeführt werden können. Hierzu muss eine Prozedur für die automatische Ausführung registriert werden, was durch die gespeicherte Systemprozedur folgendermaßen erledigt werden kann:
Der Parameter gibt den Namen der Prozedur an, für welche die automatische Ausführung ein- oder ausgeschaltet werden soll. Für den Parameter können Sie die Zeichenketten true, on oder 1 zum Aktivieren bzw. false, off oder 0 zum Deaktivieren der automatischen Ausführung verwenden.
314
7.5 Gespeicherte Prozeduren Damit die automatische Ausführung funktioniert, müssen folgende Voraussetzungen erfüllt sein: Eine Startprozedur muss in der master-Datenbank gespeichert werden und darf keine Parameter enthalten. Die Server-Option scan for startup procs muss eingeschaltet sein. Dies kann einfach über einen Aufruf von vorgenommen werden:
Startprozeduren bieten vielfältige Möglichkeiten, wenn Ihre Anwendungen Datenbankoder Serverobjekte benötigen, die beim Start von SQL Server initialisiert werden müssen. So haben Sie zum Beispiel die Möglichkeit, Tabellen in der tempdb zu erzeugen, die dann von Ihren Anwendungen benutzt werden können. Die Datenbank tempdb wird ja bei jedem Start von SQL Server neu erstellt, sodass diese Datenbank unmittelbar nach dem Start immer leer ist. Eine Startprozedur kann dann zu dieser leeren tempdb Objekte hinzufügen und diese initialisieren. Wenn Sie die WAITFOR-Anweisung verwenden, können Startprozeduren sogar Endlosschleifen abarbeiten, wie die folgende Prozedur zeigt, die zyklische Datensicherungen durchführt:
315
7 T-SQL-Programmierung
Die Prozedur ist in der SQL Server Express Edition nützlich, zu deren Umfang kein SQL Server-Agent gehört. Dadurch ist eine durch den Agenten gesteuerte, zyklische Ausführung von Datensicherungen in dieser Edition nicht möglich. Durch die obige Prozedur kann hier Abhilfe geschaffen werden. In dieser Prozedur laufen Datensicherungen in einer Endlosschleife. Jeden Morgen um 05:00 Uhr erfolgt eine vollständige Sicherung der Datenbank AdventureWorks auf ein Sicherungsmedium, das zuvor im Verzeichnis c:\SqlBackup erzeugt wird, wenn es noch nicht existiert. Hierbei wird für jeden Wochentag ein eigenes Sicherungsmedium erzeugt. Um 06:00 werden dann die Systemdatenbanken ebenfalls in dieses Sicherungsmedium gesichert. Im Anschluss an die vollständigen Sicherungen wird dann von ca. 07:00 bis 22:00 Uhr das Transaktionsprotokoll der Datenbank AdventureWorks gesichert. Danach beginnt erneut der Durchlauf der äußeren Schleife, um 05:00 ist dann also der nächste Tag an der Reihe. Die Prozedur wird niemals beendet. Damit diese Prozedur beim Start von SQL Server (Express) automatisch gestartet wird, muss sie durch den Aufruf von hierfür registriert werden:
Diese Prozedur ist die meiste Zeit mit der Ausführung der Anweisung WAITFOR also mit Warten beschäftigt, was kaum Serverressourcen belegt.
7.5.6
EXECUTE und INSERT
Wenn eine gespeicherte Prozedur eine Ergebnismenge also eine Tabelle zurückgibt, so kann dieses Ergebnis auch in einer Tabelle empfangen werden. Hierzu existiert eine spezielle INSERT-Syntax, die so aussieht:
Schauen Sie sich hierzu bitte das folgende Skript an, in dem die von der Prozedur erzeugte Ausgabe in eine zuvor angelegte temporäre Tabelle eingefügt wird:
316
7.5 Gespeicherte Prozeduren Ein Nachteil bei dieser Verfahrensweise ist, dass die Tabelle für den Empfang des Ergebnisses mit der richtigen Struktur bereits existieren muss. Es gibt keine Möglichkeit, nur bestimmte Spalten oder Zeilen aus dem von der Prozedur zurückgegebenen Ergebnis auszuwählen. Es müssen also stets alle Zeilen und Spalten empfangen werden. Wenn die Tabelle für die Aufnahme des Ergebnisses nicht explizit angelegt werden soll oder kann, so hilft ein kleiner Trick. Verwenden Sie in diesem Fall die Funktion OPENQUERY, welche die Ausführung von Abfragen auf sogenannten Verbindungsservern ermöglicht. Hierzu erwartet die Funktion als ersten Parameter den Namen des Verbindungsservers, der allerdings auch der lokale Server sein darf. Für den Server muss die Möglichkeit dieser Art des Datenzugriffs zuvor explizit aktiviert werden, was über die gespeicherte Systemprozedur erfolgen kann:
Das von OPENQUERY gelieferte Ergebnis kann wie eine Tabelle verwendet werden. Dadurch ist es auch möglich, nur bestimmte Zeilen oder Spalten zu verwenden:
7.5.7
Erweitertes Beispiel: PDF-Dateien einlesen
Zum Abschluss dieses Kapitels soll nun noch ein etwas umfangreicheres Beispiel einer gespeicherten Prozedur präsentiert werden. Wir wollen eine gespeicherte Prozedur entwickeln, die PDF-Dateien aus einem Verzeichnis in eine Tabelle überträgt. Diese Tabelle soll das folgende Format aufweisen:
Sie enthält also zwei Spalten, die den Dateinamen der PDF-Datei und den eigentlichen Inhalt der Datei in einer Spalte vom Typ VARBINARY(MAX) aufnehmen sollen.
317
7 T-SQL-Programmierung Die zu erstellende Prozedur soll ein Verzeichnis als Eingabeparameter entgegennehmen und dann alle in diesem Verzeichnis existierenden PDF-Dateien mit Namen und Inhalt in die Tabelle einfügen. Kern der Prozedur ist die Funktion OPENROWSET. Diese Funktion ermöglicht einerseits die Ausführung von Abragen auf Verbindungsservern. OPENROWSET erlaubt auch das Einlesen von Dateien aus dem Dateisystem. Von dieser zweiten Möglichkeit wollen wir hier Gebrauch machen. Dazu wird OPENROWSET in der folgenden Form verwendet:
Das obige Skript liest eine Textdatei ein und gibt den Inhalt dieser Datei im Meldungsfenster aus. Die OPENROWSET-Funktion erwartet zwei Parameter. Der erste Parameter ist der Name der Datei, der auf das Schlüsselwort BULK folgen muss. Der zweite Parameter spezifiziert das Format der Datei. Im Beispiel wird SINGLE_CLOB verwendet. Das C in dieser Bezeichnung steht für Character also eine Zeichenkette. Für unsere einzulesenden PDF-Dateien wird später der Datentyp SINGLE_BLOB verwendet werden. Das B steht hier für Binary also ein Binärdatenformat. Das von OPENROWSET zurückgegebene Resultat kann wie eine Tabelle zum Beispiel in einer SELECT-Anweisung verwendet werden. Auch dies zeigt das obige Beispiel. Nun aber genug der Vorrede, hier kommt der Code für die gespeicherte Prozedur PdfEinlesen: Listing 7.1 PDF-Dokumente in eine Tabelle einlesen
318
7.5 Gespeicherte Prozeduren
Zu Beginn des Skriptes wird zunächst die Tabelle PDFFiles neu erzeugt. Am Anfang der Prozedur wird der Inhalt des übergebenen Verzeichnisses für die Dateimaske *.PDF in eine Tabellenvariable übertragen. Dies erfolgt durch den Aufruf der erweitert gespeicherten Systemprozedur , die hier prinzipiell so verwendet wird:
Die Option /B für das Kommando DIR bewirkt nur die Ausgabe der Dateinamen ohne zusätzliche Informationen wie Größe oder Datum. Vor dem Aufruf von muss noch die entsprechende Option aktiviert werden, die den Aufruf dieser Prozedur gestattet. Direkt nach dem Aufruf wird diese Aktivierung dann wieder zurückgenommen. Für die Aktivierung wird die gespeicherte Systemprozedur verwendet. Da nicht jede SQL Server-Anmeldung die Berechtigung zum Aufruf dieser Prozedur besitzt, muss dem aufrufenden Benutzer die entsprechende Berechtigung erteilt werden. Hierzu kann der Benutzer beispielsweise in die Serverrolle serveradmin aufgenommen werden. Die Abarbeitung der gefundenen PDF-Dateien, deren Namen sich nun in der temporären Tabelle befinden, erfolgt durch einen Cursor. Innerhalb des Cursors wird für jede gefunde-
319
7 T-SQL-Programmierung ne Datei die Funktion OPENROWSET verwendet, um den Dateiinhalt in die Tabelle PDFFiles zu übertragen. OPENROWSET erwartet hierbei als ersten Parameter den Namen einer Datei als Zeichenkette. Leider kann hier keine Variable verwendet werden, die den Dateinamen enthält, der Name muss direkt angegeben werden. Aus diesem Grund verwendet die Prozedur dynamisches SQL und konstruiert für jede Datei eine eigene INSERT-Anweisung mit OPENROWSET. Wenn Sie diese Zeile auskommentieren
so erhalten Sie den Text der INSERT-Anweisung im Meldungsfenster zur Kontrolle. Die fertige Prozedur kann so verwendet werden:
7.6
Benutzerdefinierte Funktionen Benutzerdefinierte Funktionen bieten eine weitere Möglichkeit, T-SQL-Code auf dem Server zu speichern. Damit sind Funktionen Prozeduren recht ähnlich, und doch existieren einige erhebliche Unterschiede: Funktionen gestatten keine Seiteneffekte, die Datenänderungen durchführen. UPDATE-, INSERT- oder DELETE-Anweisungen sind in Funktionen also nicht erlaubt. Funktionen gestatten die Verwendung von Parametern, erlauben aber keine Ausgabeparameter. Auch für Funktionsparameter können Standardwerte angegeben werden. Da es in einem Funktionsaufruf generell nicht möglich ist, Parameter wegzulassen es müssen also immer alle Parameter angegeben werden , muss explizit DEFAULT als Parameterwert angegeben werden, wenn der Standardwert verwendet werden soll. Eine Funktion muss immer explizit ein Ergebnis über die RETURN-Anweisung zurückgeben. Dieses Ergebnis kann wie bei Prozeduren auch ein Wert vom Typ INT sein. Funktionen können jedoch Rückgabewerte beliebiger Datentypen an den Aufrufer zurückgeben. SQL Server unterscheidet zwischen Funktionen, die einen Skalarwert zurückgeben, und solchen Funktionen, die eine Tabelle als Ergebnis liefern.
7.6.1
Skalarwertfunktionen
Ein Skalarwert ist ein einzelner Wert eines einfachen Datentyps, also zum Beispiel ein INT, VARCHAR oder DATETIME. Eine Funktion, die einen Skalarwert zurückliefert, wird folgendermaßen erzeugt:
320
7.6 Benutzerdefinierte Funktionen
Eine Funktionsdefinition beginnt also mit CREATE FUNCTION und dem Funktionsnamen. Optional können dann Parameter angegeben werden, die genauso wie bei Prozeduren deklariert werden (mit der Ausnahme, dass keine Ausgabeparameter verwendet werden dürfen). Die runden Klammern hinter dem Funktionsnamen sind hier obligatorisch. Sie müssen auch hingeschrieben werden, wenn die Funktion keine Parameter entgegennimmt. Die anschließende RETURNS-Klausel bestimmt den Datentyp des Funktionsergebnisses. Nach dem Schlüsselwort AS beginnt dann der eigentliche T-SQL-Code für die Funktion. Eine Funktion muss eine abschließende RETURN-Anweisung enthalten, mit welcher der Funktionswert zurückgegeben wird. Dieser Wert muss vom in der RETURNS-Klausel angegebenen Datentyp sein oder zumindest implizit in diesen Datentyp konvertiert werden können. Lassen Sie uns hierzu ein einfaches Beispiel betrachten. SQL Server kennt keinen reinen DATE-Datentyp, in dem nur das Datum ohne Uhrzeit gespeichert ist. Wir wollen eine Funktion erstellen, die den Zeitanteil aus einem DATETIME-Wert entfernt, also ein DATETIME zurückgibt, in dem die Uhrzeit immer 00:00 ist. Hierzu wird der Funktion ein DATETIME-Wert übergeben. Diese Funktion könnte so aussehen:
Die Funktion enthält drei Anweisungen zwischen BEGIN und END, um zu zeigen, dass an dieser Stelle beliebig viele Anweisungen stehen können. Für die obige Funktion wäre auch einfach eine RETURN-Anweisung der Form
ausreichend gewesen. Erstellte Funktionen in einer Datenbank können Sie im Objekt-Explorer unter Programmierbarkeit/Funktionen sehen. Die gerade erstellte Skalarwertfunktion dbo.Datum finden Sie dort im Ordner Skalarwertfunktionen (Abbildung 7.5). In diesem Ordner sind auch einige weitere Funktionen vorhanden, die in der Beispieldatenbank standardmäßig enthalten sind. Die Funktion dbo.Datum kann nun zum Beispiel so aufgerufen werden:
321
7 T-SQL-Programmierung
Abbildung 7.5 Skalarwertfunktionen der Datenbank AdventureWorks
Beim Funktionsaufruf gibt es eine Besonderheit: Das Schema muss immer angegeben werden. Für Funktionsaufrufe wird also keine automatische Namensauflösung durchgeführt. Unsere Funktion wurde im Schema dbo erstellt, muss also stets durch dbo.Date aufgerufen werden. Außerdem ist es immer erforderlich, die runden Klammern für den Funktionsaufruf anzugeben. Dies gilt auch, wenn die Funktion keine Parameter hat, wie im nächsten Beispiel zu sehen sein wird. Wie oben erwähnt, dürfen Funktionen keine Datenänderungsoperationen durchführen. Das Abrufen von Daten, also die Ausführung von SELECT-Anweisungen, ist selbstverständlich möglich. In Abschnitt 7.1.2 haben wir eine durch Semikolon getrennte Liste aller Angestellten erzeugt. Dies soll nun nochmals innerhalb einer Funktion für alle Vertriebsmitarbeiter erfolgen. Hier ist die Funktion dbo.VBListe, die diese Aufgabe erledigt:
322
7.6 Benutzerdefinierte Funktionen Aus Gründen der Übersichtlichkeit wird in der Funktion ein allgemeiner Tabellenausdruck verwendet, der für alle Vertriebsmitarbeiter Vor- und Nachname aneinanderhängt. Die von diesem Tabellenausdruck zurückgegebenen Namen werden dann in einer Variablen von Typ VARCHAR(MAX) verkettet und schließlich über diese Variable zurückgegeben. Die Funktion kann zum Beispiel so aufgerufen werden:
Hier ist ein Ausschnitt aus dem Ergebnis:
7.6.2
Tabellenwertfunktionen
Tabellenwertfunktionen geben eine Tabelle an den Aufrufer zurück. Bei Tabellenwertfunktionen unterscheidet SQL Server generell zwischen zwei Typen. Zum einen gibt es die sogenannten Inline-Tabellenwertfunktionen, die nur aus einer einzigen SELECTAnweisung bestehen dürfen. SQL Server kennt jedoch auch Tabellenwertfunktionen, die im Anweisungsblock mehrere T-SQL-Anweisungen enthalten können. Inline-Tabellenwertfunktionen Eine Inline-Tabellenwertfunktion besitzt die folgende Struktur:
Wir wollen an dieser Stelle eine Funktion erstellen, welche die letzten Bestellungen für einen Vertriebsmitarbeiter zurückgibt. Die Funktion nimmt zwei Parameter entgegen. Zum einen wird die SalesPersonID des Vertriebsmitarbeiters übergeben, für den die Daten abgefragt werden sollen. Über einen zweiten Parameter wird angegeben, wie viele Zeilen zurückgegeben werden sollen. Diese Funktion kann als Inline-Tabellenwertfunktion erstellt werden, die so aussieht:
Der Aufruf einer Tabellenwertfunktion unterscheidet sich vom Aufruf einer Skalarwertfunktion. Da eine Tabellenwertfunktion eine Tabelle zurückgibt, kann (muss) die Funktion auch wie eine solche verwendet werden, also zum Beispiel so:
um für den Vertriebsmitarbeiter mit der SalesOrderID 284 die letzten fünf Bestellungen abzufragen.
323
7 T-SQL-Programmierung Stellen Sie sich eine Tabellenwertfunktion einfach wie eine Sicht mit Parametern vor. Eine Tabellenwertfunktion kann mit dem APPLY-Operator zusammen verwendet werden. Um zum Beispiel die jeweils letzten drei Bestellungen aller Angestellten abzufragen, ist es möglich, die Funktion dbo.LastOrders über APPLY so aufzurufen:
Diese Abfrage basiert auf dem APPLY-Beispiel aus Kapitel 4, das hier nochmals diesmal eben unter Verwendung einer Tabellenwertfunktion aufgegriffen wird. Tabellenwertfunktionen mit mehreren Anweisungen Eine Tabellenwertfunktion kann auch mehrere Anweisungen enthalten und erst am Ende eine Tabelle zurückgeben. In so einem Fall muss die RETRUNS-Klausel der CREATE FUNCTION-Anweisung die Deklaration einer Tabellenvariablen enthalten. In dieser Variablen werden während der Funktionsausführung Daten gespeichert, die dann an den Aufrufer (als Tabelle) zurückgegeben werden. Generell sieht eine solche Tabellenwertfunktion so aus:
Insbesondere darf die RETURN-Anweisung einer solchen Funktion also keinen Ausdruck enthalten. Durch RETURN wird einfach die bei RETURNS sozusagen erstellte Tabelle zurückgegeben. Wir wollen die Funktion dbo.LastOrders nun nochmals erstellen, diesmal als Tabellenwertfunktion mit mehreren Anweisungen. Dies kann ungefähr so aussehen:
324
7.7 Trigger
Im Funktionskopf wird eine Tabelle definiert, in die dann innerhalb der Funktion Zeilen hinzugefügt werden. Die abschließende RETURN-Anweisung gibt dann diese Tabelle zurück. Der Aufruf dieser Funktion kann genauso erfolgen wie im Beispiel weiter oben, also so:
7.7
Trigger Trigger sind ein spezieller Typ von gespeicherten Prozeduren. Sie unterscheiden sich von Prozeduren nur in wenigen aber wesentlichen Punkten: Trigger können keine Parameter verarbeiten. Trigger können nicht manuell aufgerufen werden. Ein Trigger wird bei seiner Definition mit einem Ereignis verknüpft, bei dessen Eintreten der Trigger automatisch ausgeführt wird. Solch ein Ereignis kann zum Beispiel eine Datenänderungsoperation, wie INSERT oder UPDATE, sein. Trigger dieser Art werden auch als DML-Trigger bezeichnet, da sie für Anweisungen ausgeführt werden, die Datenmanipulationen vornehmen. Neu in SQL Server 2005 ist die Möglichkeit, auch Ereignisse für Änderungen an Metadaten, also zum Beispiel an Datenbankstrukturen, mit Triggern verbinden zu können. Diese sogenannten DDL-Trigger können also mit Anweisungen aus dem Bereich Datendefinition verknüpft werden. Innerhalb von Triggern haben Sie Zugriff auf spezielle Objekte zur weiterführenden Information über das auslösende Ereignis. Bei DML-Triggern existieren zum Beispiel die beiden virtuellen Tabellen DELETED und INSERTED, die Informationen über die Datenänderungen liefern. In DDL-Triggern kann ein spezielles Ereignisobjekt über eine Systemfunktion abgefragt werden. Für die in diesem Abschnitt verwendeten Beispiele soll zunächst eine Tabelle Kontakt erzeugt werden, die drei zufällig ausgewählte Zeilen aus der Tabelle Person.Contact enthält:
Zur Spalte ContactID der abgerufenen Spalte wird hier einfach der Wert 0 addiert, um zu verhindern, dass die IDENTITY-Eigenschaft der Spalte in die angelegte Tabelle übernommen wird.
325
7 T-SQL-Programmierung
7.7.1
DML-Trigger
Ein DML-Trigger kann für INSERT-, UPDATE- oder DELETE-Operationen auf einer Tabelle oder auch Sicht definiert werden. Dies erfolgt über die Anweisung CREATE TRIGGER, welche die folgende Syntax hat:
Für kann INSERT, UPDATE oder DELETE angegeben werden, um die Ausführung des Triggers für eine entsprechende Datenänderung auszulösen. Es ist auch Möglich, mehrere Ereignisse durch Komma getrennt anzugeben, wodurch der Trigger dann für all diese Ereignisse ausgeführt wird. Im eines DML-Triggers haben Sie Zugriff auf die bereits in Kapitel 4 bei OUTPUT besprochenen Tabellen INSERTED und DELETED. Über diese Tabellen kann der Zustand vor und nach der Datenänderungsoperation abgefragt werden. In einem Trigger, der durch eine INSERT-Anweisung gefeuert wurde, ist die Tabelle DELETED leer. Gleiches gilt für die Tabelle INSERTED und Trigger, die durch ein DELETE ausgelöst wurden. Ein nach dem obigen Muster erstellter Trigger wird nach der Ausführung der entsprechenden SQL-Anweisung gestartet. Der Trigger wird also erst aufgerufen, wenn die gesamte Anweisung abgearbeitet wurde, und nicht etwa vor Ausführung der Anweisung oder für jede Zeile, die von der SQL-Anweisung betroffen ist. Das bedeutet, dass in den Tabellen INSERTED und DELETED durchaus auch mehrere Zeilen enthalten sein können. Dies sollten Sie beim Erstellen von Triggern unbedingt bedenken. INSERT-Trigger Es soll nun ein einfacher INSERT-Trigger für die gerade erzeugte Tabelle Kontakt erstellt werden. Dieser Trigger soll lediglich die hinzugefügten Zeilen, die ja in der INSERTED-Tabelle stehen, zurückgeben. Ein solcher Trigger kann folgendermaßen erstellt werden:
Der so erstellte Trigger existiert nicht als eigenständiges Datenbankobjekt. Der Trigger hängt immer an der Tabelle, für die er erstellt wurde. Im Objekt-Explorer wird dies auch deutlich. Der Trigger wird im Ordner für die jeweilige Tabelle, dort unter Trigger, angezeigt (Abbildung 7.6).
326
7.7 Trigger
Abbildung 7.6 Der INSERT-Trigger für die Tabelle Kontakt
Wir wollen nun natürlich ausprobieren, ob der Trigger tatsächlich beim Hinzufügen einer Zeile gefeuert wird, fügen also einfach eine Zeile hinzu:
Die Transaktion ist hier nur dazu da, die Tabelle im ursprünglichen Zustand zu belassen. Für unsere in diesem Abschnitt durchgeführten Versuche mit Triggern werden wir ausschließlich explizite Transaktionen verwenden, die stets zurückgesetzt werden. Nach der Ausführung der INSERT-Anweisung wird automatisch der Trigger gestartet. Die im Trigger ausgeführte SELECT-Anweisung liefert dieses Resultat:
Die INSERTED-Tabelle enthält im Trigger also eine einzige Zeile mit den angegebenen Spaltenwerten. UPDATE-Trigger Im zweiten Schritt soll nun ein UPDATE-Trigger hinzugefügt werden. Dies ist der einzige Trigger-Typ, in dem sowohl die INSERTED- als auch die DELETED-Tabelle mit Werten gefüllt sind. Daher geben wir im Trigger die Zeilen beider Tabellen aus:
327
7 T-SQL-Programmierung
Die Zeilen der beiden Tabellen werden einfach durch einen UNION ALL-Operator aneinandergehängt. In der ersten Spalte unterscheiden sich die Werte der beiden SELECTAnweisungen. Hier wird für die INSERTED-Tabelle die Zeichenkette Neue Werte ausgegeben, für die DELETED-Tabelle erfolgt die Ausgabe der Zeichenkette Alte Werte. Auch diesmal soll natürlich überprüft werden, ob der Trigger ausgeführt wird. Hierzu verwenden wir diese Abfrage, die alle Nachnamen ändert:
Im Ausgabebereich werden Sie dadurch so etwas erhalten:
Sie sehen den Inhalt der beiden Tabellen INSERTED und DELETED und können erkennen, dass der Nachname für alle Zeilen auf Schönfeld geändert wurde. Interessant ist, dass auch die folgende Anweisung den Trigger auslöst, obwohl hierbei keine Daten verändert werden:
Die Tabellen INSERTED und DELETED enthalten in diesem Fall identische Zeilen. Bedenken Sie dies bitte, wenn Sie Trigger verwenden. Entscheidend für die automatische Ausführung eines Triggers ist die Anweisung selber in diesem Fall UPDATE und nicht etwa die durchgeführte Datenänderung. DELETE-Trigger Zum Schluss unserer Experimente soll nun noch ein DELETE-Trigger hinzugefügt werden, der die Zeilen der Tabelle DELETED zurückgibt:
328
7.7 Trigger
Die folgende Anweisung bewirkt die Ausführung des Triggers:
Als Ausgabe werden alle gelöschten Zeilen angezeigt. Dies sind da keine Zeilenfilterbedingung angegeben wurde alle Zeilen der Tabelle Kontakt:
Auch der DELETE-Trigger wird immer gefeuert, nachdem eine DELETE-Anweisung ausgeführt wurde. Hierbei ist es unerheblich, ob tatsächlich Zeilen gelöscht wurden. Der Trigger startet also auch nach dieser Anweisung:
Sie erhalten vom Trigger eine leere Ergebnismenge, da ja nichts gelöscht wurde. Das Entfernen aller Zeilen der Tabelle ist auch über TRUNCATE TABLE möglich:
Hierbei wird der Trigger allerdings nicht ausgeführt. TRUNCATE ist eben kein DELETE. Es ist nicht möglich, für TRUNCATE einen Trigger zu erstellen. Trigger kombinieren Bislang haben wir jeweils einen Trigger für jede der drei möglichen DML-Anweisungen INSERT, UPDATE und DELETE erzeugt. SQL Server gestattet jedoch auch die Definition von Triggern für mehr als eine DML-Anweisung. Hierfür können Sie die Ereignisse, die den Trigger auslösen sollen, einfach durch Komma getrennt aufzählen. Unsere bisher erzeugten Trigger verhalten sich alle gleich. Sie geben jeweils nur den Inhalt der Tabellen INSERTED und DELETED aus. Hierfür kann auch ein einzelner Trigger verwendet werden, der für alle drei DML-Anweisungen angelegt wird:
329
7 T-SQL-Programmierung
Die oben präsentierten Ergebnisse für die einzelnen DML-Anweisungen werden auch durch diesen Trigger erzeugt, der immer den Inhalt beider Tabellen zurückgibt. Da die DELETED- bzw. INSERTED-Tabellen für INSERT- bzw. DELETE-Anweisungen keine Zeilen enthalten, funktioniert die Abfrage im Trigger in jedem Fall. Es ist auch möglich, mehrere INSERT-, DELETE- oder UPDATE-Trigger für eine Tabelle zu erzeugen. In so einem Fall kann die Aufrufreihenfolge der Trigger entscheidend sein. Wenn Sie zum Beispiel fünf UPDATE-Trigger zu unserer Tabelle Kontakt hinzugefügt haben, ist es möglicherweise wichtig festzulegen, in welcher Reihenfolge diese Trigger ausgeführt werden sollen. Hier bietet SQL Server nur die Möglichkeit, über die gespeicherte Systemprozedur anzugeben, welcher Trigger zuerst und welcher zuletzt ausgeführt werden soll. Dazwischen ist die Reihenfolge undefiniert. Am besten vermeiden Sie also eine solche Situation. Es ist immer auch möglich, mit nur einem Trigger je Ereignis auszukommen, aus dem ja zum Beispiel auch Funktionen oder Prozeduren aufgerufen werden können, wenn der Trigger ansonsten vielleicht zu komplex werden würde. Trigger deaktivieren/aktivieren In manchen Fällen kann es erforderlich sein, einen oder alle Trigger zeitweise außer Kraft zu setzen. Natürlich können Sie in so einem Fall den Trigger mit DROP TRIGGER löschen und anschließend über CREATE TRIGGER neu erstellen. SQL Server bietet jedoch auch die Möglichkeit, Trigger zu deaktivieren. Für DML-Trigger können Sie hierbei zwischen der Anweisung ALTER TABLE oder DISABLE TRIGGER wählen. Ein DISABLE TRIGGER-Kommando zur Deaktivierung des Triggers tr_all_kontakt sieht beispielsweise so aus:
Es müssen also der Name des Triggers und ebenso der Name der Tabelle angegeben werden. Über ein entsprechendes ENABLE TRIGGER kann der existierende Trigger wieder aktiviert werden:
Es ist auch möglich, alle Trigger einer Tabelle mit nur einem Kommando außer Kraft zu setzen und wieder zu aktivieren, Hierzu verwenden Sie in der obigen Syntax einfach ALL anstelle des Trigger-Namens:
330
7.7 Trigger
INSTEAD OF-Trigger Die bisher erstellten Trigger wurden allesamt nach der Ausführung der auslösenden Anweisung gestartet. SQL Server kennt einen zweiten Trigger-Typ, der anstelle von DMLAnweisungen ausgeführt wird: INSTEAD OF-Trigger. Einen INSTEAD OF-Trigger können Sie für jede der drei DML-Anweisungen genau einmal pro Tabelle erstellen. Außerdem können INSTEAD OF-Trigger auch für Sichten erzeugt werden, was eine Aktualisierung von Sichten ermöglicht, deren Zeilen ansonsten nicht geändert werden dürften. Bei einem INSTEAD OF-Trigger wird die eigentliche Anweisung also nicht ausgeführt. Stattdessen wird der Trigger aufgerufen, der dann seinerseits entsprechende Aktualisierungskommandos für die Tabelle absetzen kann. Ein INSTEAD OF-Trigger kann zum Beispiel verwendet werden, um zusätzliche Integritätsbedingungen zu überprüfen, die vor dem Einfügen, Ändern oder Löschen von Zeilen überprüft werden sollen und die nicht mittels deklarativer Integritätsbedingungen angegeben werden können. Abbildung 7.7 zeigt ein Beispiel für eine UPDATE-Anweisung.
Abbildung 7.7 INSTEAD OF UPDATE-Trigger
Im oberen Zweig ist die Ausführung aus der Sicht des Benutzers dargestellt, der ein UPDATE-Kommando ausführt. Für diesen Benutzer ist nicht ersichtlich, dass in Wirklichkeit ein Trigger, also Programmcode, aufgerufen wird, der zusätzliche Überprüfungen vornimmt und dann erst ein UPDATE-Kommando auf der Tabelle ausführt. Durch diese Ausführung wird der Trigger übrigens nicht erneut ausgeführt, eine Rekursion wird also unterbunden.
331
7 T-SQL-Programmierung
7.7.2
Hinweise zur Verwendung von Triggern
Auf den ersten Blick werden Ihnen Trigger vielleicht als eine fantastische und faszinierende Möglichkeit in der Datenbankentwicklung erscheinen. Dies mag sicherlich für gewisse Einsatzbereiche auch zutreffend sein. Setzen Sie Trigger aber bitte vorsichtig und sparsam ein. Trigger basieren auf Seiteneffekten, laufen also im Hintergrund mehr oder weniger unkontrolliert ab. Die Ausführung von Triggern hat eventuell entscheidende Auswirkunken auf das Laufzeitverhalten Ihres Systems. Eine UPDATE-Anweisung, die eigentlich nur ein paar Zeilen aktualisieren soll, kann über einen Trigger letztlich eine Transaktion erzeugen, die den Server an den Rand seines Leistungsvermögens bringt. Halten Sie Trigger daher in jedem Fall kurz, und versuchen Sie nicht, Anwendungslogik, die besser in Ihren Applikationen oder vielleicht in gespeicherten Prozeduren enthalten sein sollte, in Triggern unterzubringen. Große Trigger werden in der Regel auch große Transaktionen mit vielen Sperren zur Folge haben, und dies kann recht dramatische Auswirkungen auf das Laufzeitverhalten haben. Bedenken Sie hierbei bitte, dass ein Trigger je Anweisung ausgeführt wird. Dies kann dazu führen, dass die Tabellen INSERTED und DELETED sehr groß werden. Wenn Sie diese Tabellen im Trigger abarbeiten wollen, so werden Sie häufig auf einen Cursor zurückgreifen, was wiederum sehr ressourcenintensiv ist. Sinnvoll sind Trigger zum Beispiel dann, wenn Sie aus Performanzgründen eine Denormalisierung Ihrer logischen Datenbankstruktur vorgenommen haben. Es ist zum Beispiel denkbar, dass Sie redundante Daten speichern, etwa eine Spalte GesamtUmsatz in einer Tabelle mit Kundendaten. Der Gesamtumsatz für einen Kunden wird sich immer aus den für diesen Kunden existierenden Rechnungen herleiten lassen, die Spalte in der KundenTabelle ist folglich redundant und existiert wahrscheinlich nur aus Performanzgründen. Über Trigger können Sie den in dieser Spalte enthaltenen Wert auf dem Laufenden halten, wenn Rechnungen hinzugefügt, geändert oder gelöscht werden. In einigen Fällen kann es darüber hinaus auch sinnvoll sein, Trigger für die Aufrechterhaltung der Datenintegrität zu verwenden, wenn dies mit deklarativen Mitteln nicht möglich ist. Vorrangig sollten Sie jedoch immer versuchen, für die Wahrung der Integrität die vorhandenen Einschränkungen auf Tabellen- oder Spaltenebene zu verwenden. Wenn Sie sich unsere obigen Beispiele nochmals ansehen, werden Sie vielleicht ein weiteres Problem erkennen. Ein Trigger wird immer dann ausgeführt, wenn eine entsprechende Anweisung ausgeführt wurde, und nicht etwa erst zum Ende der Transaktion. Sie können sich dies so vorstellen, dass Trigger Daten aus dem Transaktionsprotokoll lesen. Dadurch sind in Triggern prinzipiell Dirty Reads oder auch Phantom Reads möglich. Es kann also sein, dass ein Trigger Daten verarbeitet, die danach von einer Transaktion als ungültig erklärt werden, so wie es in all unseren Beispielen der Fall gewesen ist. Seien Sie auch darauf vorbereitet, dass Trigger eventuell rekursiv aufgerufen werden können. Es ist möglich, dass ein Trigger seinerseits Änderungen an Tabellen vornimmt, die wiederum Trigger auslösen. Hierdurch kann sich unter Umständen eine Kette von TriggerAufrufen ergeben, die in einer Rekursion endet. Wenn Sie den Aufruf von Triggern aus
332
7.7 Trigger anderen Triggern heraus unterbinden wollen, können Sie die Einstellung nested triggers der gespeicherten Systemprozedur entsprechend setzen:
7.7.3
DDL-Trigger
Neu in SQL Server 2005 ist die Möglichkeit, auch Trigger für Änderungsoperationen an Server- oder Datenbankobjekten erzeugen zu können. Diese Trigger werden unter dem Begriff DDL-Trigger zusammengefasst und lassen sich wie gesagt in zwei Kategorien unterteilen, die Server- oder Datenbankereignisse überwachen. Für unsere Experimente in diesem Abschnitt soll eine Tabelle Logbuch erzeugt werden, in der wir über Trigger bestimmte Ereignisse auf der Datenbank AdventureWorks, aber auch auf dem gesamten Server protokollieren wollen. Diese Tabelle wird in der masterDatenbank wie folgt erstellt:
Die dritte Spalte der Tabelle, in der die Details der aufgetretenen Ereignisse gespeichert werden, ist von Datentyp XML. Bitte stören Sie sich nicht daran, der XML-Thematik ist das gesamte folgende Kapitel 8 gewidmet. SQL Server kennt insgesamt ca. 90 Ereignisse, welche die Auslösung eines DDL-Triggers bewirken können. Wir werden an dieser Stelle nicht alle Ereignisse besprechen. Eine vollständige Auflistung finden Sie natürlich in der Online-Dokumentation. Glücklicherweise ist die Namensgebung für die Ereignisse so gewählt, dass allein der Ereignisname genug Information über das entsprechende Ereignis enthält. Dadurch ist es sehr einfach, aus der Vielzahl von zur Verfügung stehenden Ereignissen das benötigte auszuwählen, wie die folgenden beiden Abschnitten zeigen werden. DDL-Trigger für Datenbankereignisse Ein Trigger für die Überwachung von Datenbankereignissen wird auf der entsprechenden Datenbank durch eine Anweisung der folgenden Form erstellt:
steht hierbei für ein zu überwachendes Datenbankereignis. SQL Server ver-
fügt über insgesamt 75 unterschiedliche Ereignisse, von denen so wie in der obigen Syn-
333
7 T-SQL-Programmierung tax angedeutet auch mehrere mit einem Trigger verknüpft werden können. Der Name eines Ereignisses ist hierbei so aufgebaut wie die DDL-Anweisung, für die der Trigger ausgelöst werden soll. Zwischen den einzelnen Schlüsselwörtern wird dabei anstelle des Leerzeichens ein Unterstrich verwendet. So gibt es zum Beispiel ein Ereignis CREATE_TABLE, das die Erzeugung von Tabellen überwacht. Die folgende Auflistung enthält einige weitere willkürlich ausgewählte Ereignisse, die verdeutlichen sollen, wie Ereignisnamen konstruiert werden: CREATE_USER DROP_INDEX ALTER_PROCEDURE DROP_FUNCTION CREATE_SCHEMA Durch die Art der Namensgebung ist die Auswahl von Ereignissen also recht einfach. Der folgende Trigger soll nun auf der Datenbank AdventureWorks erstellt werden:
Dieser Trigger wird immer ausgelöst, wenn eine Tabelle auf der Datenbank angelegt, geändert oder gelöscht wird. Er protokolliert die Aktion, durch die er gefeuert wurde, in der zuvor angelegten Tabelle Logbuch der master-Datenbank. Hierzu wird die Funktion EVENTDATA() verwendet, die alle Informationen über das Ereignis im XML-Format enthält. Etwas weiter unten werden wir den Rückgabewert dieser Funktion noch genauer betrachten. Für die Auslösung von DDL-Triggern können Sie übrigens nicht nur bestimmte Ereignisse, sondern auch Gruppen von Ereignissen verwenden. Eine Ereignisgruppe fasst bestimmte Ereignisse unter einem Namen zusammen. Die vollständige Liste der Ereignisgruppen und der Zuordnung von Ereignissen zu diesen Gruppen finden Sie in der OnlineDokumentation unter dem Stichwort DDL-Trigger, Ereignisgruppen zum Auslösen. Unser Trigger verwendet die Ereignisse CREATE_TABLE, ALTER_TABLE und DROP_TABLE. Für diese drei Ereignisse existiert die Ereignisgruppe DDL_TABLE_EVENTS. Bei der Erstellung des Triggers hätten wir also statt:
auch dies schreiben können:
334
7.7 Trigger Im Objekt-Explorer finden Sie den erstellten Trigger unter Programmierbarkeit/Datenbanktrigger in der entsprechenden Datenbank (Abbildung 7.8).
Abbildung 7.8 Der erzeugte DDL-Trigger im Objekt-Explorer
Nun, da der Trigger existiert, wollen wir natürlich ausprobieren, ob er auch funktioniert. Hierzu wird mit dem folgenden Skript einfach eine Tabelle erstellt, geändert und anschließend wieder gelöscht:
Das Skript verwendet die drei vom Trigger überwachten DDL-Anweisungen CREATE TABLE, ALTER TABLE und DROP TABLE. Der Trigger sollte also drei entsprechende Einträge in das Logbuch eingefügt haben. Dies können wir durch die folgende Abfrage überprüfen:
Die Abfrage sollte etwa ein Ergebnis wie Abbildung 7.9 in gezeigt liefern.
Abbildung 7.9 Inhalt der Tabelle Logbuch
Die dritte Spalte enthält alle Detaildaten für das jeweils protokollierte Ereignis im XMLFormat. Bei einem Klick auf den in dieser Spalte angezeigten Link wird das entsprechende
335
7 T-SQL-Programmierung XML-Dokument im Editor geöffnet. Für das erste Ereignis (CREATE TABLE) sieht dieses Dokument ungefähr so aus:
Es ist sehr schön zu erkennen, dass das Protokoll wirklich detailliert ist. Sie finden zum Beispiel Angaben über den Benutzer, der die Anweisung zu welcher Zeit auf welcher Datenbank ausgeführt hat, und auch den Text der Anweisung selber. Zum Schluss dieses Abschnitts soll nun der Trigger wieder von der Datenbank entfernt werden. Dies geschieht durch die Anweisung DROP TRIGGER, wobei die Zusatzklausel ON DATABASE angegeben werden muss, um anzuzeigen, dass es sich um einen DDLTrigger auf der Datenbank handelt:
DDL-Trigger für Serverereignisse Ein DDL-Trigger für die Überwachung von Serverobjekten wird durch die folgende Syntax erstellt:
Die Anzahl der Ereignisse, die für den Server überwacht werden können, ist längst nicht so hoch wie die für Datenbanken zur Verfügung stehenden. Insgesamt gibt es zwölf Ereignisse für die Überwachung des SQL Servers selber. Auch hier ist es so, dass die Namen für die Ereignisse eine Auswahl erleichtern, da sie direkt die DLL-Anweisung widerspiegeln. Zum Beispiel können Sie die folgenden Ereignisse verwenden: DROP_DATABASE CREATE_LOGIN Ebenso wie für Datenbankereignisse stehen auch für Serverereignisse Ereignisgruppen zur Verfügung, in denen bestimmte Ereignisse gebündelt sind. So gibt es zum Beispiel die Er-
336
7.7 Trigger eignisgruppe DDL_SERVER_LEVEL_EVENTS, welche die Ereignisse CREATE_DATABASE, ALTER_DATABASE und DROP_DATABASE enthält. Als Beispiel soll hier ein DDL-Trigger erzeugt werden, der das Löschen von SQL ServerLogins untersagt. Hierzu wird im Trigger einfach die Transaktion abgebrochen und eine Meldung ausgegeben. Dieser Trigger kann so erzeugt werden:
Natürlich werden auch DDL-Trigger für den Serverbereich im Objekt-Explorer angezeigt. Sie finden den erzeugten Trigger dort im Ordner Serverobjekte/Trigger (Abbildung 7.10).
Abbildung 7.10 Der erzeugte DDL-Trigger für den Server
Versuchen wir nun einmal, die oben erzeugte Anmeldung DavidBowman zu löschen, um zu sehen, ob der Trigger funktioniert:
Im Meldungsbereich der Ausgabe sollten Sie daraufhin Folgendes sehen:
Der erste Teil dieser Meldung wurde durch die PRINT-Anweisung des Triggers erzeugt. Die Meldung 3609 resultiert aus dem ROLLBACK TRANSACTION innerhalb des Triggers.
337
7 T-SQL-Programmierung Was nun noch fehlt, ist das Löschen des Triggers. Ein für den Server erstellter Trigger wird ebenfalls durch eine spezielle DROP TRIGGER-Syntax entfernt, die so aussieht:
In Kapitel 9 werden wir nochmals auf die Überwachung von Datenbanken und Servern zurückkommen, dort unter Verwendung des SQL Server Service Brokers.
7.8
Zusammenfassung Dieses Kapitel beschäftigte sich mit T-SQL als Programmiersprache. Sie wissen nun, welche Elemente die Sprache für die Ablaufsteuerung enthält und wie Sie T-SQL-Variablen einsetzen. In einem weiteren Abschnitt haben Sie erfahren, wie durch den Einsatz der Cursor eine zeilenbasierte Verarbeitung von Abfrageergebnissen vorgenommen werden kann. Ihnen ist jetzt auch geläufig, wie gespeicherte Prozeduren und benutzerdefinierte Funktionen in T-SQL erzeugt und verwendet werden. Schließlich wurde im letzten Abschnitt dieses Kapitels noch erklärt, welche Möglichkeiten Trigger für die automatische Ausführung von Code bieten und wie Trigger zur Überwachung von Servern und Datenbanken verwendet werden können.
338
8 XML-Integration XML hat sich mittlerweile als offener und universeller Standard für den Datenaustausch etabliert. Ich möchte an dieser Stelle gar nicht groß die Vor- und Nachteile von XML erörtern. Sicherlich gibt es für den Einsatz von XML mehr oder weniger geeignete Anwendungsgebiete, eine entsprechende Untersuchung oder Bewertung vorzunehmen ist aber nicht Gegenstand dieses Buches. Stellen wir zunächst einfach nur fest, dass der SQL Server seit der Version 2000 die Möglichkeit bietet, XML-Daten zu verarbeiten. Diese Möglichkeiten wurden nun mit dem SQL Server 2005 nochmals ganz erheblich erweitert. Aus diesem Grund gehört die Thematik XML-Integration natürlich in ein Buch über die Datenbankentwicklung mit dem SQL Server 2005 unbedingt hinein. In diesem Kapitel werden Sie zunächst erfahren, wie Sie Abfrageergebnisse mit der FOR XML-Klausel auf einfache Art in ein XML-Format überführen können. Daran anschließend wird die OPENXML-Funktion vorgestellt, welche die Konvertierung von XMLDokumenten in ein Tabellenformat ermöglicht. Sie erfahren dann etwas über den in der Version SQL Server 2005 hinzugekommenen nativen XML-Datentyp und die Möglichkeiten, XML-Dokumente oder -Fragmente abzufragen und zu verändern.
8.1
Tabellen im XML-Format darstellen: FOR XML FOR XML ist eine Klausel, die einfach zu einer SELECT-Anweisung hinzugefügt werden kann. Allein durch die Angabe dieser Klausel veranlassen Sie die Konvertierung der Ergebnismenge aus dem Tabellen- in das XML-Format. Die Art und Weise dieser Konvertierung können Sie hierbei in vielfältiger Weise bestimmen. indem Sie die FOR XMLKlausel durch eine Reihe von Optionen ergänzen. Eine (vereinfachte) SELECTAnweisung unter Verwendung von FOR XML hat die folgende Struktur:
339
8 XML-Integration Die FOR XML-Klausel ist seit der Version SQL Server 2000 verfügbar. Mit der neuen Version SQL Server 2005 sind einige nützliche Optionen für die bereits existierenden FOR XML-Klauseln RAW, AUTO und EXPLICIT hinzugekommen. Herausragend ist aber vor allem die Erweiterung um die neue Klausel FOR XML PATH. Diese Klausel bietet eine einfache Möglichkeit, das zurückgelieferte XML-Dokument oder -Fragment in vielfältiger Art zu gestalten. In den Kapiteln 8.1.4 und 8.1.6 wird dies näher erläutert. Generell gibt es also vier verschiedene FOR XML-Klauseln, die natürlich auch die zurückgelieferten XML-Daten in unterschiedlichen Formaten erstellen und die ihrerseits wiederum durch weitere Optionen ergänzt werden können. Wie in der obigen Syntax zu erkennen ist, sind dies die Klauseln:
In den folgenden Abschnitten werden wir uns zunächst mit diesen Klauseln beschäftigen, bevor wir dann in Abschnitt 8.1.5 auf die zusätzlich zur Verfügung stehenden Optionen eingehen. Für unsere einführenden Beispiele verwenden wir dabei zunächst die Tabelle Sales.SalesOrderHeader sowie die Sicht Sales.vSalesPerson der Datenbank AdventureWorks. Die in Listing 8.1 gezeigte SELECT-Anweisung verknüpft die Sicht mit der Tabelle über einen INNER JOIN und gibt einige Zeilen zurück. Listing 8.1 Beispielanweisung zur Auswahl einiger Bestellungen
Die Abfrage soll eine Liste der Vertriebsmitarbeiter und der Bestellungen, die diese Mitarbeiter initiiert haben, zurückliefern. Die Sicht Sales.vSalesPerson wird verwendet, um die Vertriebsmitarbeiter abzufragen, die Tabelle Sales.SalesOrderHeader enthält eine Liste aller aufgenommenen Bestellungen mit einer Referenz auf den zuständigen Vertriebsmitarbeiter. Die Tabelle und die Sicht werden über die in beiden enthaltene Spalte SalesPersonId verknüpft.
340
8.1 Tabellen im XML-Format darstellen: FOR XML Interessant ist hier die Verwendung der Funktion in der Unterabfrage. generiert bei jedem Aufruf einen eindeutigen GUID. Durch die Kombination der in der SELECT-Anweisung angegebenen TOP 20-Klausel mit der Sortierung nach werden 20 zufällig ausgewählte Zeilen aus der Tabelle Sales.SalesOrderHeader zurückgeliefert. Probieren Sie dies ruhig einmal aus, indem Sie die Unterabfrage:
separat ausführen. Auf diese Weise schränken wir unsere Ergebnismenge auf nur 20 Bestellungen ein, die wie gesagt zufällig ausgewählt werden. Ohne diese Einschränkung würden jeweils einige tausend Zeilen zurückgeliefert werden, was die Darstellung innerhalb dieses Buches sehr erschweren würde. Das Ergebnis der Abfrage aus Listing 8.1 sieht ungefähr so aus, wie in Abbildung 8.1 zu sehen.
Abbildung 8.1 Zufällig ausgewählte Bestellungen (nicht alle 20 Zeilen dargestellt)
8.1.1
FOR XML RAW
Ergänzen Sie die SELECT-Anweisung aus Listing 8.1 einfach um die Klausel FOR XML RAW:
341
8 XML-Integration �