8.2 Gespeicherte Prozeduren 197 ■ um den lesenden und schreibenden Zugriff auf Systemtabellen zu ermöglichen ■ um die Abfragen und Änderungen der Zugriffsrechte einer Datenbank durchzuführen ■ um die Speicherverwaltung einzelner Datenbanken zu überwachen bzw. zu ändern Die oben genannte Unterteilung der Systemprozeduren ist weder vollständig noch eindeutig. Viele Systemprozeduren können z.B. sowohl der ersten als auch der zweiten Gruppe zugeordnet werden. Weil Systemprozeduren für unterschiedliche Aufgaben angewendet werden können, werden wir sie in den entsprechenden Kapiteln beschreiben. Diese Beschreibung wird nicht alle angebotenen Prozeduren enthalten. Für eine vollständige Auflistung der Systemprozeduren verweisen wir den Leser auf die Onlinedokumentation des Systems. Jeder Benutzer kann auch eigene Systemprozeduren mit der CREATE PROCEDURE-Anweisung erstellen. Die Voraussetzung für solche Prozeduren ist, dass sie mit dem Präfix sp_ beginnen und der Masterdatenbank angehören. Solche Prozeduren können unabhängig von der augenblicklich aktuellen Datenbank aufgerufen werden. Benutzerdefinierte Systemprozeduren können in SQL Server 2005 durch gespeicherte Prozeduren mit Hilfe von Common Language Runtime (CLR) implementiert werden. Diese Prozeduren sind das Thema des folgenden Abschnitts. 8.2.3 Gespeicherte Prozeduren und CLR In älteren SQL Server-Versionen können gespeicherte Prozeduren nur mit Hilfe der Transact-SQL-Sprache implementiert werden. SQL Server 2005 führt zum ersten Mal eine alternative Möglichkeit ein: Common Language Runtime (CLR). Mit CLR können verschiedene Datenbankobjekte wie Prozeduren, Funktionen und benutzerdefinierte Datentypen mit Hilfe verschiedener Programmiersprachen (C# und Visual Basic 2005) implementiert werden. Um eine gespeicherte Prozedur auf der Basis von CLR zu implementieren, übersetzen und speichern, müssen folgende Schritte (in der vorgegebenen Reihenfolge) ausgeführt werden: ■ Implementiere eine gespeicherte Prozedur mit Hilfe von C# bzw. Visual Basic 2005 und übersetze diese mit Hilfe des entsprechenden Übersetzers. ■ Verwende die CREATE ASSEMBLY-Anweisung, um die ausführbare Version (Executable File) des Programms zu erstellen. ■ Speichere die Prozedur mit Hilfe der CREATE PROCEDURE-Anweisung. ■ Führe die Prozedur mit Hilfe der EXECUTE-Anweisung aus. 198 8 SQL-Erweiterungen und gespeicherte Prozeduren Abb. 8–1 Ausführung einer gespeicherten Prozedur mit Hilfe von CLR Abbildung 8–1 zeigt, wie eine gespeicherte Prozedur mit Hilfe von CLR übersetzt und gespeichert werden kann. Man verwendet eine Programmierumgebung wie Visual Studio 2005, um das Programm zu implementieren. Nach der Implementierung wird der entsprechende C#- bzw. Visual Studio 2005-Übersetzer gestartet, damit der Objekt-Code erstellt wird. Der entsprechende Code, der in eine .dllDatei gespeichert ist, wird anschließend als Quelle von der CREATE ASSEMBLYAnweisung verwendet, um den Zwischencode des Programms zu erstellen. Die anderen beiden Schritten sind schon bekannt: Mit der CREATE PROCEDURE-Anweisung wird der Code gespeichert, während die EXECUTE-Anweisung für die Ausführung der gespeicherten Prozedur verwendet wird. Die folgenden fünf Beispiele zeigen alle oben beschriebenen Schritte. Beispiel 8.10 beginnt mit einem C#-Programm, mit dem eine gespeicherte Prozedur implementiert wird. Beispiel 8.10 using using using using System; System.Data; System.Data.Sql; System.Data.SqlServer; 8.2 Gespeicherte Prozeduren 199 using System.Data.SqlTypes; public partial class StoredProcedures { [SqlProcedure] public static int GetEmployeeCount() { int iRows; SqlCommand sqlCmd = SqlContext.GetCommand(); sqlCmd.CommandText = "select count(*) as 'Employee Count' " + "from mitarbeiter"; iRows = (int)sqlCmd.ExecuteScalar(); return iRows; } }; Das C#-Programm in Beispiel 8.10 verwendet eine Abfrage: select count(*) as 'Employee Count' from mitarbeiter Damit wird die Anzahl der Reihen der Tabelle mitarbeiter berechnet. Die USING-Anweisungen am Anfang des Programms spezifizieren Namen von Paketen, wie z.B. System.Data. Diese Anweisungen ermöglichen die Angabe von Klassennamen innerhalb des Programms, ohne den Namen des Pakets, zu dem die Klasse gehört, jedes Mal explizit schreiben zu müssen. Die erste Klasse, die in Beispiel 8.10 spezifiziert wird, heißt StoredProcedures. Diese Klasse, zusammen mit dem dazugehörigen [SqlProcedure]-Attribut, informiert das System, dass hier eine gespeicherte Prozedur mit Hilfe von Common Language Runtime implementiert wird. Innerhalb der StoredProcedures-Klasse wird die Methode GetEmployeeCount() implementiert. Die SqlContext-Klasse, die zum Paket System.Data. SqlServer gehört, wird anschließend mit ihrer Methode GetCommand() verwendet, um eine Instanz der SqlCommand-Klasse (sqlCmd) zu referenzieren, welche die Verbindung zu dem aktuellen System herstellt. Die folgenden Programmzeilen: sqlCmd.CommandText = "select count(*) as 'Employee Count' " + "from mitarbeiter"; iRows = (int)sqlCmd.ExecuteScalar(); verwenden die SELECT-Anweisung für die Ermittlung der Reihenanzahl in der Tabelle mitarbeiter und für die Ausgabe des Ergebnisses. Die Anweisung ist mit Hilfe des CommandText-Attributes der Instanz sqlCmd spezifiziert. Danach wird die ExecuteScalar()-Methode in Bezug auf die selbe Instanz aufgerufen, die einen skalaren Wert zurückgibt. Dieser Wert wird umgewandelt und der iRows-Variablen zugewiesen. 200 8 SQL-Erweiterungen und gespeicherte Prozeduren Die Verwendung von Common Language Runtime ist standardmäßig deaktiviert. Um diese Funktionalität benutzen zu können, muss die Option clr_enabled der Systemprozedur sp_configure aktiviert werden. (Führen Sie anschließend die RECONFIGUREAnweisung aus, um die Konfigurationswerte zu aktualisieren.) Beispiel 8.11 zeigt den ersten Schritt: die Übersetzung des in Beispiel 8.10 implementierten Programms mit Hilfe des C#-Übersetzers. Beispiel 8.11 csc /target:library GetEmployeeCount.cs /reference:"c:\ProgramFiles\Microsoft SQLServer\MSSQL.1\MSSQL\Binn\sqlaccess.dll" In Beispiel 8.11 wird das Programm namens GetEmployeeCount übersetzt. csc ist das Betriebssystemkommando, mit dem der C#-Übersetzer aufgerufen wird. (Vor dem Aufruf des csc-Kommandos muss sein Speicherort mit Hilfe der Umgebungsvariablen PATH spezifiziert werden.) Die Option /target legt den Namen des C#-Quellprogramms fest, während die Option /reference die .dll-Dateien spezifiziert, die für den Übersetzungsprozess notwendig sind. Beispiel 8.12 zeigt den zweiten Schritt: die Verwendung der CREATE ASSEMBLY-Anweisung, um die ausführbare Version des Programms zu erstellen. Beispiel 8.12 CREATE ASSEMBLY GetEmployeeCount FROM 'C:\GetEmployeeCount.dll' WITH PERMISSION_SET = SAFE Die CREATE ASSEMBLY-Anweisung verwendet den übersetzten Code, gespeichert in einer .dll-Datei, um das entsprechende Datenbankobjekt zu erstellen. Diese Anweisung hat folgende allgemeine Syntax: CREATE ASSEMBLY assembly_name [ AUTHORIZATION owner_name ] FROM { dll_file} [ WITH PERMISSION_SET = { SAFE | EXTERNAL_ACCESS | UNSAFE } ] assembly_name ist der Name des neuen Datenbankobjektes. Die optionale AUTHORIZATION-Klausel spezifiziert den Namen des Eigentümers des Objektes. Die FROM-Klausel definiert den Pfadnamen, wo die übersetzte Datei (in Form einer dll-Bibliothek) sich befindet. (In Beispiel 8.12 haben wir die .dll-Datei aus dem Framework-Verzeichnis in das C:-Dateiverzeichnis kopiert.) 8.2 Gespeicherte Prozeduren 201 Die WITH PERMISSION SET-Klausel hat sehr große Bedeutung bei der Erstellung eines Datenbankobjektes mit Hilfe von CLR. Sie spezifiziert, auf welche Ressourcen das erstellte Datenbankobjekt zugreifen kann. Die Angabe SAFE ist die restriktivste Angabe von allen: Falls der Code eines Programms mit dieser Angabe erstellt wird, kann das Programm auf externe Systemressourcen wie Dateien nicht zugreifen. Die zweite Angabe – EXTERNAL ACCESS – erlaubt dem Code, auf gewisse externe Systemressourcen zuzugreifen, wie das Dateisystem und anderen Datenbanken mit Hilfe von ADO.NET. UNSAFE erlaubt einen uneingeschränkten Zugriff auf Ressourcen (innerhalb und außerhalb des SQL Server-Systems). Diese drei Angaben regeln, wie sicher der relationale Datenbankserver sein kann, dass das erstellte Objekt das ganze Datenbanksystem gefährden kann. Folgendes Beispiel zeigt die Erstellung der gespeicherten Prozedur auf der Basis des C#-Programms aus Beispiel 8.10. Beispiel 8.13 CREATE PROCEDURE GetEmployeeCount AS EXTERNAL NAME GetEmployeeCount.StoredProcedures.GetEmployeeCount Die CREATE PROCEDURE-Anweisung in Beispiel 8.13 zeigt eine Erweiterung, die wir bis jetzt noch nicht gesehen haben: AS EXTERNAL NAME. Mit dieser Option wird der Code, der mit Hilfe von CLR generiert wurde, spezifiziert. Der Name in dieser Klausel sieht standardmäßig so aus: assembly_name.class_name.method_name assembly_name spezifiziert den Namen des in der CREATE ASSEMBLY-Anwei- sung erstellten Datenbankobjektes (in diesem Fall die gespeicherte Prozedur). class_name ist der Name der public-Klasse, die im Quellprogramm (siehe Beispiel 8.10) definiert ist, während method_name den Namen der Methode festlegt, die innerhalb der public-Klasse angegeben ist. (In Beispiel 8.10 heißt die publicKlasse StoredProcedures und die zu ihr gehörende Methode GetEmployeeCount.) Das letzte Beispiel in dieser Beispielfolge zeigt, wie die mit Hilfe von CLR erstellte gespeicherte Prozedur ausgeführt werden kann. Beispiel 8.14 DECLARE @ret INT EXECUTE @ret=GetEmployeeCount PRINT @ret Beispiel 8.14 zeigt die Transact-SQL-Anweisungsfolge, mit der ein Datenbankobjekt (in diesem Fall eine gespeicherte Prozedur) ausgeführt werden kann. Der 202 8 SQL-Erweiterungen und gespeicherte Prozeduren integer-Wert, der die Prozedur in ihrer SELECT-Anweisung ermittelt, wird einer vorher definierten Variablen mit Hilfe der EXECUTE-Anweisung zugewiesen. (Die anschließende PRINT-Anweisung gibt den Wert 7 aus, weil die Tabelle mitarbeiter in unserem Fall sieben Reihen enthält.) 8.3 Benutzerdefinierte Funktionen Programmiersprachen unterstützen generell zwei Routine-Typen: ■ Prozeduren ■ benutzerdefinierte Funktionen Prozeduren enthalten keine oder mehrere Parameter, liefern aber keine Rückgabewerte. Im Unterschied zu Prozeduren liefern Funktionen einen oder mehrere Rückgabewerte. (Wie aus dem folgenden Unterabschnitt ersichtlich wird, können benutzerdefinierte Funktionen beim relationalen Datenbankserver nur einen einzigen Rückgabewert liefern.) 8.3.1 Erstellung und Ausführung benutzerdefinierter Funktionen Benutzerdefinierte Funktionen werden mit Hilfe der CREATE FUNCTIONAnweisung erstellt. Diese Anweisung hat folgende Syntax: CREATE FUNCTION [schema_name.]funktion_name [[(]@param_1 typ_1 [=default_1] [{,@param_2 typ_2 [=default_2]]}...[)]] RETURNS {skalar_wert | {[@variable} TABLE}} [WITH {ENCRYPTION | SCHEMABINDING} AS {block | RETURN(select_anw)} schema_name spezifiziert den Namen des Schemas, während funktion_name den Namen der benutzerdefinierten Funktion kennzeichnet. @param_1, @param_2 … sind Parameternamen, während typ_1, typ_2 … ihren Datentypen spezifizieren. (Parameter sind Werte, die von dem Aufrufprogramm an die benutzerdefinierte Funktion geschickt und innerhalb der Funktion verwendet werden.) default_1, default_2 … kennzeichnen optionale Standardwerte der entsprechenden Parameter. Die RETURNS-Klausel definiert den Datentyp des Rückgabewertes. Dieser kann entweder einen Standarddatentyp haben oder den Datentyp namens TABLE. Eine benutzerdefinierte Funktion beim relationalen Datenbankserver kann entweder einen skalaren Wert oder eine Tabelle als Rückgabewert liefern. Eine Funktion liefert einen skalaren Wert, falls in der RETURNS-Klausel ein Standarddatentyp spezifiziert wird. Jede Funktion mit der Angabe TABLE in dieser Klausel gibt eine ganze Tabelle zurück. 8.3 Benutzerdefinierte Funktionen 203 Abhängig davon, wie der Funktionskörper definiert ist, können Funktionen mit den Tabellen als Rückgabewerten: ■ inline oder ■ mit mehreren Anweisungen sein. Falls die RETURNS-Klausel die Option TABLE ohne irgendwelche zusätzliche Angaben verwendet, handelt es sich um eine inline-Funktion. Eine solche Funktion liefert das Ergebnis als eine Variable vom Typ TABLE. Eine Funktion mit mehreren Anweisungen in der RETURNS-Klausel enthält einen Namen, an den das Schlüsselwort TABLE angefügt ist. (Der Name wird damit eine interne Variable vom Typ TABLE.) Die Variable kann verwendet werden, um Reihen einzufügen und sie anschließend als den Rückgabewert der Funktion zurückzugeben. Die Angabe ENCRYPTION verursacht die Verschlüsselung der Anweisungen innerhalb der benutzerdefinierten Funktion. Diese Angabe ist empfehlenswert, wenn die Implementierung der Funktion nicht allgemein bekannt werden darf. Die alternative WITH SCHEMABINDING-Klausel bindet die benutzerdefinierte Funktion zum Datenbankobjekt, das sie referenziert. (Das Ergebnis dieser Referenzierung ist, dass jeder Versuch, das Datenbankobjekt zu ändern, fehlschlägt.) Die von einer Funktion referenzierten Datenbankobjekte müssen gewisse Kriterien erfüllen, damit die SCHEMABINDING-Klausel mit ihnen verwendet werden kann. Diese sind: ■ Alle Sichten und benutzerdefinierte Funktionen, die durch die angegebene Funktion referenziert sind, müssen selbst gebunden werden (d.h. mit WITH SCHEMABINDING definiert werden). ■ Alle referenzierten Datenbankobjekte (Tabellen, Sichten, benutzerdefinierte Funktionen) müssen zu derselben Datenbank wie die angegebene Funktion gehören. block kennzeichnet einen BEGIN-Block, der den Implementierungsteil der Funktion enthält. Die letzte Anweisung im block muss eine RETURN-Anweisung sein. (Der Wert des Argumenten der RETURN-Anweisung entspricht dem Rückgabewert der Funktion.) Im BEGIN-Block können nur folgende Anweisungen angegeben werden: ■ ■ ■ ■ Zuweisungsanweisungen (z.B. die SET-Anweisung) die WHILE- und IF-Anweisungen die DECLARE -Anweisung die SELECT-Anweisung, mit der den Spaltenwerten entsprechende Variablen zugewiesen werden ■ die INSERT-, UPDATE-, und DELETE-Anweisung, die die Werte der Variablen vom Typ TABLE ändern 204 8 SQL-Erweiterungen und gespeicherte Prozeduren Gleich nach der Erstellung einer Datenbank haben nur die Mitglieder der Datenbankrollen db_owner und db_ddladmin das Recht, die CREATE FUNCTIONAnweisung auszuführen. Anschließend können sie auch den anderen Benutzern mit Hilfe der GRANT CREATE FUNCTION-Anweisung die Rechte zur Erstellung von Funktionen vergeben (siehe Kapitel 12). Das folgende Beispiel zeigt die Erstellung einer einfachen benutzerdefinierten Funktion. Beispiel 8.15 -- Diese benutzerdefinierte Funktion berechnet zusätzliche -- Kosten, die entstehen, falls Projektmittel erhöht werden. USE beispiel GO CREATE FUNCTION compute_costs (@prozent INT =10) RETURNS DECIMAL(16,2) BEGIN DECLARE @kosten DEC (14,2), @sum_mittel dec(16,2) SELECT @sum_mittel = SUM (mittel)FROM projekt SET @kosten = @sum_mittel * @prozent/100 RETURN @kosten END Die Funktion compute_costs berechnet die Kosten, die entstehen, falls alle Projektmittel erhöht werden. Im BEGIN-Block werden zuerst zwei lokale Variablen definiert: @kosten und @sum_mittel. Der zweiten Variablen wird die Summe aller Mittel mit Hilfe der SELECT-Anweisung zugewiesen. Danach berechnet die Funktion alle zusätzlichen Kosten und gibt den Wert mit Hilfe der RETURNAnweisung zurück. Jede benutzerdefinierte Funktion kann innerhalb einer DML-Anweisung (SELECT, INSERT, UPDATE und DELETE) aufgerufen werden. Um eine benutzerdefinierte Funktion aufzurufen, muss der Funktionsname zusammen mit einem Klammerpaar angegeben werden. Innerhalb des Klammerpaares können ein oder mehrere Argumente spezifiziert werden. (Argumente sind Werte, die den definierten Funktionsparametern zugewiesen werden. Jedes Argument wird dem entsprechenden Parameter zugewiesen.) Das folgende Beispiel zeigt, wie die Funktion compute_costs (Beispiel 8.15) innerhalb einer SELECT-Anweisung verwendet werden kann. Beispiel 8.16 USE beispiel select pr_nr, pr_name from projekt WHERE mittel < dbo.compute_costs(25) 8.3 Benutzerdefinierte Funktionen 205 Das Ergebnis ist: pr_nr pr_name ----------------p2 Gemini Das Ergebnis des Beispiels 8.16 gibt alle Namen und die Nummern aller Projekte aus, bei welchen die Mittel kleiner sind als zusätzliche Kosten der Mittelerhöhung für den angegebenen Prozentsatz. Jeder Name einer benutzerdefinierten Funktion, der in einer Transact-SQL-Anweisung angegeben wird, muss in der Form: schema_name.funktions_name spezifiziert werden. Das folgende Beispiel zeigt eine benutzerdefinierte Funktion, deren Rückgabewert vom Typ TABLE ist. Beispiel 8.17 USE beispiel GO create function mitarbeiter_im_projekt (@pr_nummer char(4)) returns table as return (select m_vorname, m_name from arbeiten, mitarbeiter where mitarbeiter.m_nr = arbeiten.m_nr and pr_nr = @pr_nummer) Die Funktion mitarbeiter_im_projekt wird benutzt, um die Namen aller Mitarbeiter eines Projektes zu ermitteln. Der Eingabeparameter @pr_nummer spezifiziert eine Projektnummer. Weil die Funktion im Allgemeinen mehrere Reihen als Ergebnis liefert, muss die RETURNS-Klausel die TABLE-Angabe enthalten. Beispiel 8.18 gibt die Namen und Projektnummer aller Mitarbeiter aus, die im Projekt p3 arbeiten. Beispiel 8.18 USE beispiel select * from mitarbeiter_im_projekt('p3') Das Ergebnis ist: m_vorname m_name ----------------------------Petra Huber Rainer Meier Brigitte Kaufmann 206 8 SQL-Erweiterungen und gespeicherte Prozeduren Der relationale Datenbankserver unterstützt zusätzlich die ALTER FUNCTIONAnweisung, die die Struktur einer benutzerdefinierten Funktion ändert. Diese Anweisung wird gewöhnlich benutzt, um die WITH SCHEMABINDING-Klausel zu entfernen. Die ALTER FUNCTION-Anweisung hat dieselben Optionen wie die CREATE FUNCTION-Anweisung. 8.3.2 Benutzerdefinierte Funktionen und CLR Alles, was bis jetzt über die Erstellung von gespeicherten Prozeduren mit Hilfe von CLR gesagt wurde, gilt auch für benutzerdefinierte Funktionen. Der einzige Unterschied ist, dass die Anweisung CREATE FUNCTION anstelle von CREATE PROCEDURE verwendet wird, wenn man eine benutzerdefinierte Funktion als Datenbankobjekt speichern will. (Ein zweiter Unterschied ist, dass Funktionen in einem anderen Kontext als gespeicherte Prozeduren verwendet werden, wie wir in Beispiel 8.22 zeigen werden.) Folgendes Beispiel erstellt ein Programm, mit dem wir anschließend zeigen werden, wie benutzerdefinierte Funktionen mit Hilfe der C#-Sprache übersetzt und angewendet werden können. Beispiel 8.19 using System; using System.Data.Sql; using System.Data.SqlTypes; public class budgetPercent { private const float percent = 10; public static SqlDouble computeBudget(float budget) { float budgetNew; budgetNew = budget * percent; return budgetNew; } }; Das C#-Programm in Beispiel 8.19 zeigt die Implementierung einer benutzerdefinierten Funktion, die einen neuen Wert der Projektmittel berechnet, in dem der alte Wert mit der prozentuellen Erhöhung (in unserem Programm 10 %) multipliziert wird. Das C#-Programm in Beispiel 8.19 wird nicht erläutert, weil es identische Klassen und Methoden verwendet, die schon in Beispiel 8.10 vorkommen. Genauso wird der Aufruf des csc-Kommandos nicht gezeigt. Dieser Schritt ist in Beispiel 8.11 beschrieben. Beispiel 8.20 zeigt die CREATE ASSEMBLY-Anweisung, mit der aus der .dllDatei das entsprechende Objekt erstellt wird. 8.4 Zusammenfassung 207 Beispiel 8.20 CREATE ASSEMBLY computeBudget FROM 'C:\computeBudget.dll' WITH PERMISSION_SET = SAFE In Beispiel 8.20 wird das Objekt computeBudget erstellt, das anschließend für die Erstellung der benutzerdefinierten Funktion verwendet wird. (Alle Optionen der CREATE ASSEMBLY-Anweisung, die in Beispiel 8.20 vorkommen, sind im Zusammenhang mit Beispiel 8.12 erörtert worden.) Beispiel 8.21 CREATE FUNCTION ReturncomputeBudget (@budget Real) RETURNS FLOAT AS EXTERNAL NAME computeBudget.budgetPercent.computeBudget Die CREATE FUNCTION-Anweisung in Beispiel 8.21 speichert das computeBudget-Objekt als benutzerdefinierte Funktion. Diese Funktion kann anschließend in verschiedenen DML-Anweisungen verwendet werden. Das folgende Beispiel zeigt den Aufruf dieser Funktion. Beispiel 8.22 USE beispiel select dbo.ReturncomputeBudget (321.50) Mit der SELECT-Anweisung in Beispiel 8.22 wird der neue (erhöhte) Wert der Produktmittel auf der Basis des alten Wertes (321.50) und der prozentuellen Erhöhung (10 %) berechnet. 8.4 Zusammenfassung Der relationale Datenbankserver ist eines der wenigen relationalen Datenbankmanagementsysteme, der prozedurale Erweiterungen sowohl im Zusammenhang mit der SQL-Sprache selbst als auch für die Erstellung der gespeicherten Prozeduren unterstützt. (Die meisten DBMS unterstützen prozedurale Erweiterungen ausschließlich in Bezug auf gespeicherte Prozeduren.) Ein weiteres Merkmal des relationalen Datenbankservers ist die Unterstützung zahlreicher Systemprozeduren. Diese werden u.a. für die komplette Administrierung eines SQL Server-Systems benutzt. Das folgende Kapitel behandelt Indizes. 208 8 SQL-Erweiterungen und gespeicherte Prozeduren 8.5 Aufgaben A.8.1 Definieren Sie die Anweisungsfolge, die 3.000 Reihen in die Tabelle mitarbeiter lädt. Die Werte der Spalte m_nr sollen zwischen 1 und 3.000 liegen und eindeutig sein. Alle Werte der Spalten m_name, m_vorname und abt_nr sollen auf »Kurz«, »Jochen« und »a1« in dieser Reihenfolge gesetzt werden. A.8.2 Modifizieren Sie die Aufgabe A.8.1 so, dass die Werte der Spalte m_nr per Zufallsgenerator (mit Hilfe der RAND-Funktion) spezifiziert werden. (Hinweis: Benutzen Sie auch die Systemfunktionen DATEPART und GETDATE [siehe Kapitel 3], um Zufallswerte zu generieren.)