MySQL: InnoDB jetzt mit Volltextsuche und Standard-Storage-Engine

Die Entscheidung war anfangs nicht nachvollziehbar. Mit Version 5.5 der MySQL-Datenbank haben die Entwickler die transaktionsfähige InnoDB als Standard-Storage-Engine festgelegt. MyISAM, bisher der Standard, blieb auf der Strecke. Ein Aufschrei bei vielen Datenbank-Administratoren verstummte heute, als bekannt wurde, welche Neuerungen die Engine InnoDB erfahren wird.

Was sind überhaupt Storage-Engines in MySQL?
Eine Engine ist der Motor oder das Herz der Datenbank. Im Gegensatz zu vielen anderen Datenbanken hat MySQL die Engine vom eigentlichen Server-Prozess entkoppelt. So kann der MySQL-Datenbankbenutzer unter einer Vielzahl von Engines wählen, die alle Vor- und Nachteile bieten. Eine oft gesuchte „Eierlegende-Wollmilch-Sau“ gibt es leider nicht. Oder gab es bisher nicht, um genauer zu sein.

Jede Datenbank, oft auch Schema genannt, ist im Dateisystem durch einen Unterordner festgelegt. In diesem Ordner werden die Daten abgelegt. In jedem Ordner ist mindestens eine .frm-Datei abgelegt, die den Namen Meine-Datenbank.frm trägt. In dieser Tabelle speichert MySQL seine Tabellendefinitionen. In dieser Tabellendefinition ist auch ein Verweis auf die sogenannte Storage-Engine im Feld „Engine“.

Vorteile der MyISAM-Engine
Der grundlegende Vorteil von MyISAM ist die integrierte Volltextindizierung. So ist es möglich, die ersten 500 Zeichen von BLOB- und TEXT-Spalten zu indizieren. Nahezu jede Suche über eine Webseite erfolgt durch die Funktion dieser Engine.

Nachteile der MyISAM-Engine
Das Locking von MyISAM bereitet vielen Adminstratoren starke Kopfschmerzen. Die Engine sperrt beim Schreibzugriff ganze Tabellen, keine Zeilen. Diese „exklusiven Locks“ bremsen je nach Datenbank oft die Performance.

Die Vorteile der InnoDB-Engine liegen hauptsächlich in ihrer Geschwindigkeit. Abfragen über einen Primärschlüssel gehen wesentlich schneller von der Hand, als bei einer MyISAM-Engine. Weitere Vorteile sind ein vorausschauendes Read-Ahead, also ein „Vorwärts-Lesen, welches durch das Vorabladen von Daten einen weiteren Datenbankzugriff immens beschleunigt sowie diverse Hashs und Eingabepuffer, die die Datenbank beschleunigen.

Auf alle diese Vorteile musste der Datenbankdesigner verzichten, wenn er einen Volltextindex nutzen wolle. Es gab Tricks und Kniffe, wie man eine Datenbank in einer InnoDB vorhielt, eine Volltextindexierung dann in eine separate MyISAM-Datenbank auslagerte. Doch diese Möglichkeit war für die meisten Anwendungen überdimensioniert.
Dank des Sphinx-Plugin konnte man relativ einfach über jede MySQL-Engine einen Volltextsuche durchführen. Sphinx agiert als eine Art Proxy, der über spezielle SELECT-Statements via JOIN angesprochen wird.

Aus gutem Grund war MyISAM so die Standard-Engine von MySQL. Die Engine bietet nach wie vor einen sehr guten Kompromiss aus Leistung und Funktionen. Gerade die Möglichkeit der Volltextindexierung war eben bisher nur dieser Engine vorbehalten. Doch mit dem jetzt von Oracle veröffentlichen Preview von MySQL 5.6 ist der letzte Grund, MyISAM den Vorzug zu geben, gewichen: Das Entwicklerteam rüstet die Volltextsuche bei InnoDB nach.
So dürften viele Datenbank-Administratoren die erste stabile Version der von MySQL 5.6 entgegenfiebern. Denn auf einmal gilt es nicht mehr die eigene Datenbank mühsam zu analysieren und Vor- und Nachteile abzuwägen. Dieser Schritt dürfte von da an nur noch wirklich großen Datenbankprojekten vorenthalten sein. Nach Version 5.6 werden die meisten Nutzer von MySQL sich einfach und ohne Nachdenken der Standard-Engine InnoDB bedienen können.

Cron job – Mail abschalten

Je nach Einstellung sendet jeder Cronjob nach der Ausführung eine Mail an den Besitzer des Cronjob mit der Ausgabe des Bildschirminhaltes. Dies kann ein gewünschtes Verhalten sein, oder auch als sehr lästig angesehen werden. Gerade wenn ein Job vielleicht jede Minute ausgeführt wird, muss diese Mail unterdrückt werden.

Wer zum Beispiel seine Serververwaltung mit Plesk erledigt, wird feststellen, dass je nach Version und Installation diese Mail gesendet wird – oder nicht. Etliche Plesk-Installationen unter Plesk 9.x, die ich betreue, senden keine Mail, eine neue Installation unter Plesk 10.x mutiert munter zur „Spamschleuder“.

Cron Job: Don´t send emails of Cronjob
Die einfachste Möglichkeit, die Mail abzuschalten, ist sie einfach zu unterdrücken. Dies funktioniert auch unter Plesk (Plesk Control pannel), da diese Definition einfach bei der Anlage des Cronjobs getroffen wird. Es ist kein weiterer Eingriff in die Serverkonfiguration erforderlich. Das Geheimnis liegt im „unterdrücken“ der Mail mittels „> /dev/null 2>&1“
Hängen Sie diese Zeichenfolge einfach an jede Befehlzeile an. Ein Job lautet dann beispielsweise wie folgt:

30 * * * * /root/mein-ordner/mein-skript.sh > /dev/null 2>&1

Für Plesk-Nutzer: In der Befehlzeile den Aufruf (hier: „/root/mein-ordner/mein-skript.sh“ um die Zeichenkette „ > /dev/null 2>&1“ erweitern.

Cron Job: Mail generell abschalten
Die oben beschriebene Methode bedingt, das Ziel „dev/null“ an jede Cronjob-Zeile manuell anzuhängen. Dies funktioniert, doch kann bei vielen Cronjobs aufwändig sein. Auch werden viele Unix-Freaks dies als „unsauber“ bezeichnen.
Um das Mail generell abzuschalten, empfiehlt es sich, die „MAILTO“-Variable zu bearbeiten. Diese Variable wird in der Crontab definiert. Um dies zu ändern oder zu überprüfen, öffnet man die richtige Crontab (auf den (Login)User achten!) über die Shell mit

# vi /etc/crontab
   oder
$ crontab -e

(Hinweis:  „-e“ bedeutet Edit=Ändern, „-l“ kann mit „lesen“ bezeichnet werden)

In der ersten Zeile sollte in der enstprechenden Crontab folgende Zeile vorhanden sein:

MAILTO=meinuser@domain.tld

Um die Mail generell abzuschalten, muss man einfach die Email-Adresse durch „“ ersetzen:

MAILTO=""

Mit dem Speichern der Crontab (Je nach Editor: ESC, „:qw1“) sendet kein Cronjob dieser Crontab mehr eine Mail.

Cronjob-Logfile: Mail in ein Logfile umleiten
Wer auf die Daten der Mail nicht verzichten, aber von Email verschont bleiben möchte, der kann die Ausgabe in ein Log umleiten. Dies kann für die Nachverfolgung und Fehlersuche sehr wichtig sein. Und so leitet man die Cronjob Email in ein Logfile um:

30 * * * * /root/mein-ordner/mein-skript.sh > /home/mein-user/cronlogs/logfile-name.log

Mysql PHP Index-Optimierung: Index-Werte optimieren mittels CRC32-MD5-Hash

Im Artikel „Mysql PHP Index-Optimierung: Lange Index-Werte kürzen“ haben die Möglichkeit aufgezeigt, lange Datenbank-Varchar-Felder mittels MD5-Hash merklich zu verkürzen, um Plattenplatz zu sparen und die Performance deutlich zu erhöhen. Doch ganz so optimal ist der MD5-Hash nicht. Hier untersuchen wir nun eine andere Lösung:

Eigener Hash in MySQL aufbauen
Die Storage-Engines in MySQL bieten leider meistens keine Möglichkeit, Indizes per Hash aufzubauen (aktuell nur die Memory-Engine). Also müssen wir uns selbst darum kümmern. Vergleichbar ist das folgende Vorgehen mit der Speicherung von IP-Adressen (siehe „MySQL: IP-Adressen optimal speichern„), die ebenfalls nicht per varchar(15), sondern per Integer (nur vier Bytes!) erfolgen sollte.

MySQL bietet die mathematische Funktion „CRC32“. Diese MySQL-Funktion liefert uns mittels Umrechnung einen Integer zurück. Diese Umrechnung ist auf jedem Fall der oben beschrieben MD5-Lösung vorzuziehen, da der Speicherplatz deutlich kleiner und die Performance deutlich erhöht wird.

Feldanlage in MySQL + Index
url_crc int unsigned NOT NULL DEFAULT 0

Zugriff
SELECT * FROM meine_tabelle WHERE url_crc=CRC32(“http://sirmark.de”);

Mit der CRC-Methode erschlagen wir den Nachteil 1 (Zeichenkette immer 32-Zeichen lang) der MD5-Methode, minimieren den Speicherplatz und erhöhen die Performance noch einmal deutlich. Doch der Nachteil 2 bleibt: Auch diese Methode eignet sich nicht zur Rückrechnung des Feldinhaltes und kann bei völlig unterschiedlichen URLs (Strings) gleiche Werte zurückliefern.

Hash-Kollision ausschließen
Die Problematik, dass unterschiedliche String den gleichen Hash zurückliefern, kann je nach Anwendung zu einem Problem werden. Gerade mit der CRC32-Methode ist die Wahrscheinlichkeit von gleichen Hashs (Hash-Kollision) ein Vielfaches größer, als bei der MD5-Methode. Gehen Sie davon aus, dass Sie bei 100.000 Datensätzen mit mindestens einer Kollision rechnen müssen. Wenn Sie wie in unserem Beispiel eine URL per CRC32 optimieren und zusätzlich die URL im Klartext hinterlegen, benötigen Sie bei der Anfrage beide Werte.
Wenn wir in unserer Tabelle („meine_tabelle“) die Index-Spalte „url_crc“ und eine Textspalte „url“ (ohne Index) haben, dann genügt folgende Anfrage zur Eindeutigkeit:

SELECT * FROM meine_tabelle WHERE url_crc=CRC32(“http://sirmark.de”) AND url=’http://sirmark.de’;

Wichtig ist, dass die indexierte Spalte (“url_crc”) als erstes im SELECT abgefragt wird, da sonst der Index nicht greift!

Die 64bit-Hash-Abfrage in MySQL
MySQL liefert keine 64bit-CRC-Funktion. Wenn Sie jedoch mehr als 100.000 Datensätze in einer mittels CRC32-kodierten Funktion speichern, müssen Sie mit mindestens einer Hash-Kollision rechnen. Wenn Sie den eigentlichen Wert (in unserem Beispiel die URL im Klartext) nicht speichern möchten (da Sie den Wert nicht benötigen oder aus Sicherheitsgründen nicht speichern dürfen), dann können Sie den Hash erweitern. Wenn Sie einen zweiten eindeutigen Wert haben, hashen Sie diesen ebenfalls. Zusammen minimieren Sie die Möglichkeit einer Hash-Kollision deutlich.
In unserem Fall haben wir die Möglichkeit nicht. Also versuchen wir einen 64-Bit-Hash selbst zu generieren. Wir nutzen folgenden Trick: Aus unserer URL erzeugen wir noch zusätzlich einen MD5-Hash, nutzen jedoch nur einen Teil der 32 durch MD5 erzeugten Hash-Zeichenfolge, nämlich nur die letzten 16 Stellen:

SELECT MD5(‚http://sirmark.de‘) as MD5, RIGHT(MD5(‚http://sirmark.de‘),16) AS MyHash64

Zusammen mit der MySQL-Funktion CONV() erhalten wir nun eine Vergleichszahl, die die Möglichkeit einer Hash-Kollision deutlich minimiert. Conv arbeitet mit einer Genauigkeit von 64 Bit.

SELECT CONV(RIGHT(MD5(‚http://sirmark.de‘),16),16,10) AS MyHash64
Ergebnis: 18254737050769978686

Mysql PHP Index-Optimierung: Lange Index-Werte kürzen

Mit einem optimalen Index steht und fällt die Performance einer Datenbank. So ist ein optimales Datenbankdesign eine Kunst, die viel Erfahrung und Wissen verlangt. Und oft scheitert der Datenbankadministrator an den Anforderungen der Benutzer, die scheinbar unmögliches oder zumindest suboptimales von der Datenbank verlangen.

Ein Beispiel ist zum Beispiel das Speichern von URLs und die optimierte Abfrage darauf. Im ersten Schritt dürfte der Datenbankdesigner eine URL-Spalte mit varchar(100) definieren. Im Zeitalter von dynamischen Webseiten stößt man sehr schnell auf längere URLs, so dass die Beschränkung von 100 Zeichen sehr schnell an Grenzen stößt. Aber auch die beliebte Größe von 255 Zeichen wird schnell zu klein. Gemäß Microsoft kann der Internet Explorer URL mit bis zu 2.083 Zeichen verarbeiten. Also eine URL als Text anlegen? Sicher keine schlechte Idee, wenn die URL in der Datenbank nur zur Speicherung dient. Ist sie aber Teil einer Abfrage, dann muss ein Index über die URL gelegt werden.

SELECT irgendwas FROM meine_tabelle WHERE url=’http://sirmark.de’

Welche Probleme sind zu erwarten?
Wenn die Spalte URL plötzlich in ein varchar(500) geändert wird, explodiert die Größe des Index für diese Spalte förmlich. Neben gestiegenem Plattenplatz sinkt die Performance spürbar. Je nach verwendeter Storage-Engine überschreitet das feld bzw. Feldkombinationen plötzlich die zulässige Größe für den Index.

Möglichkeiten zur Optimierung des SQL-Index
Naheliegend ist natürlich, das Feld zum Speichern zu verkürzen. So könnte beispielsweise bei einer URL das „http://“ prinzipiell weggelassen werden, was uns pro Datensatz sieben Zeichen spart. Wenn im Beispielsfall alle URLs die gleiche Domain haben, kann diese weggelassen werden. Sind es nur eine „Handvoll“ Domains, könnten diese per Normalisierung ausgelagert werden. Doch sind es willkürliche URLs, kommen wir mit dieser Methode nicht sehr weit.

Speicherung per Hash: MD5 in PHP
Also stellt sich die Frage, ob wir überhaupt die URL speichern müssen bzw. diese zurückrechnen müssen. Wenn wir zum Beispiel Werte zu einer URL speichern und die eigentliche URL nur als Zuweisung vorhalten müssen, benötigen wir die genaue URL nicht. Nehmen wir einmal an, wir schreiben einen Cache. Wir möchten in einer Anwendung bestimmte Zugriffe nicht immer wieder erneut ausführen, sondern halten das Abfrageergebnis eine Weile in einer Datenbank vor (Cache). Die URL dient als Zuweisung, der eigentliche Content, also das Abfrageergebnis, können wir in einem Textfeld speichern. Da über die URL (Parameter) abgefragt wird, muss dieses Feld mit einem Index versehen werden.
In einem solchen Fall können wir uns damit begnügen, lediglich einen Hash der URL zu speichern. Zum Vergleich bei einer Datenbankabfrage genügt uns dies.

Das folgende Beispiel basiert darauf, dass wir eine Anwendung und den Cache in PHP schreiben. Gehen wir davon aus, dass wir eine Datenbank mit folgendem Aufbau haben

ALTER TABLE `local`.`meine_tabelle` CHANGE COLUMN `url` `url_hash` VARCHAR(32) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
ADD COLUMN `content` TEXT NOT NULL AFTER `url_hash`;

In dieser Tabelle speichern wir die URL nicht im Klartext, sondern als sogenannter MD5-Hash. Wir speichern also einen Fingerabdruck.
Der MD5-Hash hat den Vorteil, dass beliebig lange Zeichenketten auf eine maximale Zeichenkettenlänge von 32 Zeichen eingeschrumpft werden. Wir benötigen also nur ein varchar(32).
Zum Speichern berechnen wir aus der URL in PHP mittels md5($url) den Hash. Bei der Abfrage mittels SQL-SELECT müssen wir ebenfalls aus der Abfrage-URL den Hash ermitteln und über diesen abfragen:

SELECT content FROM meine_tabelle WHERE url_hash=’“.md5($url).“’

Die Funktion MD5 ist ebenfalls in MySQL implementiert, so dass Sie auch wie folgt direct in SQL abfragen können:

SELECT MD5(‚http://sirmark.de‘) as MD5

Diese Methode ist ein erster Ansatz zur Datenbankoptimierung, hat aber noch folgende Nachteile:

  1. Alle Zeichenketten werden 32-Zeichen lang. Also auch kürzere wie beispielsweise http://www.google.de
  2. Der Hash ist NICHT eindeutig! Die Umrechnung einer Zeichenkette ist zwar immer gleich, doch es ist nicht sicher, dass zwei völlig unterschiedliche Zeichenketten den gleichen Hash ergeben. Die Wahrscheinlichkeit ist je nach Anwendung gering, aber es muss damit gerechnet werden.

Der Nachteil, dass der Hash nicht zurückgerechnet gerechnet werden kann und die nicht gegebene Eindeutigkeit könnte mit einem weiteren Feld, in diesem Fall die URL im Klartext, in der Datenbank begegnet werden. Verrückt? Nicht ganz. Wir erhöhen zwar mit dieser Methode den gesamten Speicherplatzverbrauch, da wir neben der URL im Klartext ja auch noch einen 32-Zeichen langen Hash speichern, aber der Index ist in jedem Fall deutlich kleiner, als wenn dieser über die komplette URL aufgebaut werden würde.

Wir Sie mittels MySQL einen eigenen Hash-Index aufbauen, erfahren Sie im Artikel „Mysql PHP Index-Optimierung: Index-Werte optimieren mittels CRC32-MD5-Hash„.

MySQL: IP-Adressen optimal speichern

Wer sich noch nie Gedanken über die Speicherung von IP-Adressen gemacht hat, wird kaum auf die Problematik bei der Speicherung von IP-Adressen stoßen. Und so ist es an der Tagesordnung, dass eine IP-Adresse in aller Regel in einem Feld mit der Definition varchar(15) gespeichert wird. Wenn die IP-Adresse dort nur abgelegt wird, mag diese Definition genügen. Soll die IP-Adresse aber in einem SQL-SELECT abgefragt werden, soll sie eventuell eindeutig in der Tabelle vorhanden sein und/oder wird die Tabelle sehr groß, ist diese Feld-Tabelle alles andere als optimal.

Eine kurze IP-Adressen-Definition (IP4)
Die IP-Adresse besteht aus 4 Byte (32 Bit). Jedes Byte kann einen Wert zwischen 0 und 255 annehmen. Dargestellt wird  die IP-Adresse in vier Oketten, also beispielsweise 192.168.0.1. Somit ist die kleinste IP-Adresse die 0.0.0.0, die größte IP-Adresse 255.255.255.255.
Gleiches gilt für die eventuelle Speicherung einer Subnetzmaske. Diese Adresse enthält einen Netzanteil und einen Hostanteil und dient zur Wegfindung eines Computers zu einem bestimmten Zielnetz. Die Trennung von Netz- und Hostanteil erfolgt mit Hilfe der Subnetzmaske. Auch hier ist die kleinste Adresse die 0.0.0.0, die größte 255.255.255.255

Sind vier TinyInt-Blöcke eine Lösung?
Da wir nun wissen, dass es vier Blöcke gibt, deren Werte von 0 bis 255 annehmen können, wäre ein TinyInt ohne Vorzeichen ein optimaler Datentyp, da dieser Datentyp Werte von 0 bis 255 aufnehmen kann. Aus Speicherplatz-Sicht auf jeden Fall eine deutliche Verbesserung, denn der Datentyp TinyInt verbraucht nur ein Byte. Vier Bytes im Vergleich zu einem 16 Bytes bei einem varchar(15) eine deutliche Verbesserung. Doch eine Abfrage oder der Index über vier Spalten ist natürlich alles andere als optimal.

Die Lösung liegt im Integer
Vier TinyInt-Blöcke können auch in einem Integer abgebildet werden. Aus Speicherplatz-Sicht nimmt ein Integer genauso viel Platz ein, wie vier Tiny-Int, nämlich vier Byte. Doch aus Abfrage/Index-Sicht ist es natürlich wesentlich einfacher, die IP-Adresse in einem einfachen Integer-Feld abzulegen. Glücklicherweise bietet MySQL die Funktionen „INET_ATON()“ und „INET_NTOA()“ (Funktionen in PHP sind sprechender: ip2long() und long2ip()).

Diese Funktionen liefern den entsprechenden Integer-Wert einer IP-Adresse oder die IP-Adresse anhand des Integer-Wertes. In der Praxis bietet dies viele Vorteile und einen erheblichen Performance-Vorteil bei größeren Tabellen.

Beispiele:
PHP mit MySQL-Funktion:
$result = „INSERT user SET ip_int = INET_ATON(‚{$_SERVER[‚REMOTE_ADDR‘]}‘) WHERE id = “ . $user_id;

Oder PHP mit PHP-Funktion ip2long:
$result = „INSERT user SET ip_int = “.ip2long($_SERVER[‚REMOTE_ADDR‘]) .” WHERE id = “ . $user_id;

UPDATE: Fehlerhafte IP-Adressen bei der Rückrechnung

Wenn bei der Rückrechnung der IP-Adressen die Adresse „127.255.255.255“ gehäuft auftritt, sollte man sich schleunigst die MySQL-Konfiguration des Integer-Datentyps anschauen. Schauen wir uns doch mal das Ergebnis der Umrechnung an:

<?php
echo ip2long("127.255.255.255");
?>

Das Ergebnis dieser Ausgabe ist die Zahl „2147483647“. Wie wir im Artikel „MySQL Tabellen-Optimierung: Der optimale Datentyp“ nachlesen können, bietet der Integer in MySQL Platz bis zur 4.294.967.295; allerdings nur im positiven Bereich. Dieser Bereich halbiert sich, wenn auch negative Zahlen im Integer-Bereich platzfinden sollen. Dann ist eben bei der „2.147.483.647“ Schluss.

CREATE TABLE `integer_ohne_vorzeichen` (
`ip` int(11) unsigned default NULL
);
CREATE TABLE `integer_mit_vorzeichen` (
`ip` int(11) default NULL
);

Dieses Update schreibe ich (leider) aus gutem Grund, denn bei einem aktuellen Projekt habe ich dies nicht beachtet und mich auf die „default“-Einstellung des MySQL Integers verlassen. MySQL hat die IP-Adressen > 127 klaglos angenommen, den Integer jedoch beim Speichern auf den höchsten zulässigen Wert gekürzt.

SQL Server: Mail mit SQL Server verschicken

Der SQL-Server eignet sich auch hervorragend, um Emails zu versenden. Nicht verwechseln mit einem Spam-Mailschleuder, dazu ist der Datenbankserver nicht gedacht. Doch es gibt durchaus Anwendung, bei denen es sinnvoll ist, dass der SQL-Server direkt Mails verschickt.

Mögliche Anwendungen:
Jeder SQL-Administrator kennt die Fehlermails, die der SQL Server selbst verschickt. Liefert ein automatisierter Job einen Fehler, wird der zuständige Datenbankadministrator per Mail benachrichtigt. Nur selten laufende Jobs können auf diesem Wege eventuell auch im Erfolgsfall eine Statusmail verschicken: Der Datenbankadministrator oder andere im Betrieb zuständige Personen wissen so, dass der Job gelaufen ist.
Auch können so bestimmte Personen auf irgend ein Vorkommnis in der Datenbank hingewiesen werden. Bestimmte Datenbankkonstellationen erfordern das Handeln von Mitarbeiter – eine Mail weist den oder die Mitarbeiter darauf hin.

Die Mail verschicken ist eine schnell erledigte Aufgabe. Die System-Prozedur „sp_send_dbmail“ erledigt diese schnell, wie Sie im untenstehenden Beispiel erkennen können.

USE msdb
GO
EXEC sp_send_dbmail @profile_name='_SQLUser',
@recipients='test@mail.de',
@subject='Mein Betreff der Mail',
@body='Hier folgt der Email-Text'

Prüfen, ob Mails vom SQL-Server verschickt wurden
Leider kann man sich nicht zu 100 Prozent darauf verlassen, dass eine Mail auch verschickt wurde, auch wenn der ResultCode aus obigem Beispiel positiv ist. Der ResultCode bestätigt nur, dass der Cache die Mail entgegengenommen hat.
Wenn also der Verdacht nahe liegt, dass eine Mail nicht verschickt wurde, bemüht man an besten den folgende System-View aus der Datenbank „msdb“:

SELECT * FROM dbo.sysmail_sentitems
ORDER BY sent_date DESC

Alternativ:

SELECT *
FROM sysmail_mailitems

Dieser View liefert alle notwendigen Daten der verschickten oder auch anstehenden Mails des SQL-Servers. Wichtig ist das Feld „sent_status“. Es liefert Auskunft darüber, obe eine Mail verschickt wurde, oder ob es beim Versuch, die Mail zuzustellen, zu Problemen gekommen ist.

Weitere Email-Recherche auf dem SQL-Server
Wer noch weitere Informationen über das Mail-Verhalten seines SQL-Servers erhalten will, kann die folgende Abfrage bemühen:

SELECT *
FROM sysmail_log

C# Sonderzeichen entfernen mit Regex

Auf die Notwendigkeit, Sonderzeichen aus einem String zu entfernen, stößt der C#-Programmierer des Öfteren. Umso unverständlicher, dass es keine systemnahe Funktion gibt, um Strings zu glätten oder eben bestimmte Sonderzeichen zu eliminieren.

Warum benötigen wir diese Funktion?
Die Anwendungsbeispiele sind vielfältig. Wenn Sie zum Beispiel einen FTP-Upload planen, dann sollten Sie den Dateinamen ohne Sonderzeichen sicherstellen. Auch wenn es oft keine Probleme bereitet, sollten Dateinamen prinzipiell ohne Sonderzeichen generiert werden.
Oder Sie planen, Benutzereingaben zu überprüfen. Ein Benutzername sollte auf jeden Fall ohne Sonderzeichen bestehen. Oder Sie möchten im Adresssatz Straße und Wohnort einer kurzen Überprüfung unterziehen. Dort haben Zeichen wie „$%& …“ nichts zu suchen. Auch der Name und Vorname kann auf diese Weise geprüft werden. Hier gilt aber zu beachten, dass Umlaute, eventuell auch französische oder skandinavische Sonderzeichen, möglich sein sollen. UTF-8 sei dank. Auch ein beliebter Fehler (und mir leider auch schon passiert): Das Hochkomma. Aus SQL-Sicht verbietet man das Hochkomma („’“) gerne, um den SQL-String nicht zu gefährden. Doch spätestens wenn man Herrn „O’Henry“ zum Datenbestand hinzufügen möchte, kommt man um die ordentliche Maskierung dieses und anderer  Sonderzeichen nicht herum.
Um SQL-Injections zu verhindern, kann man beispielsweise in einer Benutzereingabe, die gegen die SQL-Datenbank geht, das Semikolon („;“) verhindern. Auf das Semikolon kann man in den allermeisten Fällen als Benutzereingabe verzichten und man erreicht dadurch, dass schon sehr vielen SQL-Angriffen den Grund und Boden entzogen wurde. Doch hier gilt: Das Verhindern der Semikolon-Eingabe ist nicht die alleinige Lösung. Das Semikolon MUSS auch noch – wie hier mit der folgenden Funktion – kurz vor der Datenbankabfrage geprüft werden. Auch wenn die Eingabefelder von C# meines Erachtens sehr sicher sind, schadet es nicht, Benutzereingaben auch noch mal in der zugrundeliegenden Programmvariablen zu prüfen. Im Web (PHP zum Beispiel) ist dies kein Rat, sondern der Verweis auf die Pflicht!
Noch abschließend der SQL-Injection-Hinweis, dass das Verhindern des Semikolons ein erster Schritt in die sichere SQL-Anwendung ist, doch damit ist das Thema leider noch nicht abschließend behandelt. Hier werde ich sicherlich noch einen weiteren Artikel schreiben.

Kommen wir nun endlich zum Listing Regex:

Listing1: Funktion gibt einen bereinigten String (Erlaubt sind nur 0-9, a-z, A-Z und einige Satzzeichen) zurück

public static String CheckString(String text)
{
    return System.Text.RegularExpressions.Regex.Replace(text, @"[^0-9a-zA-Z .;.,_-]", string.Empty);
}

Listing 2: Hier wird zusätzlich geprüft, ob per Regex der übergebene String sich verändert hat. Die Prüfung findet per „S“ (ein Zeichen, das kein Whitespace* ist), statt.

public static String CheckString(String text)
{
string sRes = System.Text.RegularExpressions.Regex.Replace(text, @"[^S ]", string.Empty);
      if (sRes != text)
      {
              Console.WriteLine("Text wurde geändert!");
      }
      return sRes;
            
}

* Whitespace (Leerraum): Steuerzeichen wie  f, n, r, t und v

C# DateTimePicker: Beispiele und Eigenschaften

Das DateTimePicker-Control eignet sich hervorragend, wenn man darauf angewiesen ist, dass der Benutzer ein Datum und/oder eine Uhrzeit einzugeben hat. Das Control vereinfacht die Benutzereingabe, kann aber auch gleich Falscheingaben verhindern. So sind Falscheingaben wie der der 31. Februar oder auch die Eingabe eines Datum größer oder kleiner eines Bestimmten Datums durch das Setzen von Min-/Max-Datum nicht mehr möglich.
Die wichtigsten Anwendungen und Eigenschaften des DateTimePickers sind hier kurz erläutert.

Datum dem DateTimePicker zuweisen
Per Standard zeigt der DateTimePicker das aktuelle Datum und ggfls. auch die aktuelle Uhrzeit an. Per „Value“ kann dem DateTimePicker ein bestimmtes Datum zugewiesen werden.

// Bestimmtes Datum zuweisen
dateTimePicker1.Value = new DateTime(2010, 3, 11);

//Aktuelles Datum zuweisen -> default
// Sinnvoll um Datum bei Bedarf wieder auf aktuelles Datum zurückzusetzen
dateTimePicker1.Value = System.DateTime.Now;

Um beispielsweise ein gespeichertes Datum aus einer SQL-Datenbank dem DateTimePicker zuzuweisen, verwenden Sie am Einfachsten den folgenden Programmcode:

dateTimePicker1.Value = Convert.ToDateTime(table.Rows[0]["mein-SQL-datum"].ToString());

Wenn Sie ein Datum Plus/Minus Tage, Monate oder Jahre vom aktuellen Datum aus  vorbelegen möchten (beispielsweise ein Mahndatum: plus vier Wochen), kann wie im folgenden Code dem DateTimePicker eine Zuweisung erfolgen.

// Jahre ändern
dateTimePicker1.Value = System.DateTime.Now.AddYears(-5);

// Tage ändern
dateTimePicker1.Value = datePicker.Value.AddDays(7);

Datum aus DateTimPicker auslesen
Das vom Benutzer eingegebene Datum kann einfach über den “Value”-Wert des dateTimePickers auch wieder ausgelesen werden.

//Datum einem String zuweisen
String sDatum = dateTimePicker1.Value;

Wenn wir das Datum jedoch in einem bestimmten Format benötigen (oder unsere Anwendung auch in anderen Länderregionen verbindlich funktionieren soll), müssen wir das Datum casten. Im folgenden Fall soll das Datum in eine SQL-Datenbank (Feld beispielsweise: „smalldatetime“) geschrieben werden:

//Datum aus DateTimePicker für SQL aufbereiten: yyyy-MM-dd
DateTime meindatum;
meindatum = dateTimePicker1.Value;
string sMeinDatum = meindatum.ToString("yyyy-MM-dd");

DateTimePicker: Anzeigen der Checkbox
Der DateTimePicker hat eine verborgene Checkbox, die angezeigt werden kann. Die Anwendung der Checkbox ist vielfältig. Im Standard dient sie dazu, dass erst mit aktivierter Checkbox der Benutzer das Datum verändern kann. Ein anderer Anwendungsfall ist die Möglichkeit, beispielsweise bei einem Bestellverwaltungs-System, ein bestimmtes Datum nicht als Pflichtfeld zu deklarieren, sondern erst einmal optional zu erlauben. Beispielsweise ist das Versanddatum beim Erfassen einer Bestellung noch nicht bekannt. Per Standard hätte der DateTimePicker gleich das Tagesdatum; das Enablen des DateTimePickers verhindert die Datumseingabe. Anhand der Checkbox kann der Benutzer so das Datum explizit auswählen und setzen.

dateTimePicker1.ShowCheckBox = true;

Datum begrenzen im DateTimePicker

    // Setze das Min- und das Maximum-Datum
   dateTimePicker1.MinDate = new DateTime(1990, 12, 24);
   dateTimePicker1.MaxDate = DateTime.Today;

Anzeige von Kalender zur Auswahl oder UpDown (Spin Button)
Der DateTimePicker zeigt per Standard einen Kalender zur Datumsauswahl an (einfaches Down-Icon am Ende der Box). Mit ShowUpDown = true ändert sich diese Anzeige. Das einfache Down-Icon ändert sich in ein UpDown, über das das Datum verändert werden kann.

dateTimePicker1.ShowUpDown = true;

Datum-Anzeige ändern: Kurzes Datum (ohne Wochentag), nur Uhrzeit
Per Standard zeigt der DateTimePicker neben dem Datum auch den Wochentag an. Dieses Verhalten können Sie per Custom-Format (siehe weiter unten) ändern. Einfacher geht es über die Änderung der Format-Eigenschaft: „Format: Short“
Um nur die Uhrzeit anzuzeigen, verwenden Sie einfach: „Format: Time“

Individuelle Anzeige durch das Custom-Format
Custom-Format: Anzeige von Wochentag und Datum

  // Setze das Custom-Format Wochentag Tag Monat Yahr
  dateTimePicker1.CustomFormat = "dddd dd MMMM yyyy";
  dateTimePicker1.Format = DateTimePickerFormat.Custom;

Custom-Format: Nur Datum anzeigen
Der DateTimePicker ist vom Platz her sehr mächtig. Wer in seiner Form mit der Platz sparsamer umgehen muss, der kann auf den Wochentag in der geschlossenen Ansicht des DateTimePickers verzichten. Ein Beispiel dazu zeigt das folgende Custom-Format

// Nur Datum anzeigen
dateTimePicker1.CustomFormat = "dd MMMM yyyy";
dateTimePicker1.Format = DateTimePickerFormat.Custom;

Anzeigen der Uhrzeit im DateTimePicker
Der DateTimePicker kann sehr gut für die Anzeige der Uhrzeit verwendet werden:

dateTimePicker1.Format = DateTimePickerFormat.Time;
dateTimePicker1.ShowUpDown = true;

Durch diesen Code kann die Uhrzeit angezeigt werden. Das Control eignet sich aber auch, um Uhrzeit-Benutzereingaben entgegenzunehmen.

Kalender im DateTimePicker LeftRightAlignment
Per Standard blendet sich der Kalender im DateTimePicker linksbündig ein. Eine einfache Änderung lässt den Kalender rechtsbündig erscheinen:

dateTimePicker1.DropDownAlign = LeftRightAlignment.Right;

Gimp: Spiegel-Schrift (Effekt: Wasser-Horizont)

Die Spiegelschrift im Wasser mit oder ohne Horizont ist ein Klassiker in der Schrifterstellung. Auch wenn man ihn daher nicht sehr häufig einsetzt, hat er seine Berechtigung und für das ein oder andere Logo kann dieser Effekt durchaus angebracht sein. Viele Grafikprogramme haben Filter, die diesen Effekt sozusagen „frei Haus“ liefern. Ich werde diesen Effekt heute mit der Open Source-Grafikanwendung Gimp versuchen selbst herzustellen.

Schritt 1: Neues Bild erstellen
Im ersten Schritt erstellen wir ein neues Bild mittels „Datei“ – „Neu“. Die Größe des Bildes sollte ausreichend dimensioniert sein.

Schritt 2: Text mit Farbe erstellen
Im nächsten Schritt wählen wir die Farbe unserer Schrift aus. Mit einem Klick auf das Farbsymbol „Vorder- und Hintergrundfarbe“ im Werkzeugkasten kann man die Farbe festlegen. Dann klicken wir auf das Werkzeug „Text“ („Werkzeuge“ – „Text“), wählen eine möglichst „dicke“ Schriftart und erstellen so den Text auf unserem Bild.

 

Schritt 3: Die Textebene duplizieren, verschieben und spiegeln
Nun klicken wir im Fenster „Ebenen …“ auf unsere Textebene, klicken dann mit der rechten Maustaste und wählen im Kontextmenü „Ebene duplizieren“ aus. Ich gebe der duplizierten Ebene den Namen „Spiegel“.
Nun wird mittels des Werkzeuges „Verschieben“ („Werkzeuge“- „Transformationen“ – „Verschieben“) die duplizierte Schrift unterhalb der ersten Schrift gezogen.

Nun spiegeln wir die Ebene „Spiegel“ vertikal. Dazu wählen wir das Werkzeug „Spiegeln“ mittels „Werkzeuge“ – „Transformationen“ – „Spiegeln“ aus. Unterhalb des Werkzeugkastens muss nun „Vertikal“ ausgewählt werden. Eventuell müssen Sie die gespiegelte Schrift noch einmal verschieben.

Schritt 4: Rechteckige Auswahl erstellen
Im nächsten Schritt benötigen wir eine rechteckige Auswahl („Werkzeuge“ – „Auswahlwerkzeuge“ – „Rechteckige Auswahl“). Mit diesem Werkzeug maskieren wir die nicht gespiegelte Schrift. Der untere Bereich unserer Maske sollte genau zwischen der ungespiegelten und gespiegelten Schrift liegen.

Schritt 5: Der Farbverlauf
Nun klicken wir auf das Werkzeug „Farbverlauf“ („Werkzeuge“ – „Malwerkzeuge“ – „Farbverlauf“). Im Werkzeugkasten erscheint ein Auswahlmenü. Hier ist der Farbverlauf „Von VG nach Transparent“ und der Punkt „Umkehren“ auszuwählen.

Schritt 6: Schnellmaske auswählen
Nun klicken wir mit der rechten Maustaste auf das kleine Rechteck im unteren linken Bereich unseres Bildes („Schnellmaske“) und wählen dort „Maske aus Auswahl“. Dann klicken wir auf das gleiche Symbol noch einmal, jedoch mit der linken Maustaste. Unsere rechteckige Auswahl aus Schritt 4 sollte nun rot hinterlegt sein..

Schritt 7: Farbverlauf ziehen
Nun ziehen wir unseren Farbverlauf von der oberen Schrift bis zum Ende der gespiegelten Schrift. Je nach gewählter Schriftart sind hier einige Versuche notwendig. In der Regel sollte man im unteren Drittel der Originalschrift beginnen und die Linie bis zum unteren Ende der gespiegelten Schrift ziehen. Hier muss ein wenig experimentiert werden, da jede Schrift eine individuelle Anpassung benötigt.
Dann klicken Sie bitte erneut auf das kleine Rechteck („Schnellmaske“) im unteren linken Bereich. Die roten Masken verschwinden. Dann wählen Sie „Bearbeiten“ – „Löschen“. Durch die Schnellmaske wird nun nur der „überschüssige“ Farbverlauf gelöscht.

Schritt 8: Zufrieden mit dem Ergebnis?
Sicherlich sind Sie im ersten Versuch mit Ihrem Ergebnis noch nicht zufrieden. Mittels STRG-Z können Sie die letzten Schritte widerrufen und die Schnellmaske samt Farbverlauf optimieren. Wenn Sie das beste Ergebnis erzielt haben, können Sie noch die Deckkraft der Ebene „Spiegel“ nach unten setzen (Ebene markieren, Schieberegler „Deckkraft“). Ferner kann dann im Anschluss auch noch die gespiegelte Ebene verformt werden („Werkzeuge“ – „Transformationen“ – „Perspektive“).

Hotmail-Konto: Server-Einstellung für HTC Desire

Als ich gestern Abend in einer Sitzung gesessen bin und freimütig behauptet habe, an einem bestimmten Termin Zeit zu haben, beschlich mich schon ein mulmiges Gefühl. Heute Morgen dann die Kontrolle und der Schreck: Mein HTC Desire hat alle Hotmail-Kalender-Daten nicht mehr verfügbar. Plötzlich sind auch alle Kontakte verschwunden und der Email-Verkehr kam ebenfalls zum Erliegen. Warum mein HTC die Verbindungsdaten zu Hotmail verloren hat, kann ich aktuell nicht nachvollziehen. Ich wollte nur schnell die Verbindung wieder aufbauen, da habe ich entdeckt, dass ich die Verbindungsdaten zu Hotmail Euch verschwiegen habe. Dies möchte ich hier kurz nachholen:

Hotmail Server-Verbindungsdaten für POP3
Die Verbindung via POP3 ist auch beim HTC Desire schnell erledigt. Ob dies der beste Weg ist, bezweifel ich, denn dies ist eine reine Mail-Verbindung. Kontakte und Kalenderdaten werden so nicht syncronsiert. Für eine solche Synchronisation das Protokoll „EXCHANGE“ verwenden!

So wird die POP3-Verbindung aufgebaut:

  1. Starten Sie die Mailanwendung auf dem HTC Desire, Taste „Menu“ drücken, dann „Konten“ auswählen. Dann die Taste „Menu“ erneut drücken. „Konto hinzufügen“ auswählen.
  2. E-Mail einrichten. Geben Sie Ihre Hotmail-Email-Adresse in Form benutzer@hotmail.de oder „.com“ sowie Ihr Passwort ein. Beim Passwort peinlich genau af die Eingabe achten. Schätze mal, dass 90 Prozent der fehlerhaften Verbindungen auf die falsche Passworteingabe zurückzuführen sind.
  3. en Haken bei „E-Mails standardmäßig von diesem Konto senden“ auswählen (oder nicht – je nach Wunsch).
  4. „Weiter“ klicken
  5. Auf die Frage, um welchen Kontotyp es sich handelt, nun „POP3“ eingeben.
  6. Geben Sie nun die folgenden Parameter ein:
    Nutzername: Ihre Email-Adresse (komplett)
    Passwort: Das hinterlegte Passwort (ggfls. mit manuellem Einloggen bei hotmail.com prüfen)
    POP3-Server: pop3.live.com
    Port: 995
    Sicherheitstyp: SSL (oder „SSL (Alle Zertifikate akzeptieren“)“
    E-Mail von Server löschen: „Nie“ ist erstmal gut. Wenn gewünscht, dies aktivieren
  7. „Weiter“ klicken. Es erfolgt eine Verbindung zum Server. Nur wenn diese korrekt aufgebaut werden kann, erfolgt die weitere Anlage.
  8. Für den Ausgangsserver die folgenden Daten verwenden:
    SMTP-Server: smtp.live.com
    Server-Port: 25
    Sicherheits-Typ: 25
  9. Wieder „Weiter“ klicken, noch ein paar weitere Fragen beantworten

Hotmail Server-Verbindungsdaten für EXCHANGE

  1. Die Punkte 1-4 wie bei POP3 abhandeln
  2. „EXCHANGE“ als Kontotyp auswählen
  3. Den „Domain-/Benutzername“ mit der kompletten Email-Adresse (benutzer@hotmail.de) überschreiben. Ein eventuell vorhandener „“ löschen!
  4. Passwort eingeben
  5. Server: „m.hotmail.com“
  6. Darauf achten, dass „Sichere SSL-Verbindung“ aktiviert ist
  7. Das war es eigentlich schon
  8. Nach dem Einrichten noch mal das Konto im Mail-Programm durch „lang anklicken“ – „Kontoeinstellungen“ prüfen. Wichtig sind die Parameter „Umfang der Synchronisation“, die „Abrufhäufigkeit“ und natürlich die Haken bei „Kontakte synchronisieren“ und „Kalender synchronisieren“.