Glorf.it

Glorf IT

Bedenkliches aus dem IT-Alltag

15. August 2007 um 12:24

SQL-Server: Inhalt einer Datei einlesen

Kürzlich fragte mich ein Kollege, wie man denn am SQL Server den Inhalt einer Datei in ein Feld einlesen kann. Zufällig gibt es dazu am SQl-Server-2005 ein nettes neues Feature, dass man dazu missbrauchen äh gebrauchen kann…

Im folgenden Beispiel soll ein ASCII-File (nicht Unicode!) eingelesen werden, deswegen muss der Inhalt von Typ VARBINARY noch in den Typ VARCHAR konvertiert werden. Das gilt auch, wenn man das letztlich in ein Unicodefeld speichern will.

CREATE TABLE #bla (
id integer identity(1,1) NOT NULL primary key,
dateiinhalt varchar(max) not null)

INSERT INTO #bla(dateiinhalt)
SELECT CAST(bin as varchar(max)) FROM
OPENROWSET(BULK N'c:\test\ascii-file.txt', SINGLE_BLOB) AS a(bin)

select * from #bla

Risiken und Nebenwirkungen:

  • Um das einsetzen zu dürfen muss man entweder Admin sein oder die Verwendung von Openrowset auch für andere erlauben.
  • Die Datei muss für den SQL-Server erreichbar sein. Deswegen wird der Pfad aus der Sicht des SQL-Servers angegeben. Das bedeutet im einfachsten Fall, dass sie auf einer Festplatte des Servers liegt.
  • Falls der Server-Dienst in einem Benutzerkontext läuft, der auch Netzrechte hat, dann darf die Datei auch auf einer Freigabe liegen auf die dieser Benutzer Zugriffsrechte hat.
13. August 2007 um 12:38

SQL-Server undokumentiert: DBCC PAGE

Als ich die Systemdatenbank mssqlsystemresource neulich mal untersucht, versucht ich mein Glück zunächst mit einem nur halb dokumentierten DBCC-Befehl: ersteht nicht in den Books-Online, aber in der Knowledgebase steht dann doch eine Anleitung.

Der Aufruf ist recht schlicht und kann aus einer beliebigen Datenbank erfolgen:

DBCC PAGE( {dbid|dbname}, filenum, pagenum [, printopt] [, cache] [,logical] )

Damit der Output sichtbar ist, muss man dem SQL-Server vorher noch sagen, dass er an den Client geschickt werden soll und nicht etwa ins Errorlog:

DBCC TRACEON (3604)
DBCC PAGE (32767, 1, 1, 1)

Parameter Beschreibung
dbid ID der Datenbank
dbname Datenbankname
filenum Dateinummer (1 ist immer die MDF, 2 meist die LDF, usw.)
pagenum Seitennummer in der Datei
printopt optional:
0: Default; Buffer- und Page-Header
1: Buffer- und Page-Header; jeden Datensatz einzeln und die Satz-Offset-Tabelle
2: wie 1, aber alle Datensätze zusammen
3: wie 1, aber die Daten im Klartext
cache optional:
0: lädt die Seite "frisch" aus der Datenbank-Datei
1: zeigt die Seite aus dem Cache (Default)

Ich habe das zum Glück noch nicht oft benötigt, aber irgendwie finde ich es echt cool mir die Datenseiten so richtig ansehen zu können…

Siehe auch

12. August 2007 um 12:20

beliebte Performance-Fallen

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

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

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

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

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

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

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

11. August 2007 um 12:09

Fremdschlüssel-Falle

Wenn man sich mit Datenbanken beschäftigt, dann hat man immer wieder mit Fremdschlüssel zu tun. Ich bin ein großer Fan von Fremdschlüsseln: Wenn sie sprechend benannt wurden, dann kann man mit ihnen fremde Datenbanken gleich besser verstehen. Besonders weil ich dann gerne Datenbank-Diagramme mit dem Management Studio erstellen lasse… 😉

Der eigentliche Zweck ist aber natürlich praktischer: Es werden nur solche Werte zugelassen, die in der primäre Tabelle auch tatsächlich existieren, z.B. ein Mitarbeiter kann nur in einer Abteilung sein, die es auch wirklich gibt. Neben den Problemen mit historischen Daten, die sehr sauber modelliert werden müssen, gibt es ein Feature, das weitgehend unbekannt ist:

Wenn man NULL-Werte zulässt, dann ist NULL in den Fremdschlüsselwerten grundsätzlich erlaubt. Das ist bei einsegmentigen Schlüssel total einsichtig, bei zusammengesetzen führt es in der Regel zu Verblüffung. Ich mache mal ein Beispiel:

Ich habe eine Tabelle "primtab" mit dem Schlüssel bestehend aus pk1 und pk2. Und die Tabelle "reftab", die auf die Tabelle "primtab" verweist. Mit beiliegenden Code kann man ein Beispiel mit Daten anlegen.

create table primtab (
pk1 integer not null,
pk2 integer not null,
primary key(pk1, pk2),
misc integer
– …
)

create table reftab (
pk integer not null primary key,
fk1 integer null,
fk2 integer null,
foreign key (fk1, fk2) references primtab(pk1, pk2),
misc integer
– …
)

insert into primtab (pk1, pk2, misc) values (1,1,1)
insert into primtab (pk1, pk2, misc) values (1,2,1)
insert into primtab (pk1, pk2, misc) values (1,3,1)
insert into primtab (pk1, pk2, misc) values (2,1,1)
insert into primtab (pk1, pk2, misc) values (2,2,1)

insert into reftab (pk, fk1, fk2, misc) values (1,1,1,1)
insert into reftab (pk, fk1, fk2, misc) values (2,1,2,1)
insert into reftab (pk, fk1, fk2, misc) values (3,1,3,1)
insert into reftab (pk, fk1, fk2, misc) values (4,1,1,1)
insert into reftab (pk, fk1, fk2, misc) values (5,2,1,1)
insert into reftab (pk, fk1, fk2, misc) values (6,2,2,1)
insert into reftab (pk, fk1, fk2, misc) values (7,1,1,1)
insert into reftab (pk, fk1, fk2, misc) values (8,2,1,1)

Da für die Fremdschlüsselfelder fk1 und fk2 NULL erlaubt ist, kann man den Wert auch für Beide setzen.

Update reftab
set fk1=NULL,
fk2=NULL
where pk=1

Man kann aber auch nur einen der Werte auf NULL setzen:

Update reftab
set fk2=NULL
where fk2=1

oder

Update reftab
set fk1=NULL
where fk1=1

Für mich war das damals völlig unerwartet und erst nach endlosen Diskussionen in Newsgroups konnte ich mich damit abfinden…

7. August 2007 um 12:23

Craig Freedman's WebLog

Wer sich für die Themen SQL-Ausführung und Zugriffspläne des Microsoft SQL-Servers-2005 interessiert, der wird auf dem Weblog von Craig Freedman fündig.

Er postet vergleichsweise wenig, aber ich finde die Artikel sehr gut. Sie sind fundiert und bleiben nicht an der Oberfläche ohne dabei zu lang zu werden. Seine Artikel über die Isolation-Level sind beispielsweise großartig, anbei ein paar Beispiele:

Aber Achtung: das ist keine leichte Entspannungslektüre… 😉

6. August 2007 um 12:18

Alles über DATETIME am SQL-Server

Was man immer schon mal über DATETIME am SQL-Server wissen wollte, aber nicht den leisesten Schimmer hatte, dass es wissenswert ist, hat Tibor Karaszi sehr schön beschrieben. Auf seiner Homepage findet sich wirklich alles, was man wissen sollte. Ideal für Einsteiger oder angehende Profis.

Gugst Du hier: "Tibor Karaszi's SQL Server pages"

Darauf kam ich durch eine sehr interessante Diskussion in der Newsgroup "microsoft.public.sqlserver.programming" (?) zwischen Joe Celko und Tibor Karaszi… 😉

5. August 2007 um 20:55

Infosammlung zu den Integration Services

Auf dem letzten bzw. ersten Regionalgruppentreffen der PASS-Franken empfahl schilderte jemand (war es Klaus Oberdalhoff?) Wilfried Färber als den absoluten Fachmann für die "SQL Server Integration Services" (SSIS). Beim erneuten Lesen des PASS-Newsletters vom Juli fiel mir die Adresse seiner Webseite ins Auge: www.SQLIS.de

Die Artikel auf seiner Webseite sind absolut beeindruckend. Wenn ich dann endlich dazu komme mit den SSIS rum zu spielen, dann kenne ich jetzt eine gute Anlaufstelle…

1. August 2007 um 21:43

Video zum Vorgehen bei defekten Datenbanken

Wer mal Mr. Paul Randal auf Video erleben will, der kann seinen extrem kurzweiligen Vortrag "Secrets of Fast Detection and Recovery from Database Corruptions" ansehen. Es lohnt sich, es ist geradezu ein MUSS für jeden DBA! Über eine Stunde voll mit wichtigen Tipps handlich verpackt.

Aber es gibt ein paar Schattenseiten:

  • Er redet sehr schnell, aber deutlich.
  • Man muss sich allerdings bei MS anmelden. Der alte Passport-Account tat's bei mir…
  • Ich musste es mit dem IE ansehen, Firefox klappte nicht.

Mir war übrigens nicht klar, dass Page-Checksums nicht für die TempDB berechnet werden.

31. Juli 2007 um 18:20

SQL-Server-2005: neue Windows-Gruppe

Heute fragte mich ein Kollege, was es denn mit der Gruppe "SQLServer2005MSSQLUser$$" auf sich hat. Ich war ganz schön von den Socken, denn mir war bislang noch gar nicht aufgefallen, dass der SQL-Server bei der Installation eine Gruppe anlegt.

Wenn man den SQL-Server im Benutzerkontext starten will, dann muss man den Benutzer einfach nur zum Mitglied der Gruppe machen und er hat automatisch alle Rechte die erbraucht.

Im beiliegenden Artikel steht auch eine Liste der Rechte: "Setting Up Windows Service Accounts"

28. Juli 2007 um 11:41

Visual-Studio: Datenbank-Referenzen

Ab dem jetzt erschienen Service-Release 1 für die "Visual Studio Team Edition for Database Professionals" kann man Datenbank-Referenzen verwenden. Damit muss man nicht alle Tabellen/Views, auf die man zugreifen will, in das eigene Projekt aufnehmen. Das will ich mir unbedingt noch vor meinem Urlaub ansehen.

Daher als kleiner Merker für mich und andere ein Link auf eine erstklassige Anleitung zur Verwendung von Datenbank-Referenzen von Gerd Drapers: "Data Dude : Database References"

24. Juli 2007 um 23:57

Service Release 1 für DB-Pro

Das erste Service-Release für die "Visual Studio 2005 Team Edition for Database Professionals" ist gestern erschienen.

Will man es installieren, dann benötigt man das SP1 für Visual Studio 2005. Neben einigen Fehlerbereinigungen und neuen Fehlern, die freundlicherweise schon im KB-Artikel beschrieben sind, bringt es auch drei neue Features:

  • Cross-database references – Mal sehen, ob wir das auch dazu benutzen können, Objekte in der gleichen Datenbank zu referenzieren, die aber nicht im Projekt sind…
  • Improved file support within SQL Server file groups – schön, brauche ich aber nicht…
  • Variables – hier geht es um Variablen, die dann beim Einspielen gefüllt werden.

Vielen Dank an Spencer Tracy für den Hinweis!

23. Juli 2007 um 17:00

Decrypt SQL Server Objects

Omri Bahat beschreibt im Artikel "Decrypt SQL Server Objects" (im "SQL Server Magazine", Ausgabe August 2007) wie man mit vergleichsweise wenig Aufwand den Quellcode den ENCRYPT angelegten Quellcode von Prozeduren/Funktionen/Views sichtbar machen kann. Er verwendet dazu zwei Tricks: erstens eine "Dedicated Admin Connection" (DAC, einfach beim Connect vor den Servernamen "ADMIN:" schreiben) und zweitens benutzt er ALTER, um das Objekt zu verändern, um den XOR-Schlüssel herauszubekommen (natürlich in einer Transaktion, um das Original nicht zu zerstören.

Den Artikel kann man nur als angemeldeter Leser des Magazins sehen, aber die Quelltexte der Beispiele sind freundlicherweise frei zugänglich.

Damit kann man den verschlüsselten Quelltext aus der Spalte "imageval" der Tabelle "sys.sysobjvalues" (am 2005er, siehe Datei WebListing_01.txt) lesen und dann mittels ALTER-Statement den Hex-Wert (für das XOR) ermitteln (siehe Datei Listing_03.txt). Mit den ermittelten Infos kann man den Quellcode dann entschlüsseln (siehe Listing_04.txt). Das geht auch am SQL-Server-2000, nur stehen die Infos da woanders, aber das steht auch alles in seinen Beispieldateien.

Insgesamt ist der Artikel mal erfrischend anders als die sonst eher belehrenden Artikel des Magazins. Ich weiß noch nicht, ob ich das mal benötigen werde, aber immer hin ist es gut zu wissen, dass man die Encryption des Quelltextes so leicht "knacken" kann. Langfristig wollen wir den Quelltext unserer Objekte nämlich encrypted anlegen…

Disclaimer: Hier ging es jetzt um die Datenverschlüsselung. Die ist nach wie vor richtig schön sicher…