• Jetzt anmelden. Es dauert nur 2 Minuten und ist kostenlos!

sql abfragen vereinfachen

D

DiVaO

Guest
Moin,

eine einfach Frage:

Habe mir eine Seite programmiert und versuche sie möglichst stark zu optimieren, d.h. code und Speicherplatz zu sparen, etc.

nun habe ich in vielen dateien oft eine ganze hand voller mysql-abfragen, die immer nach dem gleichen schema ablaufen...

Code:
$sql = "SELECT irgendwas FROM irgendwo";
$result = mysql_query($sql);
$row = mysql_fetch_assoc($result);

Lassen sich diese drei Schritte nicht verkürzen in zwei oder anders vereinfachen? Nervt irgendwie das fünf mal in einer Datei stehen zu haben...

Danke
 
Du könntest dir eine Funktion basteln, die das für dich macht. Obs performanter ist weiß ich nicht.
function sql(table, coloumn, where){} oder so.
 
Ich habe mir für MYSQL eine eigene Funktionsdatei gebastelt, die alles komplett erledigt, vom Verbinden, über einzelne Datensätze auslesen bis hin zur kompletten Ausgabe einer Tabelle mit Sortierung, wählbaren Spalten, Aliasnamen etc.
 
Degers schrieb:
Du könntest dir eine Funktion basteln, die das für dich macht. Obs performanter ist weiß ich nicht.

Lesbarer, gekapselter Code mit möglichst wenig Redundanz ist sozusagen immer die bessere Wahl.

Man könnte zudem überlegen, ob es sich lohnt, spezielle Funktionen (oder besser Methoden) anzulegen, die gleich die passenden Dinge zurückliefern. Also meinetwegen: function getNewestEntries($amount);
 
hier bitte:

PHP:
<?php
$server = "localhost";
$mysqlDb = "db1";
$mysqlUser = "UserXY";
$mysqlUserPassword = "password";

function getRows($table, $fields, $where){
   //zum Server verbinden:    
   mysql_connect($server, $mysqlUser, $mysqlUserPassword) or die(mysql_error());

    //Datenbank auswählen:
    mysql_select_db($mysqlDb) or die(mysql_error());

    //Das Result vom Server abfragen:
    $result = mysql_query("SELECT $fields FROM $table WHERE $where")or die(mysql_error());

    //Result zeilenweise in das Array $rows speichern:
    while($row = mysql_fetch_assoc($result)) {
        $rows[] = $row;
    }

    //Das Array $rows zurück geben:
    return $rows;
}
?>

Der obige Code steht unter der GNU General Public License version 2 Einzusehen unter GNU GPL v2 oder http://www.gnu.org/licenses/old-licenses/gpl-2.0.html

h
appy hacking, Mario
 
Lassen sich diese drei Schritte nicht verkürzen in zwei oder anders vereinfachen? Nervt irgendwie das fünf mal in einer Datei stehen zu haben...
Kannst natürlich auch einfach statt
PHP:
$sql = "SELECT irgendwas FROM irgendwo";
$result = mysql_query($sql);
$row = mysql_fetch_assoc($result);
es so schreiben:
PHP:
$row = mysql_fetch_assoc(mysql_query("SELECT irgendwas FROM irgendwo"));

Auch wenn eine Funktion auch eine brauchbare Variante ist.
 
Noch einige Hinweise/Tipps:

Deine Funktion erwartet escapte Eingaben. Escaping-Befehle setzen eine bestehende Verbindung voraus. Wenn du die Verbindung aber erst in der Funktion aufbaust, kannst du vorher nicht korrekt escapen.

So richtig geschickt ist es ohnehin nicht, vor jeder Query die Verbindung neu aufzubauen.

Sinnvoller wäre:

PHP:
function getRows($dbh, $table, $fields, $where){/*...*/}

$dbh wäre die Ressourcenkennung, die mysql_connect zurückgibt. Du könntest dir so auch sparen, die Konfigurationsdateien global in den Funktionsscope ziehen zu müssen, was nämlich auch ziemlich unsauber ist. Global ist immer schlecht, weil es verdeckt, welche Werte für eine Funktion nun wirklich relevant sind. Wenn die Funktionsrückgabe vom Status von Variablen abhängig ist, die nicht als Parameter übergeben werden, willst du eigentlich ein Objekt, das diesen Status über Instanzvariablen repräsentieren kann.

Aus dem Kopf, schematisch und ohne Fehlerbehandlung:

PHP:
class MyDbStuff
{
    protected $config;
    protected $dbh;

    public function __construct($config)
    {
        $this->config = $config;
        $this->dbh = mysql_connect($config['host'], ...);
        mysql_select_db($config['db'], $this->dbh);
    }

    public function getRows($table, $fields, $where)
    {
        mysql_query(..., $this->dbh);
    }
}

Eine Fehlerbehandlung, die einfach das Script komplett abbricht, ist auch nicht so das Gelbe vom Ei. Da könntest du über Exceptions nachdenken.
 
1)
So richtig geschickt ist es ohnehin nicht, vor jeder Query die Verbindung neu aufzubauen.
Falsch bitte lesen: PHP: mysql_connect - Manual Es wird nur eine neue Verbindung aufgebaut wenn keinen bestehende Verbindung dieser entspricht (4. parameter).

2)
wenn ich diese function mit meinen parametern aufrufe
PHP:
getRows('user', 'user_name', 'user_id = \'1\'')
werde ich sie wohl kaum escapen! Da ICH sie gesetzt habe. Wozu sollte ich Strings die escapen die ich geschrieben habe?? Escapen Sie jeden String den Sie hardcoded schreiben?

3) bitte erläutern Sie genauer genauer was Sie mit:
Global ist immer schlecht, weil es verdeckt, welche Werte für eine Funktion nun wirklich relevant sind.
meinen!
ich weiß ganz genau welche Daten für meine Function relevant sind! siehe -->
PHP:
$server = "localhost";
$mysqlDb = "db1";
$mysqlUser = "UserXY";
$mysqlUserPassword = "password";

function getRows($table, $fields, $where){
   global $server, $mysqlDb, $mysqlUser, $mysqlUserPassword;
   //zum Server verbinden:    
   mysql_connect($server, $mysqlUser, $mysqlUserPassword) or die(mysql_error());

    //Datenbank auswählen:
    mysql_select_db($mysqlDb) or die(mysql_error());

    //Das Result vom Server abfragen:
    $result = mysql_query("SELECT $fields FROM $table WHERE $where")or die(mysql_error());

    //Result zeilenweise in das Array $rows speichern:
    while($row = mysql_fetch_assoc($result)) {
        $rows[] = $row;
    }

    //Das Array $rows zurück geben:
    return $rows;
}
und zwar $server, $mysqlDb, $mysqlUser und $mysqlUserPassword. Also bitte, was ist daran verdeckt?

verzeihen Sie mir bitte wenn ich mich provozierend anhöre das ist nicht so gemeint!
 
Können wir nicht beim Du bleiben?

Es wird nur eine neue Verbindung aufgebaut wenn keinen bestehende Verbindung dieser entspricht (4. parameter).

Okay, das mit der Verbindung habe ich nicht gewusst. Dennoch ist es meines Erachtens unnötig, diese beiden Befehle (mysql_connect und mysql_select_db) immer wieder aufzurufen. Ich meine, gut, du kannst vermutlich argumentieren, dass du so innerhalb eines Scriptdurchlaufs auch Verbindungen zu verschiedenen Datenbanken oder gar DB-Servern aufbauen kannst, indem du die globalen Variablen zwischen verschiedenen Aufrufen der Funktion anpasst. Ich finde das bloß nicht intuitiv. Ich würde wie gesagt die Verbindungskennung an die Funktion übergeben und außerhalb die Verbindung aufbauen.

Wozu sollte ich Strings die escapen die ich geschrieben habe?? Escapen Sie jeden String den Sie hardcoded schreiben?

Was aber, wenn du einen variablen Wert in die Query einfügen möchtest oder gar ganz konkret eine Nutzereingabe (beispielsweise aus $_POST)?


Ich finde gerade leider keinen Ansatz, das verständlich darzustellen, deshalb nur zwei Links:

- Global Variables Are Bad
- The Clean Code Talks - "Global State and Singletons" - YouTube



Vielleicht noch ganz kurz: Abhängigkeiten werden verdeckt, weil potenziell jede Zeile eine global-Deklaration enthalten kann. Das heißt, ich muss unter Umständen die gesamte Funktionssuite (oder was auch immer) durchgehen, um zu wissen, welche globalen Variablen genutzt werden. Optimaler ist es, allein an der Funktionssignatur function f($x) erkennen zu können, welche Angaben eine Funktion erwartet. Ich weiß, das ist nicht sonderlich überzeugend dargestellt. Mir fehlt gerade ein sinnvolles Beispiel oder so.
 
ok bleiben wir bei du :)

stimmt für den Fall das man über post oder get den Abfragestring ändert aber dafür ist die function so oder so nicht ausgelegt da ich die gesamte WHERE-Einschränkung übergebe daher kann ich sie auch nicht mehr in der function escapen --> nachteil = "daten müssen vorher escapt werden" (aus meiner Sicht nicht so schlimm muss ich halt in $where schon escapte Werte übergeben wo anders ist es meines Erachtens auch nicht sinnvoll dynamische werte zu verwenden)...

zu dem global Problem:

wenn ich die function verwende verwende ich sie ja mit dem ziel nicht soviel code schreiben zu müssen deswegen wäre mein Ansatz einmal die Konfiguration zu schreiben und immer wieder zu verwenden aber ich muss dir auch recht geben. ich habe den Fall das ich eine andere Verbindung verwenden will nicht bedacht. also am schönsten (finde ich) wäre:

PHP:
$server = "localhost";
$mysqlDb = "db1";
$mysqlUser = "UserXY";
$mysqlUserPassword = "password";

function getRows($table, $fields, $where, $mysqlDb = null, $server  = null, $mysqlUser  = null, $mysqlUserPassword = null){

   if($mysqlDb == null){
      global $mysqlDb;
   }
   if($server == null){
      global $server;
   }
   if($mysqlUser== null){
      global $mysqlUser;
   }
   if($mysqlUserPassword == null){
      global $mysqlUserPassword;
   }

   //zum Server verbinden:    
   mysql_connect($server, $mysqlUser, $mysqlUserPassword) or die(mysql_error());

    //Datenbank auswählen:
    mysql_select_db($mysqlDb) or die(mysql_error());

    //Das Result vom Server abfragen:
    $result = mysql_query("SELECT $fields FROM $table WHERE $where")or die(mysql_error());

    //Result zeilenweise in das Array $rows speichern:
    while($row = mysql_fetch_assoc($result)) {
        $rows[] = $row;
    }

    //Das Array $rows zurück geben:
    return $rows;
}
 
aichingm schrieb:
muss ich halt in $where schon escapte Werte übergeben

Puh, PHP raubt mir alle meine Argumente. :D

Nein, Spaß beiseite: Ich meinte ja, dass mysql_real_escape_string eine bestehende DB-Verbindung benötigt und es deshalb bei deiner Funktion nicht möglich ist, bereits vor dem ersten Aufruf zu escapen (weil keine existierende DB-Verbindung). Das stimmt auch. Nur versucht mysql_real_escape_string im Zweifel eine Verbindung aufzubauen, indem die Funktion mysql_connect ohne Argumente aufgerufen wird. (Die Daten werden dann aus der PHP-Konfiguration geholt, siehe Doku zu mysql_connect.) Meine lokale Standard-Konfiguration scheint auszureichen, um auch tatsächlich auf diese Weise eine Verbindung auf die Reihe zu kriegen. Ich musste für einen Testcase gezielt die Konfiguration überschreiben.

PHP:
<?php

error_reporting(-1);

$_GET['foo'] = 'bar"';
ini_set('mysql.default_host', 'dies-existiert-nicht');

$fooEscaped = mysql_real_escape_string($_GET['foo']);

var_dump($fooEscaped);

Na ja, das ist jedenfalls ein Problem. Default-Angaben könnten falsch gesetzt sein oder es soll zu einem anderen Server verbunden werden, der unter einer anderen MySQL-Version läuft, sodass unter Umständen für die falsche Version escapet wird (etwas akademisches Argument).

Es ist jedenfalls so, dass die Escape-Funktionalität an der Datenbankverbindung hängt und auch nur dann zuverlässig funktionieren kann (etwa wegen Verbindungs-Charset oder speziellen Eigenheiten bestimmter DB-Versionen). Deshalb ist addslashes auch keine Alternative dazu.

zu dem global Problem:

wenn ich die function verwende verwende ich sie ja mit dem ziel nicht soviel code schreiben zu müssen deswegen wäre mein Ansatz einmal die Konfiguration zu schreiben und immer wieder zu verwenden aber ich muss dir auch recht geben. ich habe den Fall das ich eine andere Verbindung verwenden will nicht bedacht. also am schönsten (finde ich) wäre:

Du hattest die Funktion so geschrieben, dass sie nicht für sich allein funktioniert, sondern von einem weiteren Zustand abhängt (vier globale Variablen müssen gesetzt sein). Die Funktion braucht also zum Arbeiten zum einen die Eingabeparameter und zum anderen diesen Zustand.

Da du global einsetzt, muss dieser Zustand im globalen Raum abgelegt werden und kann deshalb nur in einer Ausprägung gleichzeitig existieren, weil die notwendigen Variablen eben nur an einer einzigen Stelle im globalen Raum liegen. Wenn die Anwendung nun mit zwei Datenbanken kommunizieren möchte (wieder etwas schwieriges Szenario, aber ab und an kommt das durchaus vor), müsste sie den globalen Zustand vor dem Aufruf der Funktion immer auf die jeweiligen Werte für eine der beiden Datenbanken bringen.

Die große Frage dazu: Wie macht man das?

Vielleicht so:

PHP:
function setDb($which)
{
    global $server, $mysqlDb, $mysqlUser, $mysqlUserPassword;

    if ($which === 1) {
        $server            = "localhost";
        $mysqlDb           = "db1";
        $mysqlUser         = "UserXY";
        $mysqlUserPassword = "password";
    } else if ($which === 2) {
        $server            = "external-host";
        $mysqlDb           = "db_xy";
        $mysqlUser         = "Lucas Barrios";
        $mysqlUserPassword = "bvb09ole";
    }
}

$server            = null;
$mysqlDb           = null;
$mysqlUser         = null;
$mysqlUserPassword = null;

setDb(1); // Initialisierung

Per setDb()-Funktion kann nun der Zustand für getRows() unkompliziert gesetzt werden. (Außerdem können wir die Daten an einer einzigen Stelle zentral anpassen, was natürlich ebenfalls praktisch ist.) Zu Beginn des Scripts setzen wir ihn auf die Daten von DB 1.

Allerdings muss nun vor jedem Aufruf von getRows() einmal das passende setDb() aufgerufen werden, weil wir uns nicht sicher sein können, dass unser globaler Zustand gerade passend für die gewünschte DB steht.

PHP:
setDb(2);
$data = getRows(...);

Wenn diese beiden Funktionen nun ohnehin nur noch zusammen aufgerufen werden können, warum dann nicht setDb() in getRows() setzen und die DB-Nummer als ersten Parameter übergeben?

PHP:
$data = getRows(2, ...);

Und wo ich so darüber nachdenke: Es braucht auch die globalen Variablen nicht mehr, denn wir können das jetzt einfach so machen:

PHP:
function setDb($which)
{
    if ($which === 1) {
        $server            = "localhost";
        $mysqlDb           = "db1";
        $mysqlUser         = "UserXY";
        $mysqlUserPassword = "password";
    } else if ($which === 2) {
        $server            = "external-host";
        $mysqlDb           = "db_xy";
        $mysqlUser         = "Lucas Barrios";
        $mysqlUserPassword = "bvb09ole";
    }

    return array($server, $mysqlDb, $mysqlUser, $mysqlUserPassword);
}

function getRows($dbNumber, $table, $fields, $where){
    list($server, $mysqlDb, $mysqlUser, $mysqlUserPassword) = setDb($dbNumber);

    /* ... */
}

(Da werden die globalen Variablen allerdings gewissermaßen auch bloß in einer globalen Funktion versteckt.)

Oder auch gleich so:

PHP:
$db1 = array(
    'server'            => "localhost",
    'mysqlDb'           => "db1",
    'mysqlUser'         => "UserXY",
    'mysqlUserPassword' => "password"
);

$db2 = array(
    'server'            => "external-host",
    'mysqlDb'           => "db_xy",
    'mysqlUser'         => "Lucas Barrios",
    'mysqlUserPassword' => "bvb09ole"
);

function getRows($dbData, $table, $fields, $where){
    list($server, $mysqlDb, $mysqlUser, $mysqlUserPassword) = $dbData;

    /* ... */
}

Noch mal kurz zusammengefasst: Das Problem bestand darin, die Zugangsdaten von zwei Datenbank-Verbindungen abwechselnd in einen einzigen globalen Zustand zu schreiben, den die getRows()-Funktion zum Arbeiten benötigt. Das konnte gelöst werden, indem eine Austausch-Funktion hinzugefügt wurde, die auf Angabe einer Nummer jeweils die passenden Daten für eine Verbindung in den globalen Zustand geschrieben hat.

Nun war allerdings nicht klar, welche Daten gerade im globalen Zustand stehen, weshalb die Wechsel-Funktion immer vor dem Aufruf von getRows() aufgerufen werden musste. Wenn die beiden Funktionen aber ohnehin nur zusammen aufgerufen werden können, lassen sie sich auch zu einer zusammenfassen, der dann benötigte Sets von Zustandsdaten gleich als Parameter übergeben werden können, was die Verwendung vom global-Keyword unnötig macht.

Jetzt haben wir also in $db1 und $db2 jeweils einen Zustand gekapselt, der an eine Funktion übergeben werden kann. Die Funktion ergibt für sich genommen nur dann Sinn, wenn ihr diese Daten zur Verfügung gestellt werden. Sie ist also ganz eng mit diesen Zustandsdaten-Sets verknüpft.

Wenn sie aber ohnehin nur mit so einem Set funktioniert, warum packen wir sie nicht gleich mit in das Set rein und sparen den Schritt, die Zustandsdaten immer „indirekt“ von Außen übergeben zu müssen?

PHP:
class DbConnection
{
    protected $server;
    protected $mysqlDb;
    protected $mysqlUser;
    protected $mysqlUserPassword;

    public function __construct($server, $mysqlDb, $mysqlUser, $mysqlUserPassword)
    {
        $this->server = $server;
        $this->mysqlDb = $mysqlDb;
        $this->mysqlUser = $mysqlUser;
        $this->mysqlUserPassword = $mysqlUserPassword;
    }

    public function getRows($table, $fields, $where)
    {
        //zum Server verbinden:
        mysql_connect($this->server, $this->mysqlUser, $this->mysqlUserPassword) or die(mysql_error());

        //Datenbank auswählen:
        mysql_select_db($this->mysqlDb) or die(mysql_error());

        //Das Result vom Server abfragen:
        $result = mysql_query("SELECT $fields FROM $table WHERE $where")or die(mysql_error());

        //Result zeilenweise in das Array $rows speichern:
        while($row = mysql_fetch_assoc($result)) {
            $rows[] = $row;
        }

        //Das Array $rows zurück geben:
        return $rows;
    }
}

$db1 = new DbConnection('localhost', 'db1', 'UserXY', 'password');
$db2 = new DbConnection('external-host', 'db_xy', 'Lucas Barrios', 'bvb09ole');

$data = $db1->getRows('user', 'name, age, location', 'userId = 1');

Damit haben wir dann die Objektorientierung erfunden. Jedes Objekt verfügt über einen lokalen Zustand und über Funktionen (in diesem Kontext „Methoden“ genannt), die diesen Zustand nutzen können, ohne dass er in irgendeiner Form übergeben werden muss. Wir haben ein hübsches, kleines Paket, das alles beinhaltet, was es zum Arbeiten braucht, und das wir beliebig häufig mit anderen Daten neu erstellen können, weil es seinen Zustand eben nicht global verwaltet, sondern lokal als Teil des Pakets.

Als willkommener Nebeneffekt können die Objekte durchsetzen, dass ihr jeweiliger Zustand „existiert“. Die Methode getRows() kann hier nicht aufgerufen werden, ohne dass die entsprechenden Verbindungsdaten gesetzt sind.
 
Zurück
Oben