I want to bind a array of Strings to the WHERE IN
part of a SQL command, which I want to run afterwards on a SQL Server. The problem is probably that I try to bind
You can use a wrapper method that parses the sql string for you:
so when i do the query i always write it like this :
$sql="SELECT * from TableA where account=:account_id and categories in(:categories_list)"
Then, i send this to the PDO class as:
PDO::getAll($sql, array("account_id"=>$_GET["account] , "categories_list"=>explode(",","1,2,3") );
If you notice, the account_id is a direct variable, but the categories_list will be an array, so the wrapper will need to parse that and check if that values is array or not.
Then what the wrapper does it parse the array and create the binbing elements. Because as you can see i feed the array with no ":account_id" etc.. that will be automatically assign that symbol, for the pdo binding to work.
I have many different methods depending if i want all the results of mysql or just a row or just a cell, but in this case i use my made getAll method is which this:
public static function getAll($query,$array=array()){
$me=self::getInstance();
if(self::execute($query,$array)){ $resultset = $me->statement->fetchALL(PDO::FETCH_ASSOC); }else{$resultset=false;}
return $resultset;
}
so when you call this, it will call the execute method which is like this:
private static function execute($query,$array){
$me=self::getInstance();
$query_array=$array;
$multibind_array=array();
foreach ($query_array as $key => $value) {
if(strpos($query, ":".$key)){
$query_array[":".$key] = $query_array[$key];
if(is_array($query_array[$key]) ){
$multibind_array[":".$key]="";
foreach($query_array[$key] as $bindKey=>$multibind){
$bind_id=(":__".$key).$bindKey;
$multibind_array[":".$key][]=$bind_id;
$query_array[$bind_id]=$multibind;
}
$query = str_replace(":".$key, implode(",",$multibind_array[":".$key]) ,$query ); unset($query_array[":".$key]);
}
} unset($query_array[$key]);
} //auto prepair array for PDO
$me->dbh->exec("set names utf8");
$me->statement = @$me->dbh->prepare($query); //Verify if this token really match those fieds
try{$me->result=$me->statement->execute($query_array);}
catch(Exception $e){
$me->result=false;
echo "PDO error ";
print_r($me->statement->errorInfo()) ;
}
$me->lastError=$me->statement->errorInfo()[2];
return $me->result;
}
As you can see, the array will append the ":" symbol by its own, and when is an array, it will assign the same placeholder but append an id, and then string replace the original query with those ids, that will later match the array i feed.
In the end we save alot of time, because you can write SQL code directly almost, and benefit of PDO.
in the PDO::getAll($sql, array("account_id"=>$_GET["account] ,"categories_list"=>explode(",","1,2,3") );
part i can feed even values that i end up not using in the sql, the wrapper will find those and exclude it. SO we can directly from a single update object do many different mysql actions with the same data.
$update_obj=array("id"=>1, "name"=>"test", "account"=>"10");
PDO::doQuery("UPDATE ... name=:name" , $update_obj );
This mysql will update just the name. In a normal situation, PDO would tell you error because bind value "account" or "id" do not match the same tokens found on the query.
Here is the snippet I use when trying to achieve an IN statement with an array.
This works dynamically, so whether you have an array of 2 or 200 it should execute as expected.
$ids = array(1,2,3);
$in = str_repeat('?,', count($ids) - 1) . '?';
$sql = "SELECT * FROM table WHERE column IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($ids);
$data = $stm->fetchAll();
Your code will look like so:
$refIdsPartial = array('54469c27c687b332339627','54469ba0dec3e703865612','54469c77945c7091266617');
$in = str_repeat('?,', count($refIdsPartial ) - 1) . '?';
$totalCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE referral IN ($in) GROUP BY referral";
$ps_totalCounts = $dbh->prepare($totalCount);
$ps_totalCounts->execute();
//loop over total counts
foreach($ps_totalCounts as $row)
{
echo "Test<br>";
}
You could use some string manipulation.
You can count
the number of ?
you'd need by using str_repeat("?", count(explode(",", $refIdsPartial)))
. This will create your placeholders.
$totalCount =
"SELECT referral, COUNT(username) AS cnt FROM accounts
WHERE referral IN (". str_repeat("?,", count(explode(",", $refIdsPartial))-1) . "?) GROUP BY referral";
Now that the placeholders are in place, you can explode the ,
from the string and execute
$ps_totalCounts->execute( explode(",", $refIdsPartial) );
I faced similar problem with quite a big array to bind. Instead of skipping binding and injecting whole array directly to query or making workarounds with dynamically generating multiple unique placeholders to bind each record of array, I went for using find_in_set
mysql function. Read more here.
In your case it would be:
$totalCount =
"SELECT referral, COUNT(username) AS cnt FROM accounts
WHERE find_in_set(referral,$refIdsPartial) GROUP BY referral";