Sql Dynamisch aufbauen, aber sicher

Von: , 30.01.2012 17:05 Uhr

Liebe/-r Experte/-in,
ich möchte euch noch mal mit einer Frage belästigen. ;)

erstmal zum Szenario:
Ich habe eine Datenbank ua mit der Tabelle Beiträge.
Über Filter wähle ich das anzuzeigende Quartal und die anzuzeigenden Jahre. (checkboxen). Über eine verwaltungsvariable lege ich fest wie weit die Jahre zurückgehen können sollen.

<code>
$sql_quittungen=sprintf("select beiträge.* from beitraege where
(
quarter(quittungen.datum)=%s)or
quarter(quittungen.datum)=%s)or
quarter(quittungen.datum)=%s)or
quarter(quittungen.datum)=%s)
}
and
(
Date_Format(quittungen.datum,'%%Y') = %s)or
Date_Format(quittungen.datum,'%%Y') = %s)or
Date_Format(quittungen.datum,'%%Y') = %s)or
Date_Format(quittungen.datum,'%%Y') = %s)
)
and
(
mysql_real_escape_string($array_quartal[0]),
mysql_real_escape_string($array_quartal[1]),
mysql_real_escape_string($array_quartal[2]),
mysql_real_escape_string($array_jahre[0]),
mysql_real_escape_string($array_jahre[1]),
));
</code>

Soll heissen:
Wenn checkbox quartal 1 gesetzt ist, zeige mir alle Beiträge aus dem ersten Quartal.
Wenn checkbox quartal 2 gesetzt ist, zeige mir alle Beiträge aus dem zweiten Quartal.
Wenn checkbox 2012 gesetzt ist gesetzt ist, zeige mir alle Beiträge aus 2012 und dem Quartal xy.
usw.

Also habe ich derzeit die Anzahl der auswählbaren Jahre auf 4 festgelegt. Also ab 2009 bis 2012 kann alles ausgewählt werden. Im Array $jahreanzahl_array sind die Werte 999 oder Jahreszahl.

Nun möchte ich die Anzahl der zurückreichenden Jahre dynamisch machen. Also sowas wie eine Konfigurationsvariable $jahrezurueck.
=> $jahrezurueck =5. Dann kann der User die letzten 4 jahre + das aktuelle auswählen. Bei $jahrezurueck=3 nur die letzten 2 Jahre + das aktuelle Jahr. usw.

Eine Idee ist das Ganze mit einer for-schleife zu machen und dann die sql-Abfrage via string Operationen zusammen zu bauen. (Quartal wegen der Übersichtlichkeit weggelassen). Die forschleife geht von 0 bis count(jahrezurueck_array).

<code>

$sql_teil1= "select * from beitrage where ";
$sql_string_tei2="";
for($d=0;$d<=$jahrezurueck;$d++)
{
$schleifenjahr=$aktuellesjahr-$d;
$sql_string_tei2.=" if('999'=".$jahreanzahl_array[$d].",0,Date_Format(beitraege.datum,'%Y') = ".$schleifenjahr.")or";
}
$sql_string_tei2=substr($sql_string_tei2,0,-2);
$sql = $sql_teil1.$sql_teil2;
</code>

Das sind reine strings und funktioniert. Alternativ habe ich probiert nur die gesetzten Variablen in ein Array zu packen und dies dann zu durchlaufen.

Die Abfrage ist nur ein string. Funktioniert.
Nun zur Frage:
Aber wie kriege ich das mit dem mysql_real_escape_string und den Platzhaltern hin?
Oder ist der Denkansatz falsch?

Also das sowas rauskommt:
(gesetzt ist 1., 2. und 3. Quartal
2011 und 2012 und 2009
$array_jahre=array(2012,2009);
$array_quartal = array(1,2,3);)
<code>
...
and
(
quarter(quittungen.datum)=%s)or
quarter(quittungen.datum)=%s)or
quarter(quittungen.datum)=%s)
}
and
Date_Format(quittungen.datum,'%%Y') = %s)or
Date_Format(quittungen.datum,'%%Y') = %s))
group by kategorie_id having sum(wert)!='NULL'",
mysql_real_escape_string($array_quartal[0]),
mysql_real_escape_string($array_quartal[1]),
mysql_real_escape_string($array_quartal[2]),
mysql_real_escape_string($array_jahre[0]),
mysql_real_escape_string($array_jahre[1]),
);
</code>
Eine Überlegung geht gegen implode und "or"-Verknüpfung.
Aber echo "mysql_real_escape_string"; funktioniert so leider nicht. ;)

17 Antworten zu dieser Frage

  1. Antwort von nach 35 Minuten 0 hilfreich
    Re: Sql Dynamisch aufbauen, aber sicher

    also

    du brauchst foreach.
    vorher durch die arrays durch und es passt.
    dann arraygrösse bestimmen, zb 5
    deine query ist dann:
    "select bla from bl where quartal in (".substr(str_repeat("%s,",$anz),-1).")"

    dann mit sprintf aufbauen, fertig. sprintf n8mmt den array als 2. argument.

    so sollt das klappen.

  2. Antwort von nach 38 Minuten 0 hilfreich
    Re: Sql Dynamisch aufbauen, aber sicher

    ups... foreach mit dem mysql_real_escape_string wie hier:

    In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.
    <?php
    $arr = array(1, 2, 3, 4);
    foreach ($arr as &$value) {
    $value = mysql_real_escape_string($value);
    }
    // $arr is now array(2, 4, 6, 8)

    • Antwort von nach 46 Minuten 0 hilfreich
      Re^2: Sql Dynamisch aufbauen, aber sicher

      Hi,
      das war ja schnell. Danke erstmal!
      habe ich es richtig verstanden, dass
      $value = mysql_real_escape_string($value);

      innerhalb der foreach Schleife die Ausgaben verkettet?

      Denn dann müsste ich für die Jahre noch eine verschachtelte foreach reinbasteln, damit
      #
      mysql_real_escape_string($array_quartal[0]),
      mysql_real_escape_string($array_quartal[1]),
      mysql_real_escape_string($array_quartal[2]),
      mysql_real_escape_string($array_jahre[0]),
      mysql_real_escape_string($array_jahre[1])
      #
      das dabei rauskommt. (Bei drei gewählten Quartalen und 2 gesetzten Jahren.)

    • Antwort von nach 55 Minuten 0 hilfreich
      Re^2: Sql Dynamisch aufbauen, aber sicher

      PS
      Ich habe es nun mal eingesetzt.Und der value hat nun den Wert der gesetzten Quartale (1,2,3)
      Sind dass denn schon die escapten Werte?

      Weil dann müsste ich nur die for-schleifen für den stringpart mit in das foreach packen, oder bin ich wieder auf der falschen fährte?

      for($p=0;$p<count($array_quartal);$p++)
      {
      $sql_teil2=$sql_teil2."quarter(quittungen.datum)=%s) or ";
      }
      $sql_teil2=substr($sql_teil2,0,-3);

      • Antwort von nach 18 Stunden 0 hilfreich
        Re^3: Sql Dynamisch aufbauen, aber sicher

        hi,
        (jetzt bin ich am PC, das andre war am handy getippt)

        Also:
        zuerst deine Arrays "cleanen":

        foreach ($array_quartal as &$value) {
        $value = mysql_real_escape_string($value);
        }
        foreach ($array_jahre as &$value) {
        $value = mysql_real_escape_string($value);
        }
        -> Jetzt sind alle Werte escapt. Alternativ könntest du auch einfach mit 1 multiplizieren :)

        jetzt SQL bauen:
        $sql= "SELECT beiträge.* FROM beitraege WHERE ";
        $sqlqu="quarter(quittungen.datum) in (".substr(str_repeat("%s,",count($array_quartal),-1).")";

        $sqlyr=" AND year(quittungen.datum) in (".substr(str_repeat("%s,",count($array_year),-1).")"


        Wie das geht:
        count($array_quartal) gibt die Anzahl der gewählten Quartale zurück. Nehmen wir an 3: Quartal 1,2,4
        Es ergibt sich (der ersetzte Teil in # #

        substr(str_repeat("%s,",count($array_quartal),-1)
        wird zu
        substr(str_repeat("%s,",#3#),-1)
        -> %s,%s,%s,

        Das kannst Du mit sprintf ersetzen, indem dudann einfach:

        weiter: Das Substr schneidet das letzte , weg
        ->
        %s,%s,%s

        Dadurch ergibt sich am Ende:
        "quarter(quittungen.datum) in (".substr(str_repeat("%s,",count($array_quartal),-1).")".
        wird zu
        "quarter(quittungen.datum) in (%s,%s,%s)

        Dasselbe fürs Jahr und voila =>

        $sql.=sprintf([E-Mail-Adresse entfernt];

        den Code hab ich nicht getestet nur getippt, sollt aber klappen.

        • Antwort von nach einem Tag 0 hilfreich
          Re^4: Sql Dynamisch aufbauen, aber sicher

          Hi,
          ich dachte, ich könnte programmieren,... Nun sitzt ich hier und habe fast nen Knoten im Hirn.

          Nunja erstmal ans entwirren.

          "quarter(quittungen.datum) in (%s,%s,%s)

          Nuss ich da nicht sowas haben wie //je nachdem wie viele Quater im Array drinne sind..?

          Ich bastel mal.

          Fettes Dankeschön!

          Oliver
          quarter(quittungen.datum)=%s)or
          quarter(quittungen.datum)=%s)

        • Antwort von nach einem Tag 0 hilfreich
          Re^4: Sql Dynamisch aufbauen, aber sicher

          Hi,
          also letztendlich hast Du mir die Lösung geben! Fettes Dankeschön.

          Kleine Änderungen waren von Nöten. sprintf nimmt kein array -> vsprintf.

          <?php /**********************dynamisch */
          echo "<br><br>Anzahl Quartale:".count($quartal_array)."<br><br>";
          echo "Anzahl jahre:".count($jahreanzahl_array)."<br><br>";
          $statischer_part = "select beitraege.* from beitraege where ";

          foreach ($quartal_array as &$value) {
          $value = mysql_real_escape_string($value);
          }
          foreach ($jahreanzahl_array as &$value) {
          $value = mysql_real_escape_string($value);
          }

          $sqlqu="quarter(beitraege.datum) in (".substr(str_repeat("%s,",count($quartal_array)),0,-1).")";
          $sqlyr=" AND year(beitraege.datum) in (".substr(str_repeat("%s,",count($jahreanzahl_array)),0,-1).")";

          echo "Dynamisch<br><br>";

          echo "Quartal Teil 1: ".$sqlqu;
          echo "<br><br>Jahr Teil 1: ".$sqlyr;

          echo "<br><br>TEst vsprintf_array: <br>".vsprintf($sqlqu,$quartal_array)."<br><br>".vsprintf($sqlyr,$jahreanzahl_array);

          $sql=$statischer_part.vsprintf($sqlqu,$quartal_array).vsprintf($sqlyr,$jahreanzahl_array);
          ?>

          Völlig flexibel und etliches kürzer!

  3. Antwort von nach 15 Stunden 0 hilfreich
    Re: Sql Dynamisch aufbauen, aber sicher

    Hallo,

    du versuchst in deiner Schleife das gesetzte Jahr per MySQL mit einer IF-Anweisung abzufragen. Der Gedanke mit der Schleife ist gut, aber besser wäre es, die IF-Amweisung in PHP zu machen, dann kannst du auch mysql_real_escape_string() einsetzen. Und es kommt nur das in den String, was reingehört.

    $sql_teil1= "select * from beitrage where ";
    $sql_string_tei2="";
    for($d=0;$d<=$jahrezurueck;$d++)
    {
    $schleifenjahr=$aktuellesjahr-$d;
    $sql_string_tei2.=(($jahreanzahl_array[$d]!=999)? " Date_Format(beitraege.datum,'%Y') = ".$schleifenjahr.")or " : null) //PHP-Version
    //$sql_string_tei2.=" if('999'=".$jahreanzahl_array[$d].",0,Date_Format(beitraege.datum,'%Y') = ".$schleifenjahr.")or"; //MySQL Version
    }
    $sql_string_tei2=substr($sql_string_tei2,0,-2);
    $sql = $sql_teil1.$sql_teil2;

    Ich hoffe es ist verständlich. Sonst schreib wieder ;)

  4. Antwort von nach 15 Stunden 0 hilfreich
    Re: Sql Dynamisch aufbauen, aber sicher

    Ach so, ich hatte vergessen mysql_real_escape_string einzusetzen:

    $sql_teil1= "select * from beitrage where ";
    $sql_string_tei2="";
    for($d=0;$d<=$jahrezurueck;$d++)
    {
    $schleifenjahr=$aktuellesjahr-$d;
    $sql_string_tei2.=(($jahreanzahl_array[$d]!=999)? " Date_Format(beitraege.datum,'%Y') = ".mysql_real_escape_string($schleifenjahr).")or " : null) //PHP-Version
    //$sql_string_tei2.=" if('999'=".$jahreanzahl_array[$d].",0,Date_Format(beitraege.datum,'%Y') = ".$schleifenjahr.")or"; //MySQL Version
    }
    $sql_string_tei2=substr($sql_string_tei2,0,-2);
    $sql = $sql_teil1.$sql_teil2;

  5. Antwort von nach einem Tag 0 hilfreich
    Re^2: Sql Dynamisch aufbauen, aber sicher

    Hi Alex,
    also ich habe nun noch etwas rumprobiertb und auch Deine Anregung zumindest erstmal gedanklich übernommen, die Arrays nicht ab zufragen, sondern wirklich nur mit den gesetzten Werten zu füllen. Also nicht ein Array mit 5 jahren und davon 2 auf 999 (=nicht gesetzt), sondern dann nur 3 Werte im Array.

    Hm, was macht der Parrt:

    ##$sql_string_tei2.=(($jahreanzahl_array[$d]!=999)?9)? " Date_Format(beitra##

    Insbesondere das "?"

    Da kriege ich beim lesen einen Knoten im Hirn...
    Da wird doch eine Funktion wie ein String behandelt, oder?
    Gruß
    Oliver

    • Antwort von nach einem Tag 0 hilfreich
      Re^3: Sql Dynamisch aufbauen, aber sicher

      Hi,

      also hier handelt es sich um eine verkürzte IF-Anweisung, schaue bitte hier: http://davidwalsh.name/php-shorthand-if-else-ternary...

      In deinem Beispiel wird der Teil hinter dem "?" dann an $sql_string_tei2 angehangen, wenn die Bedingung $jahreanzahl_array[$d]!=999 zutrifft. Ansonsten wird der Teil hinter dem ":" angehangen, nämlich nichts (null). Sowas macht man immer dann, wenn man innerhalb eines Strings Entscheidungen treffen muss. Das sollte Dir bei deinem Problem eigentlich helfen.

      • Antwort von nach einem Tag 0 hilfreich
        Re^4: Sql Dynamisch aufbauen, aber sicher

        Wenns interessiert:

        Ich habe die Lösung mit Hilfe von Styx:
        <?php /**********************dynamisch */
        echo "<br><br>Anzahl Quartale:".count($quartal_array)."<br><br>";
        echo "Anzahl jahre:".count($jahreanzahl_array)."<br><br>";
        $statischer_part = "select beitraege.* from beitraege where ";

        foreach ($quartal_array as &$value) {
        $value = mysql_real_escape_string($value);
        }
        foreach ($jahreanzahl_array as &$value) {
        $value = mysql_real_escape_string($value);
        }

        $sqlqu="quarter(beitraege.datum) in (".substr(str_repeat("%s,",count($quartal_array)),0,-1).")";
        $sqlyr=" AND year(beitraege.datum) in (".substr(str_repeat("%s,",count($jahreanzahl_array)),0,-1).")";

        echo "Dynamisch<br><br>";

        echo "Quartal Teil 1: ".$sqlqu;
        echo "<br><br>Jahr Teil 1: ".$sqlyr;

        echo "<br><br>TEst vsprintf_array: <br>".vsprintf($sqlqu,$quartal_array)."<br><br>".vsprintf($sqlyr,$jahreanzahl_array);

        $sql=$statischer_part.vsprintf($sqlqu,$quartal_array).vsprintf($sqlyr,$jahreanzahl_array);
        ?>

        Danke nochmals für Deine Hilfe! So gehts wesentlich kürzer, dynmaisch und sicher!

          • Antwort von nach einem Tag 0 hilfreich
            Re: Sql Dynamisch aufbauen, aber sicher

            ich würde die POST oder GET Abfragen aus dem Formular zu Arrays zusammenfassen und diese dann wie folgt abrfagen

            <code>
            select beiträge.* from beitraege
            where
            quarter(quittungen.datum) IN '".$quarter_array."'
            AND YEAR(quittungen.datum) IN '".$jahr_array."'
            </code>
            ich konnte zwar nicht prüfen ob das so funktioniert, aber so ähnlich habe ich schon einiges realisiert.

            mit freundlichem Gruß
            Roland dreix
            dreix webdesign
            E-Mail: [E-Mail-Adresse entfernt]
            Webseite: http://www.dreix.de

          • Antwort von nach einem Tag 0 hilfreich
            Re^2: Sql Dynamisch aufbauen, aber sicher

            Wenns interessiert:

            Ich habe die Lösung mit Hilfe von Styx:
            <?php /**********************dynamisch */
            echo "<br><br>Anzahl Quartale:".count($quartal_array)."<br><br>";
            echo "Anzahl jahre:".count($jahreanzahl_array)."<br><br>";
            $statischer_part = "select beitraege.* from beitraege where ";

            foreach ($quartal_array as &$value) {
            $value = mysql_real_escape_string($value);
            }
            foreach ($jahreanzahl_array as &$value) {
            $value = mysql_real_escape_string($value);
            }

            $sqlqu="quarter(beitraege.datum) in (".substr(str_repeat("%s,",count($quartal_array)),0,-1).")";
            $sqlyr=" AND year(beitraege.datum) in (".substr(str_repeat("%s,",count($jahreanzahl_array)),0,-1).")";

            echo "Dynamisch<br><br>";

            echo "Quartal Teil 1: ".$sqlqu;
            echo "<br><br>Jahr Teil 1: ".$sqlyr;

            echo "<br><br>TEst vsprintf_array: <br>".vsprintf($sqlqu,$quartal_array)."<br><br>".vsprintf($sqlyr,$jahreanzahl_array);

            $sql=$statischer_part.vsprintf($sqlqu,$quartal_array).vsprintf($sqlyr,$jahreanzahl_array);
            ?>

            Danke nochmals für Deine Hilfe! So gehts wesentlich kürzer, dynmaisch und sicher!

          • Antwort von nach 2 Tagen 0 hilfreich
            Re^3: Sql Dynamisch aufbauen, aber sicher

            na geht doch. freut mich wenn es klappt.
            und ich habe auch was gelernt. das mit dem quarter kannte ich noch nicht. brauch ich zwar im moment nicht, aber wer weiß.
            dann bis zum nächsten mal.

    • Antwort von nach einem Tag 0 hilfreich
      Re: Sql Dynamisch aufbauen, aber sicher

      Hi Oliver,

      Stichworte zur Vereinfachung:
      SQL: IN (...) benutzen statt umständlicher OR-Verknüpfungen. Einfacher im SQL-Code und in der Formulierung der Bedingungen aus einem Array (implode ist hier schon der sinnvolle Ansatz).
      SQL: Wenn du QUARTER benutzt, dann geht auch YEAR.
      PHP: mit array_map() kann eine Funktion (die escape-Funktion) auf alle Elemente eines Array angewendet werden.

      Du sprichst von Tabelle "beiträge", im Code aber 'quittungen'. Ich nehme an, es geht um ein und dieselbe Tabelle. Dann müsste dieser Bsp.-Code funktionieren, ein paar Erläuterungen in den Kommentaren.

      // "Konfig"-Variable und weitere Hilfsvariablen
      $maxYearsBack = 5;
      $minYear = date('Y') - $maxYearsBack;
      $minDate = "$minYear-01-01";
      // MySQL Connection sollte schon existieren,
      // bevor mysql_real_escape_string() aufgerufen wird
      $dbConnection = mysql_connect($host, $user, $password);
      // array_quartal enthält ein oder mehrere vom User gewählte Berichts-Quartale.
      // Sicherheitshalber leere Elemente entfernen:
      foreach(array_keys($array_quartal) as $qKey) {
      if (! $array_quartal[$qKey]) unset($array_quartal[$qKey]);
      }
      // .. und dann per array_map escapen
      $array_quartal = array_map('mysql_real_escape_string', $array_quartal);
      // IN Bedingung bauen: Sicherheitshalber - auch wenn's Zahlen sind - in 
      // einfachen Anführungszeichen. Der implode-String beinhaltet die 
      // Anführungszeichen zwischen den Elementen. Vor dem ersten und nach dem
      // letzten Element muss ebenfalls ein Anführungszeichen stehen:
      $quarterInCondition = "('" . implode("', '",  $array_quartal) . "')";
      // Dasselbe für array_jahre:
      foreach(array_keys($array_jahre) as $yKey) {
      if (! $array_jahre[$yKey]) unset($array_jahre[$yKey]);
      }
      $array_jahre = array_map('mysql_real_escape_string', $array_jahre);
      $yearInCondition = "('" . implode("', '",  $array_jahre) . "')";
      // Query bauen
      $sql = "SELECT * FROM quittungen 
      WHERE datum >= '$minDate'
      AND QUARTER(datum) IN $quarterInCondition
      AND YEAR(datum) IN $yearInCondition";
      


      Gruß
      Thomas

      • Antwort von nach einem Tag 0 hilfreich
        Re^2: Sql Dynamisch aufbauen, aber sicher

        Hi Thomas,
        Wenns interessiert:

        Ich habe die Lösung mit Hilfe von Styx:
        <?php /**********************dynamisch */
        echo "<br><br>Anzahl Quartale:".count($quartal_array)."<br><br>";
        echo "Anzahl jahre:".count($jahreanzahl_array)."<br><br>";
        $statischer_part = "select beitraege.* from beitraege where ";

        foreach ($quartal_array as &$value) {
        $value = mysql_real_escape_string($value);
        }
        foreach ($jahreanzahl_array as &$value) {
        $value = mysql_real_escape_string($value);
        }

        $sqlqu="quarter(beitraege.datum) in (".substr(str_repeat("%s,",count($quartal_array)),0,-1).")";
        $sqlyr=" AND year(beitraege.datum) in (".substr(str_repeat("%s,",count($jahreanzahl_array)),0,-1).")";

        echo "Dynamisch<br><br>";

        echo "Quartal Teil 1: ".$sqlqu;
        echo "<br><br>Jahr Teil 1: ".$sqlyr;

        echo "<br><br>TEst vsprintf_array: <br>".vsprintf($sqlqu,$quartal_array)."<br><br>".vsprintf($sqlyr,$jahreanzahl_array);

        $sql=$statischer_part.vsprintf($sqlqu,$quartal_array).vsprintf($sqlyr,$jahreanzahl_array);
        ?>

        Danke nochmals für Deine Hilfe! So gehts wesentlich kürzer, dynmaisch und sicher!

        Jetzt auf diese Frage antworten.