150 3 Eine neue Datenbank erstellen ■ 3.6 Tabelle mit Filestream und FileTable In einem früheren Abschnitt dieses Kapitels habe ich Ihnen gezeigt, wie man eine Datenbank mit einer Filestream-Dateigruppe anlegt, und Ihnen das grundlegende Konzept von Filestream und FileTable erläutert. FileTable ist eine Erweiterung, die mit dem SQL Server 2012 eingeführt worden ist, und auf Filestream basiert. Nun möchte ich Ihnen in zwei Abschnitte unterteilt zeigen, wie man Filestream in einer Tabelle nutzt und wie man FileTable einsetzt. 3.6.1 Tabelle mit Filestream erstellen Leider bietet der Tabellen-Designer im Management Studio keine Möglichkeit, eine Spalte für Filestream zu definieren. Daher müssen Sie Tabellen mit Filestream manuell mit einer SQL-Anweisung erstellen. Das ist auch der Grund, warum ich dieses Thema erst gegen Ende des Kapitels behandle. Ich möchte mit Ihnen in der Video-Datenbank eine Tabelle erstellen, in der Videodateien mit ein wenig Zusatzinformation versehen gespeichert werden sollen. Für diese Videodateien soll Filestream genutzt werden. Um Filestream zu nutzen, muss eine Tabelle folgende Voraussetzungen erfüllen: Eine Spalte vom Datentyp varbinary(max) muss mit dem Attribut FILESTREAM versehen werden. ACHTUNG! Filestream ist auch beim SQL Server 2014 nur für den Datentyp varbinary(max) und nicht für varchar(max) verfügbar. Wenn Sie Filestream für umfangreiche Texte nutzen möchten, müssen Sie für diese anstelle von varchar(max) daher varbinary(max) verwenden. Die Tabelle muss eine Spalte enthalten, die als UNIQUEIDENTIFIER und ROWGUID definiert ist. Ein UNIQUEIDENTIFIER ist ein Wert, der aus 32 hexadezimalen Stellen besteht. Nach der achten, zwöl en, sechzehnten und zwanzigsten Stelle kommt jeweils ein Bindestrich (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). Während eine als IDENTITY definierte Spalte immer innerhalb der Tabelle eindeutige Werte enthält, enthält eine als Unique Identifier festgelegte Spalte eindeutige Werte über alle Tabellen einer Datenbank hinweg. Damit ist ein absolut eindeutiger Wert innerhalb der Datenbank gegeben. Während Identity-Spalten beim Anlegen eines neuen Datensatzes automatisch mit einem neuen Wert versehen werden, geschieht dies bei Unique Identifier-Spalten nicht. Die ROWGUID-Eigenscha muss zusätzlich gesetzt werden, sonst bekommen Sie beim Erstellen der Tabelle mit Filestream eine Fehlermeldung. Ähnlich wie eine IDENTITY, kann diese Eigenscha auch nur für eine Spalte in einer Tabelle gesetzt werden. Anstelle über referenziert den Spaltennamen kann sie in einem SELECT-Statement auch mit werden, analog zu einer Identitätsspalte, die Sie auch mit $IDENTITY auswählen können. FROM tabellenname;) ( 3.6 Tabelle mit Filestream und FileTable 151 Dieser eindeutige Wert wird für die Assoziierung der im Filesystem gespeicherten Datei mit dem Datensatz verwendet. Damit ist ausgeschlossen, dass es auf dieser Ebene zu Namenskonflikten kommt, auch wenn in mehreren Tabellen Filestream genutzt wird. (Unique Identifier werden ja auch bei Replikation genutzt, um Datensätze eindeutig zu identifizieren.) Die Unique Identifier-Spalte darf nicht leer sein und muss daher als NOT NULL definiert werden. Außerdem muss sie mit einem UNIQUE KEY-Constraint versehen werden, um Eindeutigkeit zu erzwingen. Damit diese Werte automatisch vergeben werden, was sehr oder zweckmäßig ist, wird als Standardwert die Funktion verwendet. Die erste Funktion liefert einen global eindeutigen zufälligen Wert, die zweite einen sequenziell aufsteigenden, ähnlich zu IDENTITY. Der Vorteil von liegt darin, dass sie schneller einen Wert liefert. Dafür ist der nächste Wert vorhersehbar, was aber in unserem Szenario keine Rolle spielt. Erstellen wir in der Datenbank video nun eine neue Tabelle mit dem Namen medien. Dazu verwenden wir folgende Anweisung. In der ersten Variante nehmen wir die Spalte id als UNIQUEIDENTIFIER. Anstelle eines -Constraints mit NOT NULL versehen kann -Constraint verwendet werden. natürlich auch gleich ein CREATE TABLE dbo.medien ( id UNIQUEIDENTIFIER ROWGUIDCOL CONSTRAINT pk_medien PRIMARY KEY CONSTRAINT df_medien_id DEFAULT NEWSEQUENTIALID(), aufnahmedatum date NOT NULL, video varbinary(max) FILESTREAM Die Spalte titel soll die Kurzbezeichnung des Videos aufnehmen. Deren Eingabe soll verpflichtend sein. Die beschreibung kann einen längeren Beschreibungstext aufnehmen. Ebenso soll das aufnahmedatum nicht fehlen. Die Spalte Video muss mit dem Datentyp varbinary(max) definiert werden. Entscheidend ist der Zusatz FILESTREAM dahinter. Durch diesen wird diese Spalte für Filestream definiert, sonst würden die Videoclips später direkt in der Datenbank gespeichert werden und die maximale Größe pro Video wäre 2 GB. Da wir in der Datenbank nur eine Dateigruppe für Filestream definiert haben, ist diese die Default-Dateigruppe dafür und muss in der SQL-Anweisung daher als Ziel nicht extra angegeben werden. PRAXISTIPP: Falls Sie als Primärschlüssel beziehungsweise als ID gerne einen anderen Wert als den kryptischen 32-stelligen Unique Identifier verwenden möchten, können Sie auch die Funktionen von Primärschlüssel und Unique Identifier voneinander trennen. Dies ist vor allem auch dann sinnvoll, wenn Sie später mit einem Fremdschlüssel auf diese Tabelle verweisen möchten. Dafür käme dann zum Beispiel folgende Tabellendefinition zum Einsatz: CREATE TABLE dbo.medien ( id int IDENTITY CONSTRAINT pk_medien PRIMARY KEY, aufnahmedatum date NOT NULL, 152 3 Eine neue Datenbank erstellen video varbinary(max) FILESTREAM, stream_id UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL CONSTRAINT uk_medien_stream UNIQUE CONSTRAINT df_medien_stream_id DEFAULT NEWSEQUENTIALID() Gegenüber der ersten Variante habe ich hier die id als Identität mit dem Datentyp int definiert. Für den benötigten Unique Identifier habe ich dafür die Spalte stream_id ergänzt. gesetzt. Das für den Wieder wird die RowGuid-Eigenscha über den Zusatz Anwendungsfall geforderte Unique Key-Constraint ersetzt hier den Primärschlüssel. Wichtig ist auch die Definition dieser Spalte als NOT NULL. Für die weiteren Schritte des Beispiels habe ich die zweite Variante verwendet. Um Ihnen eine mögliche Verwendung des Filestreams in einer Anwendung zu zeigen, möchte ich eine „quick and dirty“ erstellte Access-Anwendung verwenden. HINWEIS: Sie finden diese Datei mit dem Namen filestream.accdb unter den Buchdateien und können diese ab der Version Access 2007 einsetzen. Ebenso finden Sie dort das Skript Kapitel3.sql, das die Anweisungen zum Erstellen der Datenbank video und der Tabelle medien enthält. Diese Anweisungen können Sie verwenden, um jene in diesem Kapitel beschriebenen nicht abtippen zu müssen. Achten Sie aber bitte darauf, dass Sie bei Ihrer Konfiguration gegebenenfalls die Pfade der Dateigruppen an Ihre Gegebenheiten anpassen. Die Access-Datei nutzt die zweite vorgestellte Variante der Tabelle medien mit dem separaten Primärschlüssel. Wenn Sie diese Datei öffnen, muss diese mit Ihrer Datenbank auf Ihrem Server verbunden werden. Dazu muss die ODBC-Verbindungszeichenfolge angepasst werden. Um Ihnen die Verwendung der Datei so einfach wie möglich zu machen, bekommen Sie automatisch die Aufforderung zur Eingabe derselben, wenn Access beim Öffnen der Datei keine Verbindung zum aktuell hinterlegten SQL Server herstellen kann. Wegen des Verbindungstimeouts wird dies beim ersten Öffnen etwas dauern. Sie erhalten danach eine ODBC-Fehlermeldung und den Standarddialog zur Anpassung der ODBC-Anmeldung. Brechen Sie diese ab und Sie bekommen die Anzeige von Bild 3.69. Bild 3.69 ODBC-Verbindung eintragen 3.6 Tabelle mit Filestream und FileTable 153 Passen Sie hier die ODBC-Verbindungszeichenfolge an Ihre Gegebenheiten an: Wenn Sie Windows-Authentifizierung verwenden, müssen Sie lediglich Ihren Servernamen oder die IP-Adresse, analog zur Anmeldung mit dem Management Studio, eintragen: DATABASE=video;LANGUAGE=Deutsch IHR_SERVER;Trusted_Connection=Yes; Verwenden Sie SQL Server-Authentifizierung, ersetzen Sie zusätzlich Trusted_ Connection=Yes durch UID und : PWD=ihr_pwd;DATABASE=video;LANGUAGE=Deutsch UID=ihr_login; Ist bei Ihnen auf dem Client der hier verwendete ODBC Driver 11 für SQL Server noch nicht installiert, laden Sie sich diesen über das SQL Server 2014 Feature Pack wie in Kapitel 1 beschrieben herunter und installieren ihn nach. Speichern Sie Ihre Änderung, danach erfolgt neuerlich eine Prüfung, ob die Verbindung hergestellt werden kann. Ist diese erfolgreich, wird auch die ODBC-Verbindung der in Access verknüp en Tabelle medien angepasst und das Zielformular geöffnet. Da diese neue Verbindung nun gespeichert ist, ist diese Vorgangsweise nur einmalig notwendig und beim erneuten Öffnen von filestream.accdb gelangen Sie direkt ins Eingabeformular. Sie können nun einen ersten Datensatz erfassen. Ein paar Videoclips im Format WMV habe ich beigefügt, die Sie dazu verwenden können. Natürlich können Sie jede andere beliebige Mediadatei verwenden. Bevor wir einen ersten Datensatz anlegen, werfen wir einen Blick auf das Verzeichnis, das wir für die Filestream-Dateigruppe festgelegt haben. Bei mir ist dies der Ordner D:\DB_FS\ video_stream auf dem Server. Ich verbinde mich über den $-Share dorthin. Dieser Ordner ist – wie erwartet – im Moment noch fast leer: Nach dem Anlegen der Datenbank video befinden sich im Ordner ein Unterordner mit dem Namen $FSLOG und die Datei filestream.hdr. Nach dem Anlegen der Tabelle medien kommt ein Ordner, dessen Name das Format eines Unique Identifier aufweist, hinzu. Bild 3.70 Basisordner für Filestream, bevor Daten erfasst werden 154 3 Eine neue Datenbank erstellen Erfassen Sie nun über die Access-Anwendung Titel, Bezeichnung und Aufnahmedatum für den ersten Clip. Fügen Sie den Clip entweder per Drag & Drop ein oder verwenden Sie dazu im Kontextmenü den Befehl Objekt Einfügen. Bild 3.71 Datensatz über Access-Formular erfasst Für die Anwendung (in diesem Fall MS Access) ist Filestream vollkommen transparent. Es macht für Sie keinen Unterschied, ob die Videodatei direkt in der Datenbank oder auf dem Server im Filesystem gespeichert wird. Wo finden wir diese Datei nun auf dem Server? In einem der Unterordner. Der Filename ist für uns nicht sprechend, muss er aber auch nicht sein. Bild 3.72 Filestream-Objekt im Filesystem des Servers Wenn Sie diesen Datensatz löschen, verschwindet diese Datei wieder. Aktivieren Sie die Datei über ihre Anwendung, zum Beispiel indem Sie sie im Access-Formular doppelt anklicken, wird Sie vom SQL Server aus dem Filesystem gestreamt und an den Client übertragen. Wie gesagt, dies ist für die Clientanwendung alles transparent. Wenn wir den Datensatz im Abfrageeditor des Management Studios anzeigen, sehen wir die ID des Streamings in Form des Unique Identifiers. 3.6 Tabelle mit Filestream und FileTable 155 Bild 3.73 Datensatz mit Filestream Um den Film in der Beispielanwendung anzusehen, müssen Sie das Symbol im Formular nur doppelt anklicken. Der SQL Server streamt die Datei dann vom Filesystem an die Anwendung, in diesem Fall an Access. Hier wird das Objekt dann mit der mit dem Filetyp assoziierten Anwendung – in diesem Fall dem Media Player – abgespielt. Entscheidend ist, dass hier aus der Anwendung heraus kein Unterschied gegenüber einer in der Datenbank gespeicherten Datei zu erkennen ist. Bild 3.74 Wiedergabe der vom Filesystem des Servers gestreamten Datei Legen Sie noch weitere Datensätze an und löschen Sie auch wieder welche, um den Vorgang zu testen! ACHTUNG! Folgendes müssen Sie beim Einsatz von Filestream unbedingt beachten: Die vom SQL Server angelegten Ordner und Dateien sind nicht für den direkten Zugriff gedacht. Greifen Sie ausschließlich in Testumgebungen und auch nur lesend darauf zu. In Produktivsystemen sollten keine Benutzer Zugriffsrechte auf diese Ordnerstruktur haben. Der Tabellen-Designer des Management Studios kann mit Filestream nicht umgehen. Wenn Sie über diesen eine Änderung an der Tabelle vornehmen, die wie beschrieben eine Neuerstellung der Tabelle erfordert, dann fehlt der Filestream danach bei der neu angelegten Tabelle. Zumindest erfolgt das so sauber, dass bei meinem Test die Datei dabei in die Datenbank kopiert 156 3 Eine neue Datenbank erstellen worden ist. Wenn auch kein Filestream mehr vorhanden ist, so sind wenigstens keine Daten abhanden gekommen. d. h., nach außen hat die Änderung keine Probleme verursacht. Dennoch ist das natürlich nicht erwünscht. Daher bearbeiten Sie in der Praxis Tabellen mit Filestream mit DDL-Anweisungen wie ALTER TABLE direkt und nicht über den grafischen Tabellen-Designer. Wie Sie Datenbanken mit Filestream sichern und wiederherstellen und damit auch auf einen anderen Server transferieren können, lesen Sie in Kapitel 9, das sich unter anderem mit dem Thema Sicherung beschä igt. 3.6.2 Objekte in einer FileTable speichern Mit FileTable liefert der SQL Server eine spannende Erweiterung von Filestream. FileTable basiert auf der Technologie von Filestream und bietet die Möglichkeit, Dateien über das Netzwerk in einem Share auf dem Datenbankserver zu speichern. Diese Dateien tauchen dann wie von Zauberhand in einer Tabelle in einer Datenbank auf. Damit hat man dann in weiterer Folge die spannende Möglichkeit, über SQL in den Dateien zu suchen und diese auszuwerten. Da FileTable auf der Technologie von Filestream basiert, werden dieselben Anforderungen an den Server und die verwendete Datenbank gestellt. Diese habe ich Ihnen in diesem Kapitel unter Punkt 3.1.4 erläutert. Für unser Beispiel erstellen wir eine neue Datenbank mit Filestream. Sie können dies wahlweise über den grafischen Dialog im Objekt-Explorer oder über folgende DDL-Anweisung realisieren. Passen Sie dabei die Pfade an Ihre Gegebenheiten an. CREATE DATABASE dateien CONTAINMENT = NONE ON PRIMARY ( NAME = 'dateien_data', FILEGROUP DATENFILES CONTAINS FILESTREAM ( NAME = 'dateien_fs', FILENAME = 'D:\DB_FS\dateien_fs') LOG ON ( NAME = 'dateien_log', Zur Erinnerung: Wir haben in diesem Kapitel Filestream für unseren Server aktiviert und als Freigabename dabei dateiablage vergeben und Vollzugriff aktiviert. Diese Einstellungen können über die Servereigenscha en vom Objekt-Explorer aus eingestellt werden. Damit besteht schon einmal der Zugriff auf den Share auf meinem Server mit \\srvsql2014\ dateiablage. In diesen Ordner kann ich natürlich direkt keine Datei kopieren, ich erhalte dabei eine Fehlermeldung über eine Zugriffsverletzung. Damit wir Dateien hier ablegen können, müssen wir noch einen Ordnernamen für unsere Datenbank festlegen. Hier ist folgende hierarchische Struktur vorgesehen: 3.6 Tabelle mit Filestream und FileTable 157 Der Freigabename repräsentiert die gesamte Serverinstanz von SQL Server 2014. Für jede Datenbank auf dieser Serverinstanz wird ein eigener Ordner darin festgelegt. Sie können auf einem Server mehrere Datenbanken mit FileTable verwenden, daher ist diese Ebene notwendig. Den Ordnernamen auf Datenbankebene sowie die Zugriffsart können Sie entweder über den grafischen Dialog oder über entsprechende ALTER TABLE-Anweisungen festlegen. Öffnen Sie die Datenbankeigenscha en über den Objekt-Explorer. Auf der Seite OPTIONEN finden Sie die notwendigen Einstellungen in der Kategorie FILESTREAM. Legen Sie den Namen des Ordners in der Eigenscha FILESTREAM-Verzeichnisname fest. Damit Sie später Dateien dort ablegen können, müssen Sie die Eigenscha Nicht transaktionsgebundener FILESTREAM-Zugriff auf Full einstellen. Sollen die Dateien später einmal nur gelesen werden können, können Sie diese Eigenscha auf ReadOnly setzen. Um den Zugriff überhaupt zu sperren, stellen Sie die Voreinstellung Off wieder her. Bild 3.75 Verzeichnisname und Zugriff Alternativ können Sie diese Einstellungen auch über folgende SQL-Anweisungen vornehmen: ALTER DATABASE dateien ALTER DATABASE dateien Über die Zusatzoption legen Sie fest, dass diese Änderung nicht auf das Beenden von Transaktionen in der Datenbank wartet und in diesem Fall fehlschlägt. Typi- 158 3 Eine neue Datenbank erstellen scherweise verwenden Sie diese Anweisungen jedoch nicht, wenn in der Datenbank gerade Transaktionen ablaufen. Die weiteren Optionen, die in SQL neben FULL für die Zugriffsoption vergeben werden können, lauten OFF und READ_ONLY. Nach dieser Änderung können wir nun eine Ebene weiter über den Explorer zugreifen, aber noch immer keine Dateien ablegen: \\srvsql2014\dateiablage\dateidatenbank Erstellen einer FileTable Der letzte Schritt ist das Erstellen einer FileTable. Diese Tabelle hat eine fixe systemseitig vorgegebene Struktur. Da diese Tabellen auch keine gewöhnlichen Tabellen im herkömmlichen Sinne sind, finden wir sie im Objekt-Explorer auch in einer eigenen Rubrik unter den Tabellen wieder. Es macht durchaus Sinn, sich das Statement zum Erstellen einer solchen Tabelle automatisch zu generieren. Dazu wählen Sie im Kontextmenü entweder auf dem Ordner Tabellen oder FileTables den Befehl Dateitabelle . . . aus. Bild 3.76 Neue FileTable erstellen Falls Sie nun einen grafischen Dialog erwartet haben, werden Sie hier enttäuscht. Was Sie erhalten, ist eine Vorlage für eine entsprechende SQL-Anweisung in einem neuen Abfrageeditor-Fenster. Dies ist aber durchaus in Ordnung, haben Sie hier ohnehin nur geringen Spielraum in der Anpassung der Anweisung. Lediglich die Namen für interne Constraints können Sie in dieser Anweisung ergänzen. In der vorgegebenen Basisanweisung ist die Angabe des Basisordners und der Sortierreihenfolge vorgesehen: FILETABLE_DIRECTORY: Der hier definierte Ordnername stellt den Basisordner der FileTable dar. In diesem können nun über die Freigabe auf Ebene des Betriebssystems Ordner erstellt und Dateien abgelegt werden. FILETABLE_COLLATE_FILENAME: Der Name für die Sortierreihenfolge, die für die Dateiund Ordnernamen innerhalb von SQL bei Zugriffen auf die FileTable verwendet werden soll. Geben Sie hier eine Sortierreihenfolge wie zum Beispiel Latin1_General_CI_AS an oder verweisen Sie mit database_default auf die diesbezügliche Standardeinstellung der Datenbank. Diese entspricht dem bei der Installation gewählten Serverstandard, wenn Sie bei der Erstellung der Datenbank nichts anderes angegeben haben. 3.6 Tabelle mit Filestream und FileTable 159 Bild 3.77 Anweisungsvorlage für eine FileTable Entfernen Sie aus der Anweisung in der Vorlage alle nicht benötigten Elemente. Auch die erste Anweisung, die prüfen soll, ob diese Tabelle schon besteht, und sie dann gegebenenfalls vor der Erstellung löscht, können Sie getrost löschen. Diese Anweisung beginnt mit . Optional können Sie die Namen für drei interne Constraints mit in der Anweisung angeben. Tun Sie dies nicht, werden diese automatisch vom System vergeben. : Legen Sie mit dieser Option den Namen für das Primärschlüssel-Constraint der Tabelle fest. Dieses wird auf die Spalte path_locator gesetzt. FILETABLE_STREAMID_UNIQUE_CONSTRAINT_NAME: Die Eindeutigkeit der Spalte stream_id wird über ein Unique Key-Constraint erzwungen. Der Name dieses Constraints wird über diese Option bestimmt. FILETABLE_FULLPATH_UNIQUE_CONSTRAINT_NAME: Ein weiteres Unique Key-Constraint erzwingt die Eindeutigkeit von Datei- und Ordnernamen. Dieser besteht zum einen aus dem parent_path_locator, der den Ordner, in dem die Datei gespeichert ist, angibt; des Weiteren aus dem Datei- oder Ordnernamen, der in der Spalte name gespeichert wird. Den Namen für dieses Constraint bestimmen Sie mit dieser Option. Die fertige Anweisung könnte nun folgendermaßen aussehen: CREATE TABLE dbo.meine_dokumente AS FileTable FILETABLE_DIRECTORY = 'db_docs', FILETABLE_COLLATE_FILENAME = database_default, FILETABLE_STREAMID_UNIQUE_CONSTRAINT_NAME = uk_meine_dokumente_streamid, FILETABLE_FULLPATH_UNIQUE_CONSTRAINT_NAME = uk_meine_dokumente_fullpath Auffallend ist, dass diese CREATE TABLE-Anweisung keine Spaltendefinitionen enthält. Dies liegt daran, dass die Struktur einer FileTable fix vorgegeben ist. Dass diese verwendet wird, legt der Zusatz AS FileTable hinter dem Tabellennamen in der Anweisung fest. Erstellen wir nun diese Tabelle, indem wir die Anweisung im Editor markieren und ausführen, taucht nun der im Statement angegebene Ordnername db_docs unter der bereits vorhandenen Freigabe auf: \\srvsql2014\dateiablage\dateidatenbank\db_docs 160 3 Eine neue Datenbank erstellen In diesem Ordner können nun erstmals zum Beispiel über den Explorer weitere Ordner erstellt und Dateien abgelegt werden. Ich habe dies getan, das Ergebnis oder einen Teil davon können Sie in Bild 3.78 sehen. Bild 3.78 In der Freigabe angelegte Ordnerstruktur Für den Zugriff auf den Ordner benötigen Sie also ausgehend vom Datenbankserver den Freigabenamen, der für eine Instanz definiert wird, gefolgt vom Share, der für jede Datenbank auf dieser Instanz, von der FILESTREAM genutzt wird, erstellt wird. Für jede FileTable innerhalb einer Datenbank wird ein eigenes Verzeichnis sichtbar. Unter dieser vierten Ebene kann nun die weitere Struktur frei definiert und genutzt werden. SQL Server 2014 FILESTREAMShare der Instanz Verzeichnis der Datenbank Verzeichnis der FileTable Benutzerverzeichnisse DB Server Beispiel: \\srvsql2014\dateiablage\dateidatenbank\db_docs\......... Bild 3.79 Basisstruktur für FileTable mit FILESTREAM Im Beispiel habe ich vorerst die drei Ordner dokumente, musik und videos erstellt. Im Ordner musik habe ich vier weitere Unterordner angelegt und in diese jeweils ein paar Musikdateien kopiert. Diese Ordner und Dateien erscheinen nun automatisch als Datensätze in 3.6 Tabelle mit Filestream und FileTable 161 der FileTable. Dies ist das Wesen dieser Tabellenart. Betrachten wir uns nun danach das Ergebnis, indem wir den kompletten Inhalt der Tabelle mit der Anweisung SELECT * FROM dbo.meine_dokumente; im Management Studio ausgeben. Bild 3.80 Befüllte FileTable In der Spalte name erkennen Sie im Ergebnis die Namen der Dateien und Verzeichnisse. Die genauen Inhalte der Tabelle finden Sie in Tabelle 3.5 aufgeschlüsselt. Tabelle 3.5 Struktur einer FileTable Spalte Beschreibung stream_id Diese stellt, wie schon vom „normalen“ FILESTREAM her bekannt, eine eindeutige ID für jede Datei oder jeden Ordner bereit. Sie ist auch hier als UNIQUEIDENTIFIER ausgeformt. file_stream Diese Spalte repräsentiert die Inhalte von Dateien. Diese sind binär hier in einer varbinary(max)-Spalte verfügbar. Für Ordner enthält diese Spalte daher keinen Wert. Auch hier ist der Zusammenhang zu klassischem FILESTREAM zu erkennen. name Der Name der Datei oder des Ordners. path_locator Diese Spalte vom Datentyp hierarchyid repräsentiert den Primärschlüssel in einer FileTable. Er setzt sich aus den Werten der übergeordneten Pfade und jenem der Datei zusammen und ist somit eindeutig. parent_path_locator Diese Spalte, ebenfalls vom Datentyp hierarchyid, stellt einen Verweis auf den übergeordneten Ordner dar. Er enthält den Eintrag aus der Spalte path_locator des Ordners, in dem sich die Datei oder das Verzeichnis befindet. Über einen Fremdschlüssel wird hier die Konsistenz gesichert file_type Enthält die Dateierweiterung bei Dateien, wie zum Beispiel mp3 oder m4a. cached_file_size Diese Spalte ist als bigint ausgeformt und zeigt bei Dateien die Größe in Bytes an. 162 3 Eine neue Datenbank erstellen Tabelle 3.5 Struktur einer FileTable (Fortsetzung) Spalte Beschreibung creation_time Diese Spalte zeigt an, wann die Datei erstellt beziehungsweise in den Ordner kopiert worden ist. Dies ist nicht jener Zeitpunkt, den Sie im Explorer als Änderungsdatum angezeigt bekommen. Diese Logik entspricht dem bekannten Verhalten, wenn Sie eine Datei in einen gewöhnlichen Ordner kopieren. Sie sehen denselben Zeitpunkt als Erstellt, wenn Sie sich im Explorer die Eigenscha en der Datei anzeigen lassen. Für diese Spalte wird der Datentyp datetimeoffset verwendet, der auch die Zeitzone mit speichert und anzeigt. last_write_time Dieser Wert entspricht jenem, der im Explorer als Änderungsdatum angezeigt wird. Sie ist ebenfalls mit dem Datentyp datetimeoffset definiert. last_access_time In den Eigenscha en einer Datei finden Sie den Eintrag Letzter Zugriff. In der FileTable finden Sie diesen Wert in dieser Spalte. Wie die beiden zuvor hat diese den Datentyp datetimeoffset. is_directory Diese bit-Spalte enthält für Ordner den Eintrag Wahr (1) und für Dateien den Wert Falsch (0). is_offline Wird dazu verwendet, um das Dateiattribut Offline innerhalb der FileTable anzuzeigen. is_hidden Dieser Wert entspricht dem Dateiattribut Versteckt. is_readonly Das Dateiattribut Schreibgeschützt wird in dieser Spalte abgebildet. is_archive Das Dateiattribut Archiv wird von dieser Spalte dargestellt. is_system Enthält den Wert 1 für Wahr, wenn eine Datei oder ein Ordner als Systemdatei beziehungsweise als Systemordner markiert ist. is_temporary Übernimmt das Dateiattribut Temporär. ACHTUNG! Sie sind besser mit der Anweisung SELECT * FROM ... bei einer FileTable vorsichtig, da in Form von Binärdaten die gesamte Datenmenge, die sich gerade im Verzeichnis befindet, abgerufen werden kann. Das können riesige Datenmengen sein, die Sie in diesem Moment bewegen und den Server sowie Datenleitungen belegen. Verzichten Sie vor allem auf die Spalte file_stream in Ihrem SELECT, denn diese Spalte enthält die Daten in binärer Form. Zugriff über SQL Mich fasziniert an FileTables, dass man die Ordner und Dateien nun bequem über SQLAnweisungen auswerten kann. In eingeschränkter Form können Sie sogar über SQL Veränderungen vornehmen. Auf die Dateien können Sie programmatisch wie generell auf Binärdaten in einer Datenbank zugreifen. Leider unterstützen dies die aktuellen ODBCTreiber noch nicht. Wir bekommen kein Ergebnis angezeigt, wenn wir die Tabelle wie im FILESTREAM-Beispiel in eine Access-Datei verknüpfen. Beschä igen wir uns nun mit ein paar Beispielen, wie Sie mit SQL auf diese Daten zugreifen können. HINWEIS: Wenn Sie mit SQL noch nicht vertraut sind, lesen Sie zuvor das nachfolgende Kapitel. Dort erkläre ich Ihnen die Grundlagen dazu. 3.6 Tabelle mit Filestream und FileTable 163 Verzeichnisse sind in einer FileTable daran zu erkennen, dass einerseits in der Spalte file_ stream der Wert NULL zu finden ist, andererseits daran, dass die Spalte is_directory den Wert 1 als Bit-Wert für Wahr enthält. Mit nachfolgender Anweisung können Sie alle Ordner anzeigen. SELECT name FROM dbo.meine_dokumente liefert: name -------------------------------------------------dokumente musik videos James Blunt Shakira Tim Bendzko Um Ordner gemeinsam mit ihren Unterordnern darzustellen, wählen Sie einen Self Join und verknüpfen die Tabelle über die Spalten path_locator und parent_path_locator. d.name AS unterordner FROM dbo.meine_dokumente d LEFT OUTER JOIN dbo.meine_dokumente p ON d.parent_path_locator = p.path_locator liefert: ordner --------------------------root root root unterordner -------------------------------dokumente musik videos musik musik musik James Blunt Shakira Tim Bendzko Über spezielle Funktionen lässt sich der Pfad zu einer Datei in lesbarer Form ausgeben. Die vorhandene Form als hierarchyid ist für uns ja nicht gut lesbar. FileTableRootpath : Diese Funktion liefert den Basispfad, der sich aus der Freigabe und den Verzeichnissen von Datenbank und FileTable ergibt. GetFileNamespacePath : Mit dieser Funktion kann der relative Pfad einer Datei unterhalb der von der FileTable festgelegten Ordnerstruktur ausgelesen werden. Kombinieren Sie beide, können Sie den gesamten Pfad von Dateien eruieren. FROM dbo.meine_dokumente liefert: 164 3 Eine neue Datenbank erstellen Datei ------------------------------------------------------------------------- Lieder.mp3 PRAXISTIPP: Da Funktionen für jede Zeile separat ausgeführt werden, muss der Basispfad für jeden Eintrag mit der Funktion FileTableRootpath() mehrfach eruiert werden, obwohl er für jede Zeile denselben Wert ergeben wird. Um diesen Vorgang effizienter zu gestalten, können Sie diesen konstanten Wert zuvor in einer Transact-SQL-Variablen speichern und verwenden. Eine ausführliche Einführung in Transact-SQL bietet Ihnen Kapitel 5. Folgende drei Anweisungen liefern dasselbe Ergebnis wie die Anweisung zuvor, reduzieren allerdings den Aufwand für das System, da der Basispfad nur ein einziges Mal eruiert werden muss. FROM dbo.meine_dokumente In einem eingeschränkten Umfang können über SQL auch Schreibzugriffe auf eine FileTable erfolgen. Das nachfolgende Beispiel zeigt, wie Sie einen Ordner umbenennen. Mit einer UPDATE-Anweisung ändern wir den Namen des Ordners videos zu fotos. Ich gehe dabei davon aus, dass es in der gesamten Verzeichnisstruktur nur einen Ordner mit diesem Namen gibt und daher die Einschränkung name = 'video' ausreichend ist. UPDATE dbo.meine_dokumente SET name = 'fotos' AND is_directory = 1; An dieser Stelle ist für mich eine deutliche Verbesserung gegenüber dem SQL Server 2012 zu erkennen. Hier hat es immer wieder etwas gedauert, bis diese Änderung in der FileTable auch im Filesystem direkt erkennbar gewesen ist. Insbesondere, wenn der betroffene Ordner im Moment im Zugriff gewesen ist, hat sich die Änderung erst etwas später gezeigt. Beim SQL Server 2014 fällt diese Verzögerung in meinen Tests weg. Im Gegenteil, habe ich den Ordner beim Umbenennen gerade geöffnet, „verschwinden“ die Dateien unmittelbar und der Ordner ist leer. Ich muss dann zum neuen Ordner navigieren, um die Dateien wieder zu sehen. 3.6 Tabelle mit Filestream und FileTable 165 Löschen Sie einen Datensatz aus der FileTable, verschwindet die betroffene Datei oder der betroffene Ordner ebenso aus dem Filesystem. Die nachfolgende Anweisung löscht eine Datei: DELETE FROM dbo.meine_dokumente Ein Ordner kann auf diese Art und Weise nur dann gelöscht werden, wenn er bereits leer ist. Ist dies nicht der Fall, bekommen Sie eine Fehlermeldung, die auf eine FremdschlüsselVerletzung hinweist. Wir erinnern uns, die Spalte path_locator verweist mit einem Fremdschlüssel auf die Spalte parent_path_locator. Folgende Anweisung wird in unserem Beispiel daher nicht erfolgreich ausgeführt. DELETE FROM dbo.meine_dokumente AND is_directory = 1; Die Fehlermeldung lautet: SAME TABLE REFERENCE-Einschränkung Tabelle 'dbo.meine_dokumente', column 'parent_path_locator' auf. HINWEIS: Wie wir gesehen haben, können wir beim Erstellen der FileTable für bestimmte Constraints selber einen Namen vergeben. Leider ist dies nicht durchgängig für alle Constraints möglich. Daher sehen wir für diesen Fremdschlüssel einen systemseitig vergebenen Namen. Dies stört nicht weiter, außer dass er nicht so „schön“ ist wie selbst vergebene. Auch das Umbenennen über das Kontextmenü im Objekt-Explorer ist in diesem Fall nicht möglich. Bild 3.81 Schlüssel der FileTable Nach dem Ändern und Löschen betrachten wir uns abschließend noch das Einfügen von neuen Datensätzen. Mit der nachfolgenden Anweisung erstellen wir ein weiteres Verzeichnis im Basisordner. 166 3 Eine neue Datenbank erstellen Nun kopieren wir eine bestehende Datei ebenfalls in den Basisordner. Dazu müssen wir den Inhalt der Spalte file_stream kopieren. Natürlich können wir dabei auch einen anderen Dateinamen vergeben. FROM dbo.meine_dokumente Als Ergebnis sehen Sie sowohl den neuen Ordner als auch die neue Datei im Basisordner. HINWEIS: Um Objekte in einem Unterordner zu erstellen, muss man sich ein wenig mit der Verwendung und Erstellung von Werten für den Datentyp hierarchyid befassen. Informationen dazu finden Sie auch in Kapitel 5. Im , um einen neuen Besonderen benötigen wir die Methode untergeordneten Wert in einer Hierarchie zu bekommen. Für die nachfolgenden Beispiele verwende ich Variablen, über die Sie auch in Kapitel 5 detailliert informiert werden. Bild 3.82 Objekte über INSERT mit SQL erstellt Ich erstelle nun einen Unterordner für den Ordner musik. Dazu muss ein passender Hierarchiewert als Nachfolger für den Hierarchiewert dieses Ordners generiert werden. Dazu lese ich zuerst den Wert der Spalte path_locator für den Ordner musik aus und speichere ihn in ab. Danach benutze ich die Methode , um für der Variablen diesen Hierarchiewert einen Nachfolger zu generieren, und weise diesem der Variablen unterordner zu. Beide Variablen müssen den passenden Datentyp hierarchyid verwenden. Den neu generierten Hierarchiewert kann ich nun beim Einfügen in die Tabelle für die Spalte path_locator verwenden. Zur Erinnerung: Im vorhergehenden Beispiel haben wir diese Spalte nicht befüllt und das neue Verzeichnis daher im Basisordner angelegt. 3.6 Tabelle mit Filestream und FileTable 167 DECLARE @unterordner hierarchyid = @ordner.GetDescendant(NULL, NULL) INSERT INTO dbo.meine_dokumente (name, is_directory, path_locator @unterordner Mit dem weiteren folgenden Beispiel kopiere ich nun eine Datei in diesen Unterordner. Wieder muss ich einen Nachfolgewert für die Hierarchie des Zielordners erstellen und diesen dann als path_locator eintragen. DECLARE @datei hierarchyid = @ordner.GetDescendant(NULL, NULL) FROM dbo.meine_dokumente path_locator @datei Sie sehen an diesen Beispielen, dass Sie beim Erstellen eines Hierarchie-Nachfolgewertes keinen Unterschied machen müssen, ob Sie diesen für einen Ordner oder eine Datei benötigen. Allerdings müssen wir noch eine Kleinigkeit zusätzlich beachten. Die Methode besitzt die beiden Parameter child1 und child2, um die Position in der Hierarchie genauer zu bestimmen. Bisher haben wir für beide Parameter den Wert NULL übergeben. Dies bedeutet, dass quasi der erste Nachfolger in der Hierarchie gebildet wird. Dies wird zum Problem, wenn Sie auf dieselbe Art und Weise nun einen weiteren Ordner in demselben anlegen oder eine weitere Datei einfügen möchten. Denn dann können Sie nicht ist mehr denselben Nachfolgewert erneut verwenden. Denn die Methode deterministisch, was bedeutet, dass sie mit denselben Parametern aufgerufen immer dasselbe Ergebnis liefert. Würde ich also nun, nachdem ich die Datei zu „Am seidenen Faden“ bereits in den Ordner deutsch kopiert habe, nun auch „Lieder“ in denselben Ordner verschieben, eine Primärschlüsselverletzung auslösen, da die eruierte Hierarchiestufe einsetzen, um bereits vergeben ist. Daher müssen wir die Parameter von dies zu vermeiden. Der Parameter child1 bedeutet, dass ein Wert „nach“ diesem, child2 bedeutet ein Wert „vor“ diesem generiert werden soll. Wir lesen also immer den größten Wert der betroffenen Hierarchiestufe aus und übergeben ihn als Parameter child1, damit kann dann nichts mehr schiefgehen. Im nächsten Beispiel verschieben wir eine Datei, anstelle sie zu kopieren. Dazu verwenden wir ein gewöhnliches UPDATE, um die neu eruierte Hierarchiestufe der Spalte path_locator zuzuweisen. Nebenbei können wir auch gleich den Dateinamen anpassen. In der nun lesen wir den größten Hierarchiewert aus, zusätzlich verwendeten Variablen der dem Zielordner zugewiesen ist. Diesen geben wir nun als Parameter child1 der Methode den Wert NULL mit. Gibt es noch gar keine Inhalte im Ordner, erhält die Variable und wir würden damit auch das gewünschte Ergebnis erzielen. DECLARE @vorhanden hierarchyid = (SELECT MAX(path_locator) FROM dbo.meine_dokumente WHERE parent_path_locator = @ordner) 168 3 Eine neue Datenbank erstellen (@vorhanden UPDATE dbo.meine_dokumente name = 'lieder.mp3' Mit dieser Variante können wir nun weitere Dateien in den Ordner deutsch verschieben, ohne auf einen Fehler aufzulaufen. HINWEIS: Wenn Sie sich mit dem Nachvollziehen der letzten Beispiele noch etwas schwergetan haben, dann empfehle ich Ihnen, die Kapitel 4 und 5 zu lesen, hier bekommen Sie die Grundlagen zu SQL und Transact-SQL. PRAXISTIPP: Wenn Sie mittels UPDATE zum Beispiel einen Ordner verschieben möchten, der andere Dateien enthält, bekommen Sie eine Fremdschlüsselverletzung. Sie können so etwas bewerkstelligen, indem Sie die Dateien einzeln verschieben, um so sauber jeweils den passenden Hierarchiewert zu ermitteln. Um das zu automatisieren, benötigen Sie zum Beispiel eine gespeicherte Prozedur mit einem Cursor, um die Dateien in einer Schleife zu bearbeiten. Dazu lesen Sie die Kapitel 5 und 6, um die dazu notwendigen Vorgangsweisen zu erlernen. Im letzten Beispiel kopiere ich ein Video aus der früher in diesem Kapitel erstellten Datenbank video in die FileTable. Da es sich um eine andere Datenbank handelt, geben wir vor dem Schemanamen der Tabelle medien noch den Namen der anderen Datenbank an. Da in der Ursprungstabelle zwar das Video, aber kein Filename gespeichert ist, vergebe ich diesen beim Anfügen. FROM video.dbo.medien Auch dieses Video erscheint letztendlich als Datei im Ordner und der Zugriff darauf über das Filesystem ist möglich. Um diese Datei in einen bestimmten Ordner einzufügen, gehen Sie wie in den vorangegangenen Beispielen vor. Was zum Thema FileTables noch wichtig ist . . . Prinzipiell unterstützen FileTables alle Funktionen eines normalen Dateisystems. Allerdings ist die Verzeichnistiefe eingeschränkt. Sie können Unterverzeichnisse nur in einer maximalen Tiefe von 15 Ebenen erstellen. Wenn Sie diese maximale Anzahl ausnützen, können Sie aber keine Dateien mehr auf der untersten Ebene speichern, da diese eine weitere Ebene in der für die Darstellung in der Spalte path_locator verwendeten hierarchyid bedeuten würden. Informationen über FileTables finden Sie in einer eigenen Systemtabelle filetables im Schema sys. 3.7 Beispieldatenbank generieren 169 Hier finden Sie Informationen darüber, ob der Zugriff aktiviert ist, wie der Verzeichnisname der Tabelle lautet sowie welche Sortierreihenfolge für die Namen verwendet wird. Bild 3.83 Informationen aus sys.filetables Wenn wir mit dem Explorer über das Dateisystem mit dem Share auf FileTables zugreifen, sehen wir eine vom SQL Server übersetzte Darstellung. Intern gleicht die Speicherung jener von Filestream. Auf der Datenplatte D haben wir für die verwendete Datenbank den Ordner DB_FS\dateien_fs gewählt. Darin hat der SQL Server beim Erstellen der FileTable einen weiteren Ordner für diese in der Syntax eines Unique Identifiers angelegt. Für jede FileTable innerhalb derselben Datenbank wird ein eigener Ordner auf dieser Ebene erstellt. Darunter verwaltet der SQL Server die Daten in der Art von FILESTREAM. Die einzelnen Dateien liegen in einer für uns nicht lesbaren Form vor, wie in Bild 3.84 zu sehen. Bild 3.84 Realsicht auf Inhalt einer FileTable ■ 3.7 Beispieldatenbank generieren Nachdem wir in diesem Kapitel jede Menge über das Erstellen von Datenbanken und Tabellen gehört haben, wird es Zeit, nun die Beispieldatenbank, die wir in den folgenden Kapiteln des Buches benötigen werden, zu generieren. Im ersten Kapitel habe ich Ihnen gezeigt, wie Sie die Beispieldatenbank wawi durch Anfügen der Datenbankdateien, die Sie heruntergeladen haben, verfügbar machen können. Nun möchte ich Ihnen zeigen, wie Sie dies anhand eines SQL-Skripts erledigen können.