MySQL – Den richtigen (Daten)typ kennen

Am Wochenende sah ich mir ein Script eines anderen Programmierers an … der Webmaster des Scriptes hatte eine Frage und zur Beantwortung benötigte ich Zugang zum Script. Nach langen Erklärungen, was denn genau passieren soll und was denn aktuell wirklich passiert, stieß ich auf einige Besonderheiten im Script.

Nun, den Fehler konnte ich beseitigen und der Webbi bat mich dann gleich mal, ob ich denn mal über den Code an sich sehen könnte, “ob man da noch was rausholen kann, so, Geschwindigkeitstechnisch”. “Immer”, denke ich mir und sehe mir das ganze so.

Okay, optimal war der Code nicht, eher genau das Gegenteil. Mein Kommentar war dann, dass, wenn der Webbi das ganze schnell haben will, er sich doch bitte einen _guten_ Programmierer holen soll, der das ganze mal neu macht. Der alte Code wäre dafür nicht geeignet. Der Webbi war nachher froh, dass der alte Code das macht, was er wollte und damit ist die Geschichte erstmal beendet.

Warum schreibe ich das ganze?
Nun, im Code fiel mir auf, dass ziemlich viele Variablen gegen die Datenbank geprüft wurden, nur für den Zweck, ob in einem Datenbankfeld – sinngemäß – ja oder nein steht. Dieses ja-oder-nein wurde dann aber in einem Zahlenfeld in mySQL gespeichert – soweit, so gut – nur eben ein INT(1).

Und nun zum Sinn dieses Postings: Lieber Programmierer, du brauchst in der DB einen Platz um “ja” oder “nein” zu speichern? Dann minimiere “ja” zu 1 und “nein” zu 0, soweit kamst du ja schon. Nun noch die Auswahl des geeigneten DB-Typen und da hast du gepatzt.

Ein INT(1) speichert eben NICHT nur eine Zahl mit der Länge 1 (also 0 bis 9), sondern einen 4 Byte großen Integer Wert, den du mittels “ZEROFILL” dann auf die in den Klammern angegebene Zahl mit 0 auffüllen kannst. Habe ich ein INT(3) Feld und speichere dort die Zahl 4 rein, dann steht da 004; in ein INT(3) kann ich aber auch die Zahl 123456 schreiben und habe kein Problem damit, da ein 4-Byte INT Zahlen von 0 bis 4294967295 (UNSIGNED) oder von –2147483648 bis 2147483647 (SIGNED) speichern kann, egal was in Klammern steht.

Um Speicherplatz zu sparen nimm bitte den Datentyp TINYINT, der speichert nur 1 Byte große Zahlen, also von 0 bis 255 (UNSIGNED) oder –128 bis 127 (SIGNED).

Zur weiteren Info lies dir die folgenden beiden Links durch:
http://dev.mysql.com/doc/refman/5.1/de/numeric-types.html
http://dev.mysql.com/doc/refman/5.1/de/storage-requirements.html

Refactoring – Alter Code im neuen Kleid … nicht nur für PHP

Grade in einem Code gefunden, der Aufruf der Funktion ‘getSession()’. Und nun mal die Preisfrage an alle PHP-Pro’s: Wieviel Code davon muss wirklich sein?
Aber erstmal der Originalcode:

function getSession(){
  $akt_timestamp = getTimestamp();
  $ses_timestamp = $_SESSION[getSession_Timeout()];
  if($akt_timestamp > $ses_timestamp){
    return false;
  }
else{
    if($akt_timestamp < $ses_timestamp){
      return true;
    }
  }
}
function getTimestamp() {
  return time();
}
function getSession_Timeout(){ return „usrTimeout“; }

*wow* Das sind 3 Funktionen, die ineinandergreifen, was?
Und nun – nochmal – die Preisfrage: Wieviel Code davon muss sein?
Ich behaupte: Nicht so viel, wie oben steht.
Beweis? Ich refaktorisiere den Code oder neudenglisch: Ich mache refactoring! (Ich red’ da lieber deutsch, aber jeder wie er/sie will).
Betrachten wir die Anforderungen:

  • beim Aufruf der Funktion getSession() wird eine Rückgabe vom Typ Boolean erwartet, dies sollten wir beachten.
  • Vereinfacht wird das refactoring (hier finde ich das passend) dadurch, dass es keine Übergabeparameter an die Funktion gibt.

So, die Hülle der Funktion steht, nun zum Inhalt.
Was macht die Funktion eigentlich?

  • Sie legt eine lokale Variable an, in der die aktuelle Zeit gespeichert wird.
  • Sie legt eine lokale Variable an, in der ein Wert aus der aktuellen Session gespeichert wird.
  • Wenn die aktuelle Zeit größer ist als der Wert der Session, gibt die Funktion false zurück.
  • Ist die aktuelle Zeit kleiner als der Wert der Session, dann gibt die Funktion true zurück.
  • Hier schon der erste Fehler: Was ist, wenn beide Werte gleich sind? Dieser Fall wird nicht berücksichtigt.

Nun das refactoring:

  1. Die Funktion getTimestamp() wird in der Funktion getSession() durch den Aufruf von time() ersetzt, somit fällt diese Funktion weg.
  2. Die Funktion getSession_Timeout() wird in der Funktion getSession() durch die Rückgabe von getSession_Timeout() ersetzt, somit fällt auch diese Funktion weg.
  3. Ziel der Funktion ist es, ein true oder false bei o.a. Bedingungen zurückzugeben.

Während unseres Refactorings verbessern wir auch gleich die Funktion der Funktion (*g*) und legen auch gleich fest, was passieren soll:

  • Ist die aktuelle Zeit größer wie der Wert in der Session, dann gibt die Funktion true zurück, sonst false.

Klingt einfach? Ist es auch.
Verkürzt man nun sukzessive die if…else Schleife und bedenkt, dass man die beiden lokalen Variablen nicht braucht und ersetzt diese mit ihren Entsprechungen (time() und Session-Wert),  so erhält man folgende Funktion:

function getSession()
{
  return (time() < $_SESSION[‚usrTimeout‘]);
}

Kurz, knackig und präzise. Diese Funktion erfüllt alle Anforderungen (und übererfüllt sogar diese, in dem es die alte Funktion um den Bereich “Zeit ist gleich Sessionwert” erweitert).

Für meine Behauptung oben auf die Frage “Wieviel Code davon ist nötig” würde ich nun sagen: q.e.d.

So, das war ein kleiner Ausflug in die Welt des Refactoring.
Ich hoffe, ihr habt alle was gelernt …

HEREDOC in PHP – Kleine Einführung …

Muss man oft fremden Code in eine Seite einbinden und dies auch noch per JavaScript, so kommt es nicht selten vor, dass mensch den entsprechenden Code per E-Mail bekommt.

Ich rede hier von Fremdcode in der länge 6-60 Zeilen, so wie in etwa für Analyse-Javascripte oder sonstige, kleinere Sachen. Diese sollen dann meist über PHP in den HTML-Code eingebettet werden und meist auch exakt so, wie der Code vorgegeben wird (Stichwort Zeilenumbrüche).

Nun folgt der nächste Schritt, man öffnet das PHP-File und bereitet den Einbau vor. Oft sehe ich es bei Kollegen und Bekannten so:

  1. PHP-Datei öffnen.
  2. Fremdcode per Copy & Paste in den heimischen Editor bringen.
  3. Ein “echo” vorbereiten.
  4. Es folgt ein wildes getippe, damit alle Anführungszeichen entsprechend escaped werden.
  5. Hochladen
  6. zu 80%: Frust!

Frust! Der entsteht zu 80% am Ende der oft langen Phase des “coden” und “escapen”, weil am Ende vergessen wurde, den ganzen “echo’s” auch Zeilenumbrüche wie \n mitzugeben, damit nicht der ganze Code auf einer Zeile im Quelltext steht. Manche Analyse-Codes nehmen einem das mit falschem oder – schlimmstenfalls – keinem Tracking übel.

Nur, was tun? Das Stichwort dazu heißt: HEREDOC

Mit HEREDOC kann ich Fremdcode so in eine Variable oder an eine Ausgabe übergeben, dass der exakt so ausgegeben wird und das ist so einfach, dass ich auf jeden Fall schneller bin wie meine Kollegen – und dabei kann ich mich auch auf die Zeilenumbrüche verlassen.

HEREDOC funktioniert so:

$meinFremderInhalt = <<< MEINHEREDOC

Inhalt, Inhalt

Mehr Inhalt, kann auch Code sein

usw. , usf.

MEINHEREDOC;

Man nehme eine Variable und weise ihr mit Hilfe von <<< den Inhalt dessen zu, was der folgende HEREDOC-Delimiter enthält. Alles ab der Zeile unter MEINHEREDOC wird übernommen, Zeilenumbrüche, Einschübe usw.

Ein HEREDOC Bereich endet dann wieder mit dem exakten (!) HEREDOC Delimiter, den mensch auch oben benutzt plus einem abschließenden ; (nicht vergessen!).

Dannach kann mensch ganz einfach den Inhalt per

printf(‘%s’.”\n”,$meinFremderInhalt);

zum Beispiel in die Webseite ausgeben – inklusive Zeilenumbrüche.

Wie oft kommt string1 in string 2 vor? Kleine Hilfe …

Weil ich es nun schon oft gesehen habe, hier mal die kleine Anleitung zum Thema: „Wie zähle ich, wie oft string1 in string 2 vorhanden ist?“

Ich sehe fast täglich neue implementationen dazu, wobei sich die entsprechenden Programmierer des öfteres mal „einen abbrechen“, eine aufwändige Funktion zu erstellen, die dann aber doch an Kleinigkeiten scheitert (z.B. keine UFT-8 Unterstützung, nicht binary-safe, usw.).

Aber warum machen es sich die Leute so schwer?
Weil diese Leute die PHP-Funktion substr_count nicht kennen.

Ein Beispiel: Ihr wollt zählen, ob eine Zeichenkette in einem String vorkommt, dann geht das hiermit am schnellsten:

$string = "Hallo Welt, ich bin da, dein liebster elton!";
$suchstring = "elt";
if (substr_count($string,$suchstring) > 1)
{   
  // suchstring in string mindestens einmal vorhanden, macht was
}
else
{  
  // suchstring NICHT vorhanden
}

Gar nicht schwer, oder?

Darüberhinaus kann man auch einen offset angeben, sozusagen die Startposition, in unserem Beispiel oben ergibt das:

echo substr_count($string,$suchstring);
echo substr_count($string,$suchstring,12);

Ergebnis:
2
1

Der optionale vierte Parameter gibt die Suchlänge an, d.h.:

echo substr_count($string,$suchstring);
echo substr_count($string,$suchstring,12,3);

Ergebnis nun:
2
0

Die Funktion ist sehr schnell – in meinen Tests sogar schneller wie strpos – und läßt sich auch sehr einfach einsetzen; darüberhinaus ist substr_count binary-safe, d.h. es arbeitet auch mit UTF-8 usw.

Mein Tipp: spart euch Zeit und nerven, benutzt die Funktionen die schon da sind, erfindet nicht das Rad neu und werdet schneller produktiv…

strtolower auch für UTF-Sonderzeichen

Das Problem: Ein beliebiger String soll kleingeschrieben werden.

Unsere Funktion dazu:

$sMyString = strtolower(trim($sMyString));

Das funktionierte aber plötzlich für einen String nicht mehr, das große Ö blieb ein großes Ö, also debuggen und siehe da, im Zend Debugger stellt sich das Ö als Viereck dar –> Hinweis auf ein Somderzeichen. Es war auch wirklich kein ASCII-Ö, sondern ein UTF8-Ö (komm da erstmal drauf).

Die Lösung: Es wird die Multibyte-Variante von strtolower benutzt:

$sMyString = mb_strtolower(trim($sMyString));

Vorher allerdings drauf achten, dass euer Projekt auch wirklich UTF-8 geeignet ist.

Dazu

  • Datei als UTF-8 speichern
  • Charset UTF-8 einstellen
  • Die mysql-connect’s auf UFT-8 einstellen, dies geht so

// mysql_connect

mysql_query("SET character_set_results = 'utf8', character_set_client = 'utf8', character_set_connection = 'utf8', character_set_database = 'utf8', character_set_server = 'utf8'");

// query ausführen

Diesen Code nach dem mysql_connect und der ersten Abfrage ausführen, dann hat man alle Daten als UTF-8.

Ach ja, und natürlich auch die Datenbank-Tabellen in einem geeigneten Charset speichern 😉

Immatrikuliert

Es geht weiter, grade kam per Post meine Imatrikulationsbescheinigung. Ab April geht es dann mit 3 Modulen los.

Natürlich war dem ganzen auch gleich der Gebührenbescheid angehängt, was dann die Freude wieder etwas trübt.

Naja, ab April kann ich meine Gedanken dann auf “Mathematische Grundlagen”, “Einführung in die objektorientiere Programmierung” und “Datenstrukturen I” lenken.

Mit Putty per Doppelklick direkt zum Zielserver

Tja, wer kennt das nicht. Putty auf, richtige Verbindung raussuchen, Username eintippen, an das richtige Passwort erinnern und so weiter und so fort.

Wenn man, wie ich, mit vielen verschiedenen Servern arbeiten muss, wäre man ja über jede Erleichterung dankbar. Für Putty habe ich so eine Arbeitserleichterung nun gefunden.

Also, zunächst stellt man sich in Putty alles für den Server so ein, wie gewohnt, sprich, Serveradresse eingeben, Farben und Größe des Fenstern bestimmen und so weiter.

Nun gibt man dem ganzen einen netten Namen (in diesem Beispiel nennen wir den Rechner “Hades”).

Nun erstellt man sich (zum Beispiel auf dem Desktop) eine Verknüpfung Putty. Diese Verknüpfung editiert man nun und stellt (bei Windows) unter “Ziel” das ganze so ein.

C:\Programme\PUTTY\putty.exe -load „Hades“

Nun wird beim doppelklick schonmal der entsprechende Server ausgewählt und man kann direkt mit dem eintippen von Username und Passwort weitermachen.

Aber da Entwickler bekanntermnaßen ein recht faules optimierfreudiges Volk sind, kann man diesen Arbeitsaufwand auch noch minimieren.

Wir ergänzen das “Ziel” also noch um mindestens 2, maximal 3 Einträge, diese sind

  • ssh
  • l
  • pw

“ssh” gibt den Verbindungstypen vor, “l” den Usernamen und “pw” das Passwort. Die Optionen “l” und “pw” setzen einander voraus (“pw” allein geht nicht) und beide setzen die Option “ssh” vorraus (ohne die geht nix).

Also, wer sich mit Putty zu einem bestimmen Zielserver (zum Beispiel dem SVN oder CVS Server) verbinden will und dabei _nur_ das Passwort eintippen möchte, der schreibt sowas:

C:\Programme\PUTTY\putty.exe -load „Hades“ –ssh –l USERNAME

Den Usernamen müsst ihr schon richtig reinschreiben.

Soll die Verbindung sofort und komplett und ohne weitere Angaben aufgebaut werden, dann macht ihr das so:

C:\Programme\PUTTY\putty.exe -load „Colin (SVN)“ -ssh -l USERNAME -pw PASSWORD

Auch hier: Passwort und Username noch eintragen, dann kann es ganz leicht per Doppelklick losgehen.

Erleichtert einem wirklich etwas die Arbeit, auch wenn es sich empfiehlt, die “pw” Option nicht zu nutzen. Man spart sich das (r)aussuchen des richtigen Servers und den Usernamen, braucht also nur noch das Passwort.

Viel Spaß …

Java MySQL Tutorial

In Sachen Java und Datenbank bin ich ein totaler Neuling. Allerdings hält man sich ja gern fit in solchen Sachen und da wir hier firmenintern ein neues Desktop-Tool brauchen, bot sich Java an. Für mich die einzige Schwierigkeit: Die Verbindung von Java zur MySQL-Datenbank.

Lange habe ich gesucht, lange herumprobiert und getippt und gelesen und wieder getippt. Am Ende habe ich es dann geschafft, mit ganz einfachen Mitteln eine Verbindung herzustellen.

Die Anforderungen waren:

  • Connect zur MySQL Datenbank
  • Ausführen eines SELECT´s
  • Auswerten der Antwort
  • Schließen der Datenbank

Das ganze aber ohne Frameworks wie Hibernate oder sowas, sondern nur mit Bordmitteln, die Java mitliefert. Betrachtet man das Ergebnis, ist es doch sehr einfach, aber man muss es ja auch erstmal wissen. Ich will es euch nicht vorenthalten. Dazu habe ich mal eine Beispiel – Konsolen Anwendung mit NetBeans erstellt, der Code läßt sich aber problemlos in große Projekte übernehmen.

Schritt 0: Benötigte imports

Folgendes import wird benötigt

import java.sql.*;

Vor den ganzen Code setzt man noch folgende Merker:

Statement stmt;       
ResultSet rs;

Die Funktion von ResultSet und Statement erkläre ich später.

Schritt 1: Treiber einbinden

(Alles ab hier sollte in einen try…catch Block)
Den Treiber für MySQL muss man mit 2 Schritten einbinden, in Netbeans geht das ziemlich einfach.
a) Rechtsklick auf das Projekt, Properties, dann Libraries, dann bei „Compile“ und „Run“ die „MySQL JDBC Driver“ mit „Add Library“ auswählen und bestätigen.
b) Den Code

Class.forName("com.mysql.jdbc.Driver");

hinzufügen.

Schritt 2: Verbindung zur MySQL-Datenbank herstellen

Die Verbindung zur Datenbank wird wie folgt hergestellt


String url     = "jdbc:mysql://localhost:3306/meinDatenbankSchema";
Connection con = DriverManager.getConnection(url,"benutzername", "benutzerpasswort");
stmt = con.createStatement();

Zeile 1 definiert das Datenbankziel, Zeile 2 stellt die eigentliche Verbindung her. Nicht vergessen, die Daten für „meinDatenbankSchema„, „benutzername“ und „benutzerpasswort“ anzupassen.
Zeile 3 erzeugt ein sogenanntes „Statement“ Objekt, mit welchem wir Anfragen an die Datenbank stellen können (Querys).

Schritt 3: Querys zur Datenbank senden

SQL-Querys sendet man mit Hilfe des Statement-Objektes zur Datenbank und wertet das ganze mit dem ResultSet-Objekt wieder aus.
SELECT-Beispiel, absenden des SQL, return-typ ist ResultSet:

rs = stmt.executeQuery("SELECT vorname, nachname FROM users");

UPDATE-Beipiel, absenden des SQL, return-typ ist integer:

int anzahlZeilen = stmt.executeQuery("UPDATE users SET vorname = 'Harry' WHERE vorname='Larry' ");
Schritt 4: Auswerten eines SELECT Ergebnisses

Bei einer SELECT Anfrage bekommt man ein ResultSet Objekt zurück, dieses kann man wie folgt auswerten:

while (rs.next())       
{
    String  vorname = rs.getString("vorname");
    String  nachname = rs.getString("nachname");
    System.out.println("Vorname: "+vorname+" | Nachname: "+nachname);
}
Schritt 5: Auswerten eines UPDATE Ergebnisses

Einfach den integer auswerten, er gibt die Anzahl der sogenannten „betroffenen Zeilen“ an.

Schritt 6: Verbindung zur MySQL-Datenbank schließen

Geht ganz einfach und sollte in jedem Fall gemacht werden (im Gegensatz zu PHP, wo das leider noch ein kann ist, ist es bei Java ein muss).

con.close();

So, ich hoffe, ihr könnt damit was anfangen – im wahrsten Sinne des Wortes. Mir jedenfalls wird es sehr helfen, meine ersten Datenbankabhängigen Servlets, JSP’s oder Java-Anwendungen zu schreiben.