8

すべてのクエリを使用して、リスト内の要素の数が異なる場合bind_paramに使用したいと思います。IN(?)

ここで使用している SQLout 関数は、基本$sql_db->prepare->bind_param->execute()、、、、、->store_result()->bind_result

// the code below does not work as the query only matches on element 'a':
$locations = ('a','b','c','d','e');

SQLout ("SELECT Name FROM Users WHERE Locations IN (?)",
    array('s', $locations), array(&$usrName));


// the code below does work as a brute-force method,
// but is not a viable solution as I can't anticipate the number of elements in $locations going forward:

SQLout ("SELECT Name FROM Users WHERE Locations IN (?,?,?,?,?)",
    array('sssss', $locations[0],$locations[1],$locations[2],$locations[3],$locations[4]), array(&$usrName));

これに対するよりエレガントな解決策を思いついた人はいますか?

4

5 に答える 5

8

これは、プレースホルダーが顔に落ちる 1 つの場所です。自動エスケープを除いて、それらはほとんど文字どおり、内部的に単なる文字列置換操作です。つまり、 を持っていてWHERE Locations IN (?)を渡すと1,2,3,4

WHERE Locations IN ('1,2,3,4')  // note, it's a string, not individual comma-separated integers

論理的に同等

WHERE Locations = '1,2,3,4' // again, just a string

意図したのではなく

WHERE Locations = 1 OR Locations = 2 OR Locations = 3 OR Locations = 4

唯一の実用的な解決策は、コンマ区切りのプレースホルダー ( ?) の独自のリストを作成することです。次に例を示します。

$placeholders = implode(',', array_fill(0, count($values), '?'));
$sql = "SELECT Name FROM Users WHERE Locations IN ($placeholders)";

そして、あなたのパラメータをバインドするのが普通です。

于 2013-02-19T15:07:23.577 に答える
2

Hazmatが言ったように、パラメーターを作成してから、プリペアドステートメントを呼び出してパラメーターを渡す必要がありますがcall_user_func_array、彼の例よりも動作するコードに少し近いです:)

//In the calling code
$queryString = "SELECT Name FROM Users WHERE Locations IN (";
$queryString .= getWhereIn($locations);
$queryString .= " )";

$parametersArray = array();

foreach($locations as $location){
    $parameter = array();
    $parameter[0] = 's'; //It's a string
    $parameter[1] = $location;

    $parametersArray[] = $parameter;
}



//This is a function in a class that wraps around the class mysqli_statement
function    bindParameterArray($parameterArray){

    $typesString = '';
    $parameterValuesArray = array();

    foreach($parameterArray as $parameterAndType){
        $typesString .= $parameterAndType[0];
        $parameterValuesArray[] = $parameterAndType[1];
    }

    $finalParamArray = array($typesString);
    $finalParamArray = array_merge($finalParamArray, $parametersArray);
    call_user_func_array(array($this->statement, "bind_param"), $finalParamArray);
}

function getWhereIn($inArray){
    $string = "";
    $separator = "";
    for($x=0 ; $x<count($inArray) ; $x++){
        $string .= $separator."?";
        $separator = ", ";
    }
    return  $string;
}
于 2013-02-19T15:38:31.910 に答える
1

IN準備/バインドする前に、句を「構築」できます。

$sql = 'SELECT Name FROM Users WHERE Locations IN (' . implode(array_fill(0, count($locations), '?')) . ')';

次にcall_user_func_array、パラメーターの数を知らなくても、パラメーターをバインドするために使用できます。

// Parameters for SQLOut
$params = array(
    # The SQL Query
    $sql,
    # The params for bind_param
    array(str_repeat('s', count($locations))),
    # The params for bind_result
    array(&$usrName)
);

// Add the locations into the parameter list
foreach($locations as &$loc){
    // not sure if this is needed, but bind_param
    // expects its parameters to be references
    $params[1][] = &$loc;
}

// Call your function
call_user_func_array('SQLout', $params);

注:これはテストされていません

于 2013-02-19T15:11:27.390 に答える
-1

IN は通常遅く、準備されたステートメントに適していません。より良い解決策は、IN に含まれる項目のテーブルを作成し、JOIN を使用して同じ効果を得ることです。

于 2013-02-19T15:05:53.710 に答える
-2

これに対するよりエレガントな解決策を思いついた人はいますか?

もちろん。私は持っている。

Mysqli は、特にそのような複雑なケースでは、準備されたステートメントでは実際には使用できません。
したがって、準備されたステートメントを取り除き、開発者が遭遇できるすべての実際のケースをサポートする独自のプレースホルダーを実装することをお勧めします。

safeMysql (私の作成) には、探しているソリューションがあります (また、他の多くの頭痛の種のソリューションもあります)。

あなたの特定のケースでは、この1行のコードと同じくらい簡単です

// the code works alright:
$locations = array('a', 'b', 'c', 'd', 'e');
$usrName = $db->getCol("SELECT Name FROM Users WHERE Locations IN (?a)", $locations);

一部の PHP および API 関数をいじっているときに得られる醜いコードとは異なり (現在使用している PHP のバージョンによっては、依然として厄介な警告が表示されます)、このコードはきちんとしていて読みやすいです。これは重要な問題です。1 年が経過した後でも、このコードが何をするかがわかります。

于 2013-02-19T15:14:58.513 に答える