Related
How can I find the longest identifier name in a MySQL database? short of looping identifiers in a scripting language ala foreach("SHOW TABLES" as table){SHOW COLUMNS FROM TABLE}, i haven't found any SQL-way of finding it
To simply answer the question
MySQL how to find the longest identifier name?
use information_schema.COLUMNS. Below query finds the longest identifier in all schemas, you can restriscted by adding the following condition AND TABLE_SCHEMA='your_database'
SELECT COLUMN_NAME,TABLE_SCHEMA,TABLE_NAME,LENGTH(COLUMN_NAME)
FROM information_schema.COLUMNS
WHERE LENGTH(COLUMN_NAME) in (SELECT MAX(LENGTH(COLUMN_NAME)) as max_column_name FROM information_schema.COLUMNS );
Tested:
mysql> SELECT COLUMN_NAME,TABLE_SCHEMA,TABLE_NAME,LENGTH(COLUMN_NAME)
-> FROM information_schema.COLUMNS
-> WHERE LENGTH(COLUMN_NAME) in (SELECT MAX(LENGTH(COLUMN_NAME)) as max_column_name FROM information_schema.COLUMNS );
+---------------------------------------------------------+--------------------+--------------------------------------+---------------------+
| COLUMN_NAME | TABLE_SCHEMA | TABLE_NAME | LENGTH(COLUMN_NAME) |
+---------------------------------------------------------+--------------------+--------------------------------------+---------------------+
| LAST_APPLIED_TRANSACTION_LAST_TRANSIENT_ERROR_TIMESTAMP | performance_schema | replication_applier_status_by_worker | 55 |
+---------------------------------------------------------+--------------------+--------------------------------------+---------------------+
1 row in set (0.05 sec)
I hope someone has a better solution, but here's a loop implementation in PHP, prints
$ php longest_identifier_names.php
string(31) "blocket_external_targetbookings"
string(32) "is_available_for_all_subscribers"
string(64) "blocket_external_targetbookings.is_available_for_all_subscribers"
code:
<?php
declare(strict_types=1);
/**
* quote identifiers like column names, table names, aliases
* warning: does not support identifiers with dots in the name, like
* `ads.productid` will be interpreted as
* `ads`.`productid` (table ads, column productid), not `ads.productid`
*
* #param string $identifier
* #return string
* #throws \LengthException if identifier is too long
*/
function quoteIdentifier(string $identifier): string
{
// identifiers have different escaping rules than other strings
// https://www.codetinkerer.com/2015/07/08/escaping-column-and-table-names-in-mysql-part2.html
$ret = '`' . strtr($identifier, array(
'`' => '``',
'.' => '`.`'
)) . '`';
if(strlen($ret) > 66){
// MySQL does weird/scary things when identifiers are too long,
// protect against possible "SQL Truncation Vulnerability" attacks..
throw new \LengthException('Identifier too long: ' . $identifier);
}
return $ret;
}
$host = 'mysql.foo.com';
$db = 'dbname';
$user = 'username';
$pass = 'password';
$db = new \PDO("mysql:host={$host};dbname={$db};charset=utf8mb4", $user, $pass, array(
\PDO::ATTR_EMULATE_PREPARES => false,
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
));
$longest_table = "";
// #var string[] $tables
$tables = [];
foreach($db->query("SHOW TABLES", PDO::FETCH_NUM) as $row) {
$table = $row[0];
if(strlen($table) > strlen($longest_table)) {
$longest_table = $table;
}
$tables[] = $row[0];
}
$longest_field = "";
foreach($tables as $table) {
foreach($db->query("SHOW COLUMNS FROM ". quoteIdentifier($table), PDO::FETCH_ASSOC) as $row) {
$field = $row['Field'];
if(strlen($field) > strlen($longest_field)) {
$longest_field = $field;
}
}
}
var_dump($longest_table, $longest_field, "{$longest_table}.{$longest_field}");
I'm curious to know if it's possible to bind an array of values to a placeholder using PDO. The use case here is attempting to pass an array of values for use with an IN() condition.
I'd like to be able to do something like this:
<?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();
?>
And have PDO bind and quote all the values in the array.
At the moment I'm doing:
<?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();
?>
Which certainly does the job, but just wondering if there's a built in solution I'm missing?
You'll have to construct the query-string.
<?php
$ids = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));
$db = new PDO(...);
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN(' . $inQuery . ')'
);
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
$stmt->bindValue(($k+1), $id);
$stmt->execute();
?>
Both chris (comments) and somebodyisintrouble suggested that the foreach-loop ...
(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
$stmt->bindValue(($k+1), $id);
$stmt->execute();
... might be redundant, so the foreach loop and the $stmt->execute could be replaced by just ...
<?php
(...)
$stmt->execute($ids);
For something quick:
//$db = new PDO(...);
//$ids = array(...);
$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
Is it so important to use IN statement? Try to use FIND_IN_SET op.
For example, there is a query in PDO like that
SELECT * FROM table WHERE FIND_IN_SET(id, :array)
Then you only need to bind an array of values, imploded with comma, like this one
$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);
and it's done.
UPD: As some people pointed out in comments to this answer, there are some issues which should be stated explciitly.
FIND_IN_SET doesn't use index in a table, and it is still not implemented yet - see this record in the MYSQL bug tracker. Thanks to #BillKarwin for the notice.
You can't use a string with comma inside as a value of the array for search. It is impossible to parse such string in the right way after implode since you use comma symbol as a separator. Thanks to #VaL for the note.
In fine, if you are not heavily dependent on indexes and do not use strings with comma for search, my solution will be much easier, simpler, and faster than solutions listed above.
Since I do a lot of dynamic queries, this is a super simple helper function I made.
public static function bindParamArray($prefix, $values, &$bindArray)
{
$str = "";
foreach($values as $index => $value){
$str .= ":".$prefix.$index.",";
$bindArray[$prefix.$index] = $value;
}
return rtrim($str,",");
}
Use it like this:
$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";
Returns a string :id1,:id2,:id3 and also updates your $bindArray of bindings that you will need when it's time to run your query. Easy!
very clean way for postgres is using the postgres-array ("{}"):
$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));
Solution from EvilRygy didn't worked for me. In Postgres you can do another workaround:
$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();
Here is my solution:
$total_items = count($array_of_items);
$question_marks = array_fill(0, $total_items, '?');
$sql = 'SELECT * FROM foo WHERE bar IN (' . implode(',', $question_marks ). ')';
$stmt = $dbh->prepare($sql);
$stmt->execute(array_values($array_of_items));
Note the use of array_values. This can fix key ordering issues.
I was merging arrays of ids and then removing duplicate items. I had something like:
$ids = array(0 => 23, 1 => 47, 3 => 17);
And that was failing.
Looking at PDO :Predefined Constants there is no PDO::PARAM_ARRAY which you would need as is listed on PDOStatement->bindParam
bool PDOStatement::bindParam ( mixed $parameter , mixed &$variable [, int $data_type [, int $length [, mixed $driver_options ]]] )
So I don't think it is achievable.
I extended PDO to do something similar to what stefs suggests, and it was easier for me in the long run:
class Array_Capable_PDO extends PDO {
/**
* Both prepare a statement and bind array values to it
* #param string $statement mysql query with colon-prefixed tokens
* #param array $arrays associatve array with string tokens as keys and integer-indexed data arrays as values
* #param array $driver_options see php documention
* #return PDOStatement with given array values already bound
*/
public function prepare_with_arrays($statement, array $arrays, $driver_options = array()) {
$replace_strings = array();
$x = 0;
foreach($arrays as $token => $data) {
// just for testing...
//// tokens should be legit
//assert('is_string($token)');
//assert('$token !== ""');
//// a given token shouldn't appear more than once in the query
//assert('substr_count($statement, $token) === 1');
//// there should be an array of values for each token
//assert('is_array($data)');
//// empty data arrays aren't okay, they're a SQL syntax error
//assert('count($data) > 0');
// replace array tokens with a list of value tokens
$replace_string_pieces = array();
foreach($data as $y => $value) {
//// the data arrays have to be integer-indexed
//assert('is_int($y)');
$replace_string_pieces[] = ":{$x}_{$y}";
}
$replace_strings[] = '('.implode(', ', $replace_string_pieces).')';
$x++;
}
$statement = str_replace(array_keys($arrays), $replace_strings, $statement);
$prepared_statement = $this->prepare($statement, $driver_options);
// bind values to the value tokens
$x = 0;
foreach($arrays as $token => $data) {
foreach($data as $y => $value) {
$prepared_statement->bindValue(":{$x}_{$y}", $value);
}
$x++;
}
return $prepared_statement;
}
}
You can use it like this:
$db_link = new Array_Capable_PDO($dsn, $username, $password);
$query = '
SELECT *
FROM test
WHERE field1 IN :array1
OR field2 IN :array2
OR field3 = :value
';
$pdo_query = $db_link->prepare_with_arrays(
$query,
array(
':array1' => array(1,2,3),
':array2' => array(7,8,9)
)
);
$pdo_query->bindValue(':value', '10');
$pdo_query->execute();
When you have other parameter, you may do like this:
$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();
For me the sexier solution is to construct a dynamic associative array & use it
// 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);
I had a unique problem where, while converting the soon-to-be deprecated MySQL driver to the PDO driver I had to make a function which could build, dynamically, both normal parameters and INs from the same parameter array. So I quickly built this:
/**
* 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;
}
It is still untested however the logic seems to be there.
After some testing, I found out:
PDO does not like '.' in their names (which is kinda stupid if you ask me)
bindParam is the wrong function, bindValue is the right function.
A little editing about the code of Schnalle
<?php
$ids = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids)-1, '?'));
$db = new PDO(...);
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN(' . $inQuery . ')'
);
foreach ($ids as $k => $id)
$stmt->bindValue(($k+1), $id);
$stmt->execute();
?>
//implode(',', array_fill(0, count($ids)-1), '?'));
//'?' this should be inside the array_fill
//$stmt->bindValue(($k+1), $in);
// instead of $in, it should be $id
What database are you using? In PostgreSQL I like using ANY(array). So to reuse your example:
<?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();
?>
Unfortunately this is pretty non-portable.
On other databases you'll need to make up your own magic as others have been mentioning. You'll want to put that logic into a class/function to make it reusable throughout your program of course. Take a look at the comments on mysql_query page on PHP.NET for some more thoughts on the subject and examples of this scenario.
If the column can only contain integers, you could probably do this without placeholders and just put the ids in the query directly. You just have to cast all the values of the array to integers. Like this:
$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
"SELECT *
FROM table
WHERE id IN($listOfIds)"
);
$stmt->execute();
This shouldn't be vulnerable to any SQL injection.
As I know there is no any possibility to bind an array into PDO statement.
But exists 2 common solutions:
Use Positional Placeholders (?,?,?,?) or Named Placeholders (:id1, :id2, :id3)
$whereIn = implode(',', array_fill(0, count($ids), '?'));
Quote array earlier
$whereIn = array_map(array($db, 'quote'), $ids);
Both options are good and safe.
I prefer second one because it's shorter and I can var_dump parameters if I need it.
Using placeholders you must bind values and in the end your SQL code will be the same.
$sql = "SELECT * FROM table WHERE id IN ($whereIn)";
And the last and important for me is avoiding error "number of bound variables does not match number of tokens"
Doctrine it's great example of using positional placeholders, only because it has internal control over incoming parameters.
It's not possible to use an array like that in PDO.
You need to build a string with a parameter (or use ?) for each value, for instance:
:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5
Here's an example:
<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = join(
', ',
array_map(
function($index) {
return ":an_array_$index";
},
array_keys($ids)
)
);
$db = new PDO(
'mysql:dbname=mydb;host=localhost',
'user',
'passwd'
);
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN('.$sqlAnArray.')'
);
foreach ($ids as $index => $id) {
$stmt->bindValue("an_array_$index", $id);
}
If you want to keep using bindParam, you may do this instead:
foreach ($ids as $index => $id) {
$stmt->bindParam("an_array_$index", $ids[$id]);
}
If you want to use ? placeholders, you may do it like this:
<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = '?' . str_repeat(', ?', count($ids)-1);
$db = new PDO(
'mysql:dbname=dbname;host=localhost',
'user',
'passwd'
);
$stmt = $db->prepare(
'SELECT *
FROM phone_number_lookup
WHERE country_code IN('.$sqlAnArray.')'
);
$stmt->execute($ids);
If you don't know if $ids is empty, you should test it and handle that case accordingly (return an empty array, or return a Null Object, or throw an exception, ...).
After going through the same problem, i went to a simpler solution (although still not as elegant as an PDO::PARAM_ARRAY would be) :
given the 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();
... and so on
So if you are using a mixed values array, you will need more code to test your values before assigning the type param:
// 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 ));
But i have not tested this one.
With MySQL and PDO we can use a JSON array and JSON_CONTAINS() (https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains) to search in.
$ids = [123, 234, 345, 456]; // Array of users I search
$ids = json_encode($ids); // JSON conversion
$sql = <<<SQL
SELECT ALL user_id, user_login
FROM users
-- Cast is mandatory beaucause JSON_CONTAINS() waits JSON doc candidate
WHERE JSON_CONTAINS(:ids, CAST(user_id AS JSON))
SQL;
$search = $pdo->prepare($sql);
$search->execute([':ids' => $ids]);
$users = $search->fetchAll();
Whe can also use JSON_TABLE() (https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html#function_json-table) for more complex cases and JSON data exploration :
$users = [
['id' => 123, 'bday' => ..., 'address' => ...],
['id' => 234, 'bday' => ..., 'address' => ...],
['id' => 345, 'bday' => ..., 'address' => ...],
]; // I'd like to know their login
$users = json_encode($users);
$sql = <<<SQL
SELECT ALL user_id, user_login
FROM users
WHERE user_id IN (
SELECT ALL user_id
FROM JSON_TABLE(:users, '$[*]' COLUMNS (
-- Data exploration...
-- (if needed I can explore really deeply with NESTED kword)
user_id INT PATH '$.id',
-- I could skip these :
user_bday DATE PATH '$.bday',
user_address TINYTEXT PATH '$.address'
)) AS _
)
SQL;
$search = $pdo->prepare($sql);
$search->execute([':users' => $users]);
...
Here is my solution, based on alan_mm's answer. I have also extended the PDO class:
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);
}
}
}
Here is a sample usage for the above code:
$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'];
}
you first set number of "?" in query and then by a "for" send parameters
like this :
require 'dbConnect.php';
$db=new dbConnect();
$array=[];
array_push($array,'value1');
array_push($array,'value2');
$query="SELECT * FROM sites WHERE kind IN (";
foreach ($array as $field){
$query.="?,";
}
$query=substr($query,0,strlen($query)-1);
$query.=")";
$tbl=$db->connection->prepare($query);
for($i=1;$i<=count($array);$i++)
$tbl->bindParam($i,$array[$i-1],PDO::PARAM_STR);
$tbl->execute();
$row=$tbl->fetchAll(PDO::FETCH_OBJ);
var_dump($row);
I took it a bit further to get the answer closer to the original question of using placeholders to bind the params.
This answer will have to make two loops through the array to be used in the query. But it does solve the issue of having other column placeholders for more selective queries.
//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();
You could convert this:
$stmt = $db->prepare('SELECT * FROM table WHERE id IN('.$in.')');
In this:
$stmt = $db->prepare('SELECT * FROM table WHERE id IN(:id1, :id2, :id3, :id7, :id8, :id9)');
And execute it with this array:
$stmt->execute(array(
:id1 =>1, :id2 =>2, :id3 =>3, :id7 =>7, :id8 =>8, :id9 => 9
)
);
Thus:
$in = array();
$consultaParam = array();
foreach($ids as $k => $v){
$in[] = ':id'.$v;
$consultaParam[':id'.$v] = $v;
}
Final code:
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$in = array();
$consultaParam = array();
foreach($ids as $k => $v){
$in[] = ':id'.$v;
$consultaParam[':id'.$v] = $v;
}
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN('.$in.')'
);
$stmt->execute($consultaParam);
I'm curious to know if it's possible to bind an array of values to a placeholder using PDO. The use case here is attempting to pass an array of values for use with an IN() condition.
I'd like to be able to do something like this:
<?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();
?>
And have PDO bind and quote all the values in the array.
At the moment I'm doing:
<?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();
?>
Which certainly does the job, but just wondering if there's a built in solution I'm missing?
You'll have to construct the query-string.
<?php
$ids = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));
$db = new PDO(...);
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN(' . $inQuery . ')'
);
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
$stmt->bindValue(($k+1), $id);
$stmt->execute();
?>
Both chris (comments) and somebodyisintrouble suggested that the foreach-loop ...
(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
$stmt->bindValue(($k+1), $id);
$stmt->execute();
... might be redundant, so the foreach loop and the $stmt->execute could be replaced by just ...
<?php
(...)
$stmt->execute($ids);
For something quick:
//$db = new PDO(...);
//$ids = array(...);
$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
Is it so important to use IN statement? Try to use FIND_IN_SET op.
For example, there is a query in PDO like that
SELECT * FROM table WHERE FIND_IN_SET(id, :array)
Then you only need to bind an array of values, imploded with comma, like this one
$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);
and it's done.
UPD: As some people pointed out in comments to this answer, there are some issues which should be stated explciitly.
FIND_IN_SET doesn't use index in a table, and it is still not implemented yet - see this record in the MYSQL bug tracker. Thanks to #BillKarwin for the notice.
You can't use a string with comma inside as a value of the array for search. It is impossible to parse such string in the right way after implode since you use comma symbol as a separator. Thanks to #VaL for the note.
In fine, if you are not heavily dependent on indexes and do not use strings with comma for search, my solution will be much easier, simpler, and faster than solutions listed above.
Since I do a lot of dynamic queries, this is a super simple helper function I made.
public static function bindParamArray($prefix, $values, &$bindArray)
{
$str = "";
foreach($values as $index => $value){
$str .= ":".$prefix.$index.",";
$bindArray[$prefix.$index] = $value;
}
return rtrim($str,",");
}
Use it like this:
$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";
Returns a string :id1,:id2,:id3 and also updates your $bindArray of bindings that you will need when it's time to run your query. Easy!
very clean way for postgres is using the postgres-array ("{}"):
$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));
Solution from EvilRygy didn't worked for me. In Postgres you can do another workaround:
$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();
Here is my solution:
$total_items = count($array_of_items);
$question_marks = array_fill(0, $total_items, '?');
$sql = 'SELECT * FROM foo WHERE bar IN (' . implode(',', $question_marks ). ')';
$stmt = $dbh->prepare($sql);
$stmt->execute(array_values($array_of_items));
Note the use of array_values. This can fix key ordering issues.
I was merging arrays of ids and then removing duplicate items. I had something like:
$ids = array(0 => 23, 1 => 47, 3 => 17);
And that was failing.
Looking at PDO :Predefined Constants there is no PDO::PARAM_ARRAY which you would need as is listed on PDOStatement->bindParam
bool PDOStatement::bindParam ( mixed $parameter , mixed &$variable [, int $data_type [, int $length [, mixed $driver_options ]]] )
So I don't think it is achievable.
I extended PDO to do something similar to what stefs suggests, and it was easier for me in the long run:
class Array_Capable_PDO extends PDO {
/**
* Both prepare a statement and bind array values to it
* #param string $statement mysql query with colon-prefixed tokens
* #param array $arrays associatve array with string tokens as keys and integer-indexed data arrays as values
* #param array $driver_options see php documention
* #return PDOStatement with given array values already bound
*/
public function prepare_with_arrays($statement, array $arrays, $driver_options = array()) {
$replace_strings = array();
$x = 0;
foreach($arrays as $token => $data) {
// just for testing...
//// tokens should be legit
//assert('is_string($token)');
//assert('$token !== ""');
//// a given token shouldn't appear more than once in the query
//assert('substr_count($statement, $token) === 1');
//// there should be an array of values for each token
//assert('is_array($data)');
//// empty data arrays aren't okay, they're a SQL syntax error
//assert('count($data) > 0');
// replace array tokens with a list of value tokens
$replace_string_pieces = array();
foreach($data as $y => $value) {
//// the data arrays have to be integer-indexed
//assert('is_int($y)');
$replace_string_pieces[] = ":{$x}_{$y}";
}
$replace_strings[] = '('.implode(', ', $replace_string_pieces).')';
$x++;
}
$statement = str_replace(array_keys($arrays), $replace_strings, $statement);
$prepared_statement = $this->prepare($statement, $driver_options);
// bind values to the value tokens
$x = 0;
foreach($arrays as $token => $data) {
foreach($data as $y => $value) {
$prepared_statement->bindValue(":{$x}_{$y}", $value);
}
$x++;
}
return $prepared_statement;
}
}
You can use it like this:
$db_link = new Array_Capable_PDO($dsn, $username, $password);
$query = '
SELECT *
FROM test
WHERE field1 IN :array1
OR field2 IN :array2
OR field3 = :value
';
$pdo_query = $db_link->prepare_with_arrays(
$query,
array(
':array1' => array(1,2,3),
':array2' => array(7,8,9)
)
);
$pdo_query->bindValue(':value', '10');
$pdo_query->execute();
When you have other parameter, you may do like this:
$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();
For me the sexier solution is to construct a dynamic associative array & use it
// 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);
I had a unique problem where, while converting the soon-to-be deprecated MySQL driver to the PDO driver I had to make a function which could build, dynamically, both normal parameters and INs from the same parameter array. So I quickly built this:
/**
* 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;
}
It is still untested however the logic seems to be there.
After some testing, I found out:
PDO does not like '.' in their names (which is kinda stupid if you ask me)
bindParam is the wrong function, bindValue is the right function.
A little editing about the code of Schnalle
<?php
$ids = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids)-1, '?'));
$db = new PDO(...);
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN(' . $inQuery . ')'
);
foreach ($ids as $k => $id)
$stmt->bindValue(($k+1), $id);
$stmt->execute();
?>
//implode(',', array_fill(0, count($ids)-1), '?'));
//'?' this should be inside the array_fill
//$stmt->bindValue(($k+1), $in);
// instead of $in, it should be $id
What database are you using? In PostgreSQL I like using ANY(array). So to reuse your example:
<?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();
?>
Unfortunately this is pretty non-portable.
On other databases you'll need to make up your own magic as others have been mentioning. You'll want to put that logic into a class/function to make it reusable throughout your program of course. Take a look at the comments on mysql_query page on PHP.NET for some more thoughts on the subject and examples of this scenario.
If the column can only contain integers, you could probably do this without placeholders and just put the ids in the query directly. You just have to cast all the values of the array to integers. Like this:
$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
"SELECT *
FROM table
WHERE id IN($listOfIds)"
);
$stmt->execute();
This shouldn't be vulnerable to any SQL injection.
As I know there is no any possibility to bind an array into PDO statement.
But exists 2 common solutions:
Use Positional Placeholders (?,?,?,?) or Named Placeholders (:id1, :id2, :id3)
$whereIn = implode(',', array_fill(0, count($ids), '?'));
Quote array earlier
$whereIn = array_map(array($db, 'quote'), $ids);
Both options are good and safe.
I prefer second one because it's shorter and I can var_dump parameters if I need it.
Using placeholders you must bind values and in the end your SQL code will be the same.
$sql = "SELECT * FROM table WHERE id IN ($whereIn)";
And the last and important for me is avoiding error "number of bound variables does not match number of tokens"
Doctrine it's great example of using positional placeholders, only because it has internal control over incoming parameters.
It's not possible to use an array like that in PDO.
You need to build a string with a parameter (or use ?) for each value, for instance:
:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5
Here's an example:
<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = join(
', ',
array_map(
function($index) {
return ":an_array_$index";
},
array_keys($ids)
)
);
$db = new PDO(
'mysql:dbname=mydb;host=localhost',
'user',
'passwd'
);
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN('.$sqlAnArray.')'
);
foreach ($ids as $index => $id) {
$stmt->bindValue("an_array_$index", $id);
}
If you want to keep using bindParam, you may do this instead:
foreach ($ids as $index => $id) {
$stmt->bindParam("an_array_$index", $ids[$id]);
}
If you want to use ? placeholders, you may do it like this:
<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = '?' . str_repeat(', ?', count($ids)-1);
$db = new PDO(
'mysql:dbname=dbname;host=localhost',
'user',
'passwd'
);
$stmt = $db->prepare(
'SELECT *
FROM phone_number_lookup
WHERE country_code IN('.$sqlAnArray.')'
);
$stmt->execute($ids);
If you don't know if $ids is empty, you should test it and handle that case accordingly (return an empty array, or return a Null Object, or throw an exception, ...).
After going through the same problem, i went to a simpler solution (although still not as elegant as an PDO::PARAM_ARRAY would be) :
given the 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();
... and so on
So if you are using a mixed values array, you will need more code to test your values before assigning the type param:
// 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 ));
But i have not tested this one.
With MySQL and PDO we can use a JSON array and JSON_CONTAINS() (https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains) to search in.
$ids = [123, 234, 345, 456]; // Array of users I search
$ids = json_encode($ids); // JSON conversion
$sql = <<<SQL
SELECT ALL user_id, user_login
FROM users
-- Cast is mandatory beaucause JSON_CONTAINS() waits JSON doc candidate
WHERE JSON_CONTAINS(:ids, CAST(user_id AS JSON))
SQL;
$search = $pdo->prepare($sql);
$search->execute([':ids' => $ids]);
$users = $search->fetchAll();
Whe can also use JSON_TABLE() (https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html#function_json-table) for more complex cases and JSON data exploration :
$users = [
['id' => 123, 'bday' => ..., 'address' => ...],
['id' => 234, 'bday' => ..., 'address' => ...],
['id' => 345, 'bday' => ..., 'address' => ...],
]; // I'd like to know their login
$users = json_encode($users);
$sql = <<<SQL
SELECT ALL user_id, user_login
FROM users
WHERE user_id IN (
SELECT ALL user_id
FROM JSON_TABLE(:users, '$[*]' COLUMNS (
-- Data exploration...
-- (if needed I can explore really deeply with NESTED kword)
user_id INT PATH '$.id',
-- I could skip these :
user_bday DATE PATH '$.bday',
user_address TINYTEXT PATH '$.address'
)) AS _
)
SQL;
$search = $pdo->prepare($sql);
$search->execute([':users' => $users]);
...
Here is my solution, based on alan_mm's answer. I have also extended the PDO class:
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);
}
}
}
Here is a sample usage for the above code:
$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'];
}
you first set number of "?" in query and then by a "for" send parameters
like this :
require 'dbConnect.php';
$db=new dbConnect();
$array=[];
array_push($array,'value1');
array_push($array,'value2');
$query="SELECT * FROM sites WHERE kind IN (";
foreach ($array as $field){
$query.="?,";
}
$query=substr($query,0,strlen($query)-1);
$query.=")";
$tbl=$db->connection->prepare($query);
for($i=1;$i<=count($array);$i++)
$tbl->bindParam($i,$array[$i-1],PDO::PARAM_STR);
$tbl->execute();
$row=$tbl->fetchAll(PDO::FETCH_OBJ);
var_dump($row);
I took it a bit further to get the answer closer to the original question of using placeholders to bind the params.
This answer will have to make two loops through the array to be used in the query. But it does solve the issue of having other column placeholders for more selective queries.
//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();
You could convert this:
$stmt = $db->prepare('SELECT * FROM table WHERE id IN('.$in.')');
In this:
$stmt = $db->prepare('SELECT * FROM table WHERE id IN(:id1, :id2, :id3, :id7, :id8, :id9)');
And execute it with this array:
$stmt->execute(array(
:id1 =>1, :id2 =>2, :id3 =>3, :id7 =>7, :id8 =>8, :id9 => 9
)
);
Thus:
$in = array();
$consultaParam = array();
foreach($ids as $k => $v){
$in[] = ':id'.$v;
$consultaParam[':id'.$v] = $v;
}
Final code:
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$in = array();
$consultaParam = array();
foreach($ids as $k => $v){
$in[] = ':id'.$v;
$consultaParam[':id'.$v] = $v;
}
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN('.$in.')'
);
$stmt->execute($consultaParam);
I am trying to work with a voting database system. I have a form to display all the candidates per candidate type. I'm still trying to to explore that. But this time, I want to try one candidate type, lets say for Chairperson, I want to display all candidate names for that type in the ballot form. However, there's an error with the line where i declare $query and made query statements, can somebody know what it is. I am very certain that my syntax is correct.
function returnAllFromTable($table) {
include 'connect.php';
$data = array ();
$query = 'SELECT * FROM ' . $table. 'WHERE candidateId=1'; //ERROR
$mysql_query = mysql_query ( $query, $conn );
if (! $mysql_query) {
die ( 'Go Back<br>Unable to retrieve data from table ' . $table );
} else {
while ( $row = mysql_fetch_array ( $mysql_query ) ) {
$data [] = $row;
}
}
return $data;
}
As #Darwin von Corax says, I sure that you have a problem between $table and WHERE
Your query:
$query = 'SELECT * FROM ' . $table. 'WHERE candidateId=1';
If $table = 'Chairperson';
You have:
'SELECT * FROM ChairpersonWHERE candidateId=1';
The your query should be:
$query = 'SELECT * FROM ' . $table. ' WHERE candidateId=1';
I'm trying to search a whole table in mySQL for a string.
I want to search all fields and all entrees of a table, returning each full entry that contains the specified text.
I can't figure out how to search multiple fields easily; here are the details:
The table is "clients". It has about 30 fields and 800 entries, too much to show all at once in a browser. I would like to search for a name (i.e. "Mary"), but it could be in the shipping_name field or the billing_name field, or the email field, etc.
I would like to search all fields for any entries that contain the string "Mary". This is what I think should work but doesn't:
SELECT * FROM `clients` IN 'Mary'
Try something like this:
SELECT * FROM clients WHERE CONCAT(field1, '', field2, '', fieldn) LIKE "%Mary%"
You may want to see SQL docs for additional information on string operators and regular expressions.
Edit: There may be some issues with NULL fields, so just in case you may want to use IFNULL(field_i, '') instead of just field_i
Case sensitivity: You can use case insensitive collation or something like this:
... WHERE LOWER(CONCAT(...)) LIKE LOWER("%Mary%")
Just search all field: I believe there is no way to make an SQL-query that will search through all field without explicitly declaring field to search in. The reason is there is a theory of relational databases and strict rules for manipulating relational data (something like relational algebra or codd algebra; these are what SQL is from), and theory doesn't allow things such as "just search all fields". Of course actual behaviour depends on vendor's concrete realisation. But in common case it is not possible. To make sure, check SELECT operator syntax (WHERE section, to be precise).
Identify all the fields that could be related to your search and then use a query like:
SELECT * FROM clients
WHERE field1 LIKE '%Mary%'
OR field2 LIKE '%Mary%'
OR field3 LIKE '%Mary%'
OR field4 LIKE '%Mary%'
....
(do that for each field you want to check)
Using LIKE '%Mary%' instead of = 'Mary' will look for the fields that contains someCaracters + 'Mary' + someCaracters.
In addition to pattern matching with 'like' keyword. You can also perform search
by using fulltext feature as below;
SELECT * FROM clients WHERE MATCH (shipping_name, billing_name, email) AGAINST ('mary')
If you are just looking for some text and don't need a result set for programming purposes, you could install HeidiSQL for free (I'm using v9.2.0.4947).
Right click any database or table and select "Find text on server".
All the matches are shown in a separate tab for each table - very nice.
Frighteningly useful and saved me hours. Forget messing about with lengthy queries!!
A PHP Based Solution for search entire table ! Search string is $string . This is generic and will work with all the tables with any number of fields
$sql="SELECT * from client_wireless";
$sql_query=mysql_query($sql);
$logicStr="WHERE ";
$count=mysql_num_fields($sql_query);
for($i=0 ; $i < mysql_num_fields($sql_query) ; $i++){
if($i == ($count-1) )
$logicStr=$logicStr."".mysql_field_name($sql_query,$i)." LIKE '%".$string."%' ";
else
$logicStr=$logicStr."".mysql_field_name($sql_query,$i)." LIKE '%".$string."%' OR ";
}
// start the search in all the fields and when a match is found, go on printing it .
$sql="SELECT * from client_wireless ".$logicStr;
//echo $sql;
$query=mysql_query($sql);
Try this code,
SELECT
*
FROM
`customers`
WHERE
(
CONVERT
(`customer_code` USING utf8mb4) LIKE '%Mary%'
OR
CONVERT(`customer_name` USING utf8mb4) LIKE '%Mary%'
OR
CONVERT(`email_id` USING utf8mb4) LIKE '%Mary%'
OR
CONVERT(`address1` USING utf8mb4) LIKE '%Mary%'
OR
CONVERT(`report_sorting` USING utf8mb4) LIKE '%Mary%'
)
This is help to solve your problem mysql version 5.7.21
If you're using Sublime, you can easily generate hundreds or thousands of lines using Text Pastry in conjunction with multiple line selection and Emmet.
So in my case I set the document type to html, then typed div*249, hit tab and Emmet creates 249 empty divs. Then using multiple selection I typed col_id_ in each one and triggered Text Pastry to insert an incremental id number. Then with multiple selection again you can delete the div markup and replace it with the MySQL syntax.
for specific requirement the following will work for search:
select * from table_name where (column_name1='%var1%' or column_name2='var2' or column_name='%var3%') and column_name='var';
if you want to query for searching data from the database this will work perfectly.
One can take an export of the table in an excel sheet & find the string in the excel file itself.
This is not the best method and should be used with care as it can crash db with large amounts of tables and data. Somethings may need to be modified to use with your specific setup however should get you close.
<?php
class DBSearch{
// DB Connection
protected $db;
// Name of the DB to search in
protected $db_name = 'my_db_name';
// Tables to exclude from search
protected $excluded_tables = array(
'TABLE_I_DONT_WANT_INCLUDED',
);
// Search String
protected $search_string = '';
// Table has column
protected $has_column = '';
// Set the result limit per query
protected $limit = 5;
public function __construct($db_conn) {
parent::__construct();
$this->db = $db_conn;
}
public function search(string $search_str, string $has_column, array $exclude_table){
$this->search_string = $search_str;
$this->has_column = $has_column;
$this->excluded_tables = $exclude_table;
if(!empty($this->has_column)){
$table_names = $this->get_table_with_column($this->has_column,$this->excluded_tables);
}else{
$table_names = $this->get_all_tables($this->excluded_tables);
}
$query_string = $this->generate_query_string($table_names, $this->search_string);
$results = array();
foreach($query_string as $k=>$v){
$query = $v.' LIMIT '.$this->limit;
$results[] = $this->db->query($query)->result();
}
return $results;
}
/**
* Returns the column names associated with the table
* provided by the $table param
*
* #param string $table
* #return array
*/
private function get_table_column_names($table){
$response = array();
$sql = 'SELECT COLUMN_NAME, TABLE_NAME
FROM information_schema.columns
WHERE table_schema = ?
AND table_name = ?
ORDER BY table_name, ordinal_position';
$param = array($this->db_name, $table);
$result = $this->db->query($sql, $param);
if($result->num_rows() >= 1){
foreach ($result->result() as $v){
$response[$table][] = $v->COLUMN_NAME;
}
}
return $response;
}
/**
* Returns a object contaning the table names that
* have columns that have the name provided in $column
*
* You can also pass in a string or an array of tables not to in clude in
* the result set using the $exclude_table param
*
* #param string $column
* #param array|string $exclude_table
* #return object|boolean
*/
private function get_table_with_column($column, $exclude_table=NULL){
$sql = 'SELECT table_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE (COLUMN_NAME = ?
OR COLUMN_NAME LIKE ?)
AND table_schema = ? ';
if(NULL !== $exclude_table){
if(is_array($exclude_table)){
foreach($exclude_table as $v){
$sql .= ' AND TABLE_NAME != "'.strip_quotes($v).'"';
}
}
if(is_string($exclude_table)){
$sql .= ' AND TABLE_NAME != "'.strip_quotes($exclude_table).'"';
}
}
$sql .= ' GROUP BY TABLE_NAME ORDER BY TABLE_NAME ';
$query_param = array($column, '%'.$column.'%', $this->db_name);
$result = $this->db->query($sql, $query_param);
if($result->num_rows() >= 1){
return $result->result();
}
return false;
}
/**
* Returns an object contaning the table names.
*
* You can also pass in a string or an array of tables not to in clude in
* the result set using the $exclude_table param
*
* #param array|string $exclude_table
* #return object|boolean
*/
private function get_all_tables($exclude_table=NULL){
$sql = 'SELECT table_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = ? ';
if(NULL !== $exclude_table){
if(is_array($exclude_table)){
foreach($exclude_table as $v){
$sql .= ' AND TABLE_NAME != "'.strip_quotes($v).'"';
}
}
if(is_string($exclude_table)){
$sql .= ' AND TABLE_NAME != "'.strip_quotes($exclude_table).'"';
}
}
$sql .= ' ORDER BY TABLE_NAME';
$query_param = array($this->db_name);
$result = $this->db->query($sql, $query_param);
if($result->num_rows() >= 1){
return $result->result();
}
return false;
}
/**
* Generates a search string for each table
* provided $table_names array
*
* #param array $table_names
* #param string $search_string
* #return array[]
*/
private function generate_query_string($table_names, $search_string){
$search_split = explode(' ', $search_string);
$search_a = isset($search_split[0]) ? $search_split[0]:'';
$search_b = isset($search_split[1]) ? $search_split[1]:'';
$queries = array();
if(is_array($table_names)){
foreach ($table_names as $v){
$query_string = 'SELECT * FROM '.$v->TABLE_NAME.' WHERE (';
foreach ($this->get_table_column_names($v->TABLE_NAME)[$v->TABLE_NAME] as $c){
$query_string .= '`'.$c.'` LIKE "%'.$search_string.'%" OR';
if(!empty($search_a)){
$query_string .= '`'.$c.'` LIKE "%'.$search_a.'%" OR';
}
if(!empty($search_b)){
$query_string .= '`'.$c.'` LIKE "%'.$search_b.'%" OR';
}
}
// Remoe Last OR
$query_string = substr($query_string, 0, strlen($query_string)-3). ')';
$queries[$v->TABLE_NAME] = $query_string;
}
}
return $queries;
}
}
// USEAGE
$search = new DBSearch($db_conn);
$exclude_table = array(
'tables',
'i_dont',
'want_searched'
);
$search->search('Something to search for', 'has_this_column', $exclude_table);
This essentials is a query builder for database tables and then runs the query on each table/column found in the DB. Maybe it will be helpful. Enjoy!