php - this-> stmt this-> dbh-> prepare($ query);




هل يمكنني ربط مصفوفة بشرط IN()؟ (14)

أخذته إلى أبعد قليلاً للحصول على الإجابة أقرب إلى السؤال الأصلي لاستخدام العناصر النائبة لربط المعلمات.

سيكون لهذه الإجابة لجعل اثنين من الحلقات من خلال المصفوفة لاستخدامها في الاستعلام. ولكنه يعمل على حل مشكلة الحصول على عناصر نائبة للعمود للحصول على استعلامات أكثر انتقائية.

//builds placeholders to insert in IN()
foreach($array as $key=>$value) {
    $in_query = $in_query . ' :val_' . $key . ', ';
}

//gets rid of trailing comma and space
$in_query = substr($in_query, 0, -2);

$stmt = $db->prepare(
    "SELECT *
     WHERE id IN($in_query)";

//pind params for your placeholders.
foreach ($array as $key=>$value) {
    $stmt->bindParam(":val_" . $key, $array[$key])
}

$stmt->execute();

أنا فضولي لمعرفة ما إذا كان من الممكن ربط صفيف من القيم لعنصر نائب باستخدام PDO. تحاول حالة الاستخدام هنا تمرير صفيف من القيم للاستخدام مع شرط IN() .
أنا لست جيدًا جدًا في التوضيح ، لذلك إليك بعض psuedocode للتوضيح. أود أن أكون قادرًا على فعل شيء كالتالي:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

ويكون ربط PDO واقتبس جميع القيم في الصفيف.

في هذه اللحظة أفعل:

<?php
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
foreach($ids as &$val)
    $val=$db->quote($val); //iterate through array and quote
$in = implode(',',$ids); //create comma separated list
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$in.')'
);
$stmt->execute();
?>

ما هي المهمة بالتأكيد ، ولكن أتساءل فقط إذا كان هناك حل مضمّن؟


أدرك أيضًا أن هذا الخيط قديم ولكني واجهت مشكلة فريدة من حيث ، أثناء تحويل برنامج تشغيل mysql الذي تم إيقافه قريبًا إلى برنامج تشغيل PDO ، كان علي أن أصنع وظيفة يمكن أن تبني ديناميكيًا كلا من المعلمات العادية و INs من نفس مجموعة المعلمة. لذلك بنيت بسرعة هذا:

/**
 * mysql::pdo_query('SELECT * FROM TBL_WHOOP WHERE type_of_whoop IN :param AND siz_of_whoop = :size', array(':param' => array(1,2,3), ':size' => 3))
 *
 * @param $query
 * @param $params
 */
function pdo_query($query, $params = array()){

    if(!$query)
        trigger_error('Could not query nothing');

    // Lets get our IN fields first
    $in_fields = array();
    foreach($params as $field => $value){
        if(is_array($value)){
            for($i=0,$size=sizeof($value);$i<$size;$i++)
                $in_array[] = $field.$i;

            $query = str_replace($field, "(".implode(',', $in_array).")", $query); // Lets replace the position in the query string with the full version
            $in_fields[$field] = $value; // Lets add this field to an array for use later
            unset($params[$field]); // Lets unset so we don't bind the param later down the line
        }
    }

    $query_obj = $this->pdo_link->prepare($query);
    $query_obj->setFetchMode(PDO::FETCH_ASSOC);

    // Now lets bind normal params.
    foreach($params as $field => $value) $query_obj->bindValue($field, $value);

    // Now lets bind the IN params
    foreach($in_fields as $field => $value){
        for($i=0,$size=sizeof($value);$i<$size;$i++)
            $query_obj->bindValue($field.$i, $value[$i]); // Both the named param index and this index are based off the array index which has not changed...hopefully
    }

    $query_obj->execute();

    if($query_obj->rowCount() <= 0)
        return null;

    return $query_obj;
}

لا يزال غير اختبار ولكن المنطق يبدو أنه موجود.

آمل أن يساعد شخص ما في نفس الموقف ،

تحرير: بعد بعض الاختبارات اكتشفت:

  • لا تحب شركة "." في أسمائهم (وهو غبي نوعا ما إذا سألتني)
  • bindParam هي الدالة الخاطئة ، bindValue هي الوظيفة الصحيحة.

قانون تحريرها إلى إصدار العمل.


إذا كان العمود يحتوي على أعداد صحيحة فقط ، فمن المحتمل أن تفعل ذلك بدون العناصر النائبة وتضع معرّفات الاستعلام في الحال. عليك فقط لإلقاء جميع قيم المصفوفة إلى الأعداد الصحيحة. مثله:

$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
    "SELECT *
     FROM table
     WHERE id IN($listOfIds)"
);
$stmt->execute();

هذا لا ينبغي أن يكون عرضة لأي حقن SQL.


بالنسبة لي ، فإن الحل الجنسي هو إنشاء مجموعة ارتباطية ديناميكية واستخدامها

// a dirty array sent by user
$dirtyArray = ['Cecile', 'Gilles', 'Andre', 'Claude'];

// we construct an associative array like this
// [ ':name_0' => 'Cecile', ... , ':name_3' => 'Claude' ]
$params = array_combine(
    array_map(
        // construct param name according to array index
        function ($v) {return ":name_{$v}";},
        // get values of users
        array_keys($dirtyArray)
    ),
    $dirtyArray
);

// construct the query like `.. WHERE name IN ( :name_1, .. , :name_3 )`
$query = "SELECT * FROM user WHERE name IN( " . implode(",", array_keys($params)) . " )";
// here we go
$stmt  = $db->prepare($query);
$stmt->execute($params);

بعد الدخول في نفس المشكلة ، ذهبت إلى حل أبسط (على الرغم من أنه لا يزال غير أنيق مثل PDO::PARAM_ARRAY ):

بالنظر إلى الصفيف $ids = array(2, 4, 32) :

$newparams = array();
foreach ($ids as $n => $val){ $newparams[] = ":id_$n"; }

try {
    $stmt = $conn->prepare("DELETE FROM $table WHERE ($table.id IN (" . implode(", ",$newparams). "))");
    foreach ($ids as $n => $val){
        $stmt->bindParam(":id_$n", intval($val), PDO::PARAM_INT);
    }
    $stmt->execute();

... وما إلى ذلك وهلم جرا

لذلك إذا كنت تستخدم صفيف قيم مختلط ، فستحتاج إلى المزيد من التعليمات البرمجية لاختبار قيمك قبل تعيين معلمة الكتابة:

// inside second foreach..

$valuevar = (is_float($val) ? floatval($val) : is_int($val) ? intval($val) :  is_string($val) ? strval($val) : $val );
$stmt->bindParam(":id_$n", $valuevar, (is_int($val) ? PDO::PARAM_INT :  is_string($val) ? PDO::PARAM_STR : NULL ));

لكني لم اختبر هذا.


بما أنني أقوم بالكثير من الاستعلامات الديناميكية ، فهذه وظيفة مساعد بسيطة للغاية قمت بها.

public static function bindParamArray($prefix, $values, &$bindArray)
{
    $str = "";
    foreach($values as $index => $value){
        $str .= ":".$prefix.$index.",";
        $bindArray[$prefix.$index] = $value;
    }
    return rtrim($str,",");     
}

استخدمه على النحو التالي:

$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";

تقوم بإرجاع سلسلة :id1,:id2,:id3 وتحديث $bindArray الخاص بك من الارتباطات التي ستحتاجها عندما يحين وقت تشغيل الاستعلام الخاص بك. سهل!


طريقة نظيفة جدا ل postgres تستخدم صفيف postgres ("{}"):

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));

عندما يكون لديك معلمة أخرى ، يمكنك فعل هذا:

$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$query = 'SELECT *
            FROM table
           WHERE X = :x
             AND id IN(';
$comma = '';
for($i=0; $i<count($ids); $i++){
  $query .= $comma.':p'.$i;       // :p0, :p1, ...
  $comma = ',';
}
$query .= ')';

$stmt = $db->prepare($query);
$stmt->bindValue(':x', 123);  // some value
for($i=0; $i<count($ids); $i++){
  $stmt->bindValue(':p'.$i, $ids[$i]);
}
$stmt->execute();

كما أعرف لا يوجد أي إمكانية لربط صفيف في بيان الشركة.

لكن يوجد حلان شائعان:

  1. استخدام المواضع الموضعية (؟ ،؟ ،؟ ،؟) أو العناصر النائبة المسماة (: id1 ،: id2 ،: id3)

    $ whereIn = implode ('،'، array_fill (0، count ($ ids)، '؟'))؛

  2. اقتبس صفيف في وقت سابق

    $ whereIn = array_map (array ($ db، 'quote')، $ ids)؛

كلا الخيارين جيدان وآمنان. أنا أفضل الثاني لأنه أقصر ويمكنني المعلمات var_dump إذا كنت بحاجة إليها. باستخدام العناصر النائبة ، يجب عليك ربط القيم وفي النهاية سيكون كود SQL الخاص بك هو نفسه.

$sql = "SELECT * FROM table WHERE id IN ($whereIn)";

والأخير والمهم بالنسبة لي هو تجنب الخطأ "عدد المتغيرات المرتبطة لا يطابق عدد الرموز المميزة"

المبدأ هو مثال رائع على استخدام العناصر النائبة الموضعية ، فقط لأنه يحتوي على تحكم داخلي على المعلمات الواردة.


لشيء سريع:

//$db = new PDO(...);
//$ids = array(...);

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);

لم يعمل حل من EvilRygy بالنسبة لي. في Postgres ، يمكنك تنفيذ حل بديل آخر:


$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (string_to_array(:an_array, ','))'
);
$stmt->bindParam(':an_array', implode(',', $ids));
$stmt->execute();

ما قاعدة البيانات التي تستخدمها؟ في PostgreSQL أحب استخدام أي (مجموعة). لذلك لإعادة استخدام المثال الخاص بك:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

للأسف هذا غير محمول.

في قواعد البيانات الأخرى ، ستحتاج إلى تكوين سحرك الخاص كما ذكر آخرون. ستحتاج إلى وضع هذا المنطق في فئة / وظيفة لجعلها قابلة لإعادة الاستخدام طوال البرنامج بالطبع. إلقاء نظرة على التعليقات على صفحة mysql_query على PHP.NET لمزيد من الأفكار حول هذا الموضوع وأمثلة على هذا السيناريو.


هل من المهم جدًا استخدام عبارة IN ؟ حاول استخدام FIND_IN_SET المرجع.

على سبيل المثال ، هناك استعلام في الشركة مثل ذلك

SELECT * FROM table WHERE FIND_IN_SET(id, :array)

ثم تحتاج فقط إلى ربط صفيف من القيم ، بفواصل مع فاصلة ، مثل هذا

$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);

وانتهى

محدث: كما أشار بعض الناس في التعليقات على هذا الجواب ، هناك بعض القضايا التي ينبغي ذكرها بشكل خارق.

  1. لا يستخدم FIND_IN_SET الفهرس في جدول ، ولا يزال غير مطبق حتى الآن - راجع هذا السجل في أداة تتبع الأخطاء MYSQL . بفضلBillKarwin للإشعار.
  2. لا يمكنك استخدام سلسلة بفاصلة بداخله كقيمة لصفيف للبحث. من المستحيل تحليل مثل هذه السلسلة في الطريق الصحيح بعد implode لأنك تستخدم رمز الفاصلة كفاصل. بفضلVaL للملاحظة.

في حالة جيدة ، إذا كنت لا تعتمد بشكل كبير على الفهارس ولا تستخدم السلاسل باستخدام فاصلة للبحث ، سيكون حلّي أسهل وأبسط وأسرع من الحلول المذكورة أعلاه.


هنا هو الحل الخاص بي. لقد قمت بتمديد فئة PDO أيضًا:

class Db extends PDO
{

    /**
     * SELECT ... WHERE fieldName IN (:paramName) workaround
     *
     * @param array  $array
     * @param string $prefix
     *
     * @return string
     */
    public function CreateArrayBindParamNames(array $array, $prefix = 'id_')
    {
        $newparams = [];
        foreach ($array as $n => $val)
        {
            $newparams[] = ":".$prefix.$n;
        }
        return implode(", ", $newparams);
    }

    /**
     * Bind every array element to the proper named parameter
     *
     * @param PDOStatement $stmt
     * @param array        $array
     * @param string       $prefix
     */
    public function BindArrayParam(PDOStatement &$stmt, array $array, $prefix = 'id_')
    {
        foreach($array as $n => $val)
        {
            $val = intval($val);
            $stmt -> bindParam(":".$prefix.$n, $val, PDO::PARAM_INT);
        }
    }
}

فيما يلي استخدام نموذج للرمز أعلاه:

$idList = [1, 2, 3, 4];
$stmt = $this -> db -> prepare("
  SELECT
    `Name`
  FROM
    `User`
  WHERE
    (`ID` IN (".$this -> db -> CreateArrayBindParamNames($idList)."))");
$this -> db -> BindArrayParam($stmt, $idList);
$stmt -> execute();
foreach($stmt as $row)
{
    echo $row['Name'];
}

اسمحوا لي أن أعرف ما هو رأيك





where-in