Benutzer-Werkzeuge

Webseiten-Werkzeuge


dev:all:examples:sql

Dies ist eine alte Version des Dokuments!


So funktioniert das mit SQL...

Die ganzen Beispiele in diesem Bereich sind im neuen Stil von WB-2.11.x aufwärts ausgeführt. Bei älteren Versionen ist meist nur $oDb durch $database, <php>$oDb→TablePrefix</php> durch TABLE_PREFIX und <php>$oDb→doQuery()</php> durch <php>$oDb→query()</php> zu ersetzen.

Eine Änderung ist jedoch sehr wichtig:
Ab 2.11.0 kann das aktuelle Datenbankobjekt an jeder beliebigen Stelle mit
<php>$oDb = Database::getInstance();</php> abgerufen werden.
<php>global $database;</php> oder <php>$GLOBALS['database']</php> ist deprecated (unerwünscht) und wird in einer der Folgeversionen nicht mehr zur Verfügung stehen.

SQL-Statements richtig aufgebaut

Das ist schon fast ein ganzes Kapitel für sich. Also am besten erst ein mal die grundlegensten Regeln:

  • Statements dürfen nicht in der Argumentenklammer einer Funktion/Methode erstellt werden.
  • Statements müssen grundsätzlich im SQL-Strikt-Syntax aufgebaut werden
    • alle SQL Schlüsselwörter müssen in Großbuchstaben geschrieben werden.
    • alle Feld- und Tabellennamen müssen in `Backticks` eingeschlossen werden.
  • Statements dürfen nicht mit text, sondern ausschließlich mit 'text' begrenzt werden. Wird innerhalb des Strings ein Apostroph ' benötigt, so ist dieser mit einem Backslash \' zu maskieren.

SQL-Statements sollten auch optisch so aufgebaut werden, dass sie problemlos und schnell gelesen und erfasst werden können. Die zeilenweise Aufteilung nach Action-Schlüsselwörtern ist an der Stelle sehr sinnvoll. Ist eine Zeile zu lang (Codingstandards) dann mit Einrückung auf mehrere Zeilen verteilen. Bei der Feldauswahl im SELECT-Bereich ist zu beachten, dass der Server komplette Datensätze (SELECT *) in der Regel deutlich schneller liefern kann, als eine lange Auswahlliste von einzelnen Feldern. Um den Aufbau von Statements zu demonstrieren, folgen einige Beispiele der wichtigsten Abfragetypen.


SELECT

Alle vier Beispiele geben jeweils ein Result-Objekt mit allen Datensätzen der zum aktuellen Zeitpunkt sichtbaren Sections einer Seite zurück.

Beispiel-1.php
$oResult = $oDb->doQuery("SELECT section_id, page_id, position, module, block, publ_start, publ_end FROM ".TABLE_PREFIX."sections where page_id= $iPageId and (publ_start = 0 OR publ_start <= $iTimestamp ) and (publ_end = 0 OR publ_end >= $iTimestamp ) order by block, position");
Beispiel-2.php
$sql  = 'SELECT `section_id`, `page_id`, `position`, `module`, `block`, `publ_start`, `publ_end` FROM `'.$oDb->TablePrefix.'sections` ';
$sql .= 'WHERE `page_id`='.$iPageId.' AND (`publ_start`=0 OR `publ_start`<='.$iTimestamp.') AND (`publ_end`=0 OR `publ_end`>='.$iTimestamp.') ';
$sql .= 'ORDER BY `block`, `position`';
$oResult = $oDb->doQuery($sql);
Beispiel-3.php
$sql = 'SELECT `section_id`, `page_id`, `position`, `module`, '
     .        '`block`, `publ_start`, `publ_end` '
     . 'FROM `'.$oDb->TablePrefix.'sections` '
     . 'WHERE `page_id`='.$iPageId.' '
     .        'AND (`publ_start`=0 OR `publ_start`<='.$iTimestamp.') '
     .        'AND (`publ_end`=0 OR `publ_end`>='.$iTimestamp.') '
     . 'ORDER BY `block`, `position`';
$oResult = $oDb->doQuery($sql);     
Beispiel-4.php
$sql = 'SELECT * FROM `'.$oDb->TablePrefix.'sections` '
     . 'WHERE `page_id`='.$iPageId.' '
     .        'AND (`publ_start`=0 OR `publ_start`<='.$iTimestamp.') '
     .        'AND (`publ_end`=0 OR `publ_end`>='.$iTimestamp.') '
     . 'ORDER BY `block`, `position`';
$oResult = $oDb->doQuery($sql);     

Zur Auflockerung eine kleine Quiz-Frage:
Welche Beispiele lassen sich leichter lesen, verstehen und bei Bedarf auch leichter ändern? 1&2 oder 3&4 ??


DELETE

Bei dieser Abfrage kann man eigentlich nichts falsch machen… ausser der Angabe falscher Lösch-Kriterien. ;-)
Aber auch hier gilt: Erst das Statement in einer Variablen aufbauen und diese dann an die Query-Methode übergeben.

DELETE-1.php
$sql = 'DELETE FROM `'.$oDb->TablePrefix.'users`'
     . 'WHERE `user_id`='.$iUserId;
$oDb->doQuery($sql);

Datensätze sollten in der Regel nur über ihre Datensatz-ID gelöscht werden.


INSERT / UPDATE

In diesem Bereich wird es langsam interessant. Es gibt mehrere verschiedene Arten die Statements für INSERTs und UPDATEs aufzubauen. Speziell im Bereich der Werteübergabe.
Bei allen Arten von INSERTs gilt jedoch die SQL-Strikt Regel, dass allen Feldern eines Datensatzes Werte zugewiesen werden müssen. Ausgenommen hiervon sind nur die Felder, die in der Tabelle bereits mit einem Default-Wert vordefiniert sind. Immer wenn Daten in die Datenbank geschrieben werden sollen, sind bestimmte Sicherheitsregeln zu beachten.

  • Es muss sichergestellt sein, dass nur der jeweils richtige Datentyp übergeben wird.
  • Es muss sichergestellt sein, dass jeder übergebene Wert zuvor überprüft wurde und gültig ist.
  • Es dürfen nicht Werte aus Superglobalen Arrays, speziell aus $_POST/$_GET/$_REQUEST/usw. direkt übergeben werden.
  • Stringvariable müssen vor der Übergabe 'escaped' werden. Dazu ist ausschließlich die Methode escapeString() des Datenbankobjektes zu verwenden, da nur diese Methode genau so maskiert, wie es die aktuell benutzte Datenbank benötigt. Irgendwelche andere Methoden wie addslashes() oder selbstgebaute Funktionen etc. sind nicht zulässig.
  • Enthält eine Tabelle ein Autoincrement-Feld, so darf dieses bei einem INSERT nicht gesetzt und bei einem UPDATE nicht verändert werden. Bei einer unter STRIKT laufenden Datenbank würde dies einen schweren Fehler auslösen und das Programm abbrechen. Ebenso wird ein Abbruch ausgelöst, wenn einem Feld ohne Default-Wert bei einem INSERT kein Wert zugewiesen wird.

:!: Im Umfeld von WebsiteBaker ist für INSERT und UPDATE Statements ausschließlich die 'SET'-Methode zur Werteübergabe zulässig!

Erst ein Beispiel wie ein Statement (obwohl syntaktisch richtig) nicht aussehen darf:

insert-01.php
// das ist übrigens ein Original-Statement aus WB-2.8.3-SP1 'add user'
$sql = "INSERT INTO ".TABLE_PREFIX."users (group_id,groups_id,active,username,password,display_name,home_folder,email,timezone, language) VALUES ('$group_id', '$groups_id', '$active', '$username','$md5_password','$display_name','$home_folder','$email','-72000', '$default_language')";

Das selbe Statement jetzt nach den zuvor beschriebenen Regeln:

insert-02.php
$sql = 'INSERT INTO `'.$oDb->TablePrefix.'users` '
     . 'SET `group_id`='.(int)$group_id.', '
     .     '`groups_id`=\''.$oDb->escapeString($groups_id).'\'`, '
     .     '`active`='.($active ? 1 : 0).', '
     .     '`username`=\''.$oDb->escapeString($username).'\', '
     .     '`password`=\''.$oDb->escapeString($md5_password).'\', '
     .     '`display_name`=\''.$oDb->escapeString($display_name).'\', '
     .     '`home_folder`=\''.$oDb->escapeString($home_folder).'\', '
     .     '`email`=\''.$oDb->escapeString($email).'\', '
     .     '`timezone`='.-72000.', '
     .     '`language`=\''.$oDb->escapeString($default_language).'\'';

Ein UPDATE sieht im Prinzip genau so aus:

SET-1.php
$sql = 'UPDATE `'.$oDb->TablePrefix.'users` '
     . 'SET `display_name`=\''.$oDb->escapeString($sNewDisplayName).'\', '
     .     '`active`=1 '
     . 'WHERE `user_id`='.(int)$iUserId;

Ein großer Vorteil der SET-Methode ist neben der Übersichtlichkeit und der leichten Änderbarkeit, der Umstand, dass auf die Reihenfolge der Felder keine Rücksicht genommen werden muss. Es genügt einfach, dass alle benötigten Felder vorhanden sind. Diese Vorteile wiegen weit stärker als die dafür zusätzlich benötigte Zahl an Zeilen.


REPLACE

(mySQL spezifische Erweiterung zu ANSI-SQL)

Wie überall gibt es fast keine Regel ohne Ausnahme.
REPLACE funktioniert primär identisch wie INSERT. Mit einem kleinen, jedoch entscheidenden Unterschied:
Wird versucht, einen Datensatz einzufügen, der einen Index-Konflikt mit einem bestehende Datensatz auslöst, wird der Prozess nicht abgebrochen, sondern der bestehende, alte Datensatz wird gelöscht und der neue eingefügt.
REPLACE ist grundsätzlich nicht für Tabellen geeignet, die einen Autoincrement-Wert nutzen, der in einer anderen Tabelle als Fremdschlüssel benutzt wird.
Der WebsiteBaker-Core benutzt dieses SQL-Kommando z.B. zum Eintragen und Ändern von Werten in die Tabelle `settings`. Damit das Ganze funktioniert, sind gewisse Anforderungen an die Tabelle zu erfüllen.

-- Structure of table  '{TABLE_PREFIX}settings'
--
CREATE TABLE IF NOT EXISTS `{TABLE_PREFIX}settings` (
  `name` VARCHAR(255){FIELD_COLLATION} NOT NULL,
  `value` text{FIELD_COLLATION} NOT NULL,
  PRIMARY KEY (`name`)
){TABLE_ENGINE};

Im Statement muss zwingend ein Wert für das Feld mit dem PRIMARY_KEY oder UNIQUE_KEY übergeben werden. Auch der Aufbau des Statements weicht vom normalen 'SET' - Standard ab, dafür können als Ausgleich auch mehrere Datensätze in einem Zug geändert werden:

snippet.php
    $sql = 'REPLACE INTO `'.$oDb->TablePrefix.'settings` (`name`, `value`) '
         . 'VALUES (\''.$sName_1.'\', \''.$oDb->escapeString($sValue_1).'\'), ' // Record 1
                . '(\''.$sName_2.'\', \''.$oDb->escapeString($sValue_2).'\'), ' // Record 2
                . '(\''.$sName_3.'\', \''.$oDb->escapeString($sValue_3).'\')';  // Record 3
    if (!$oDb->doQuery($sql)) {
        $retval = false;
    }
dev/all/examples/sql.1489400303.txt.gz · Zuletzt geändert: 13.03.2017 10:18 von Manuela v.d.Decken