How do I reuse a PDOStatement prepare? - php

I made a search but I couldn't find anything fulfilling
Imagine the following:
<?php
$connection = new \PDO($dsn, $user, $pass);
$stmt1 = $connection->prepare("
SELECT
*
FROM
table
WHERE
a = :a
AND b = :b
AND c = :c
AND d = :d
AND search LIKE :search
");
$stmt1->bindValue(":a", $a);
$stmt1->bindValue(":b", $b);
$stmt1->bindValue(":c", $c);
$stmt1->bindValue(":d", $d);
$stmt2 = clone $stmt1;
$stmt1->bindValue(":search", "a%");
$stmt2->bindValue(":search", "b%");
$stmt1->execute();
$stmt2->execute();
while(($r1 = $stmt1->fetchObject()) && ($r2 = $stmt2->fetchObject()))
echo $r1->foo . " " . $r2->foo . "\n";
}
Am I allowed to do something like this? How can I clone/reuse a PDOStatement instance and use it at the same time of its original instance?
Don't say "use UNION", that's not the point of my question :P
Thank you in advance.

This is not what prepared statement re-use is for. The idea of reusing a prepared statements is consecutive, not concurrent.
So you can do this:
$connection = new \PDO($dsn, $user, $pass);
$stmt = $connection->prepare("
SELECT *
FROM table
WHERE a = :a
AND b = :b
AND c = :c
AND d = :d
AND search LIKE :search
");
$stmt->bindValue(":a", $a);
$stmt->bindValue(":b", $b);
$stmt->bindValue(":c", $c);
$stmt->bindValue(":d", $d);
foreach (["a%", "b%"] as $search) {
$stmt->bindValue(":search", $search);
$stmt->execute();
while($r = $stmt->fetchObject()) {
echo $r->foo . "\n";
}
$stmt->closeCursor();
}
If you want to handle multiple result sets concurrently (at least with MySQL), you will need to do one of the following:
Use an appropriate set of UNION/JOIN to create a single result set.
Buffer the result sets in memory and iterate them again when you have all the data available.
Create multiple connections - you cannot have more than one open statement cursor per connection, but you can have multiple open connections.
If you want to use multiple connections, your code becomes:
$query = "
SELECT *
FROM table
WHERE a = :a
AND b = :b
AND c = :c
AND d = :d
AND search LIKE :search
";
$connection1 = new \PDO($dsn, $user, $pass);
$connection2 = new \PDO($dsn, $user, $pass);
$stmt1 = $connection1->prepare($query);
$stmt1->bindValue(":a", $a);
$stmt1->bindValue(":b", $b);
$stmt1->bindValue(":c", $c);
$stmt1->bindValue(":d", $d);
$stmt1->bindValue(":search", "a%");
$stmt2 = $connection2->prepare($query);
$stmt2->bindValue(":a", $a);
$stmt2->bindValue(":b", $b);
$stmt2->bindValue(":c", $c);
$stmt2->bindValue(":d", $d);
$stmt2->bindValue(":search", "b%");
$stmt1->execute();
$stmt2->execute();
while(($r1 = $stmt1->fetchObject()) && ($r2 = $stmt2->fetchObject()))
echo $r1->foo . " " . $r2->foo . "\n";
}
$stmt1->closeCursor();
$stmt2->closeCursor();

Sounds like you're looking for an abstraction of a prepared statement that is able to carry it's own parameters with it.
As you actually consume each result by traversing it, adding an Iterator that knows how to traverse such a parametrized prepared statement looks fitting then.
However this suggestion is not part of PDO, so you would need to write it your own (however this might also prevent to repeat yourself with PDO code and you perhaps can even deffer the actual creation and execution of the statement so this might also create some benefit for lazy loading data).

Related

Performance of fetching DB items individually vs looping with php

Out of curiosity, which of these segments of code would have the faster performance time when implemented on a mass scale?
Let's say we have a table members and we want to fetch their photos.
Method 1
$members = $db->query('SELECT * FROM members ORDER BY id ASC');
foreach($members as $m)
$memberIDs[] = $m['id'];
$photos = $db->query('SELECT * FROM photos WHERE member IN'.join(',', $memberIDs).' ORDER BY id ASC');
foreach($photos as $p) {
// multi_arr_search(search_val, search_column, search_array) returns parent key in multi dimensional array
$memberArrayKey = multi_arr_search($p['member'], 'id', $members);
$members[$memberArrayKey]['photos'][] = $p;
}
OR
Method 2
$members = $db->query('SELECT * FROM members ORDER BY id ASC');
foreach($members as $k=>$m)
$members[$k]['photos'] = $db->query('SELECT * FROM photos WHERE member='.$m['id'].' ORDER BY id ASC');
Method 1 would result in fewer queries being ran, but requires more PHP work.
neither. You are using 15 years old deprecated methods.
If you want to go fast, you need PDO prepared statements.
#alberto is right. I am pretty sure, you can have one and only sql to perform this but here is a way you might want to consider in case off:
$showError = true;
define("SQLHOST", "127.0.0.1");
define("SQLUSER", "login");
define("SQLPASS", "password");
define("SQLSGBD", "database");
$conn = new PDO('mysql:host=' . SQLHOST . ';dbname=' . SQLSGBD . ';charset=UTF8', SQLUSER, SQLPASS);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql1 = 'SELECT * FROM members ORDER BY id ASC';
$stmt1 = $conn->prepare($sql1);
$sql2 = 'SELECT * FROM photos WHERE member IN ? ORDER BY id ASC';
$stmt2 = $conn->prepare($sql2);
try {
$stmt1->execute();
$members = $stmt1->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
if ($showError === true) {
var_dump("error query 1:" . __LINE__ . "-------" . __FUNCTION__ . "-------" . $e->getMessage());
exit;
}
}
if (count($members) !== 0) {
$memberIDs = array();
foreach ($members as $m) {
$memberIDs[] = $m['id'];
}
$memberlist = join(',', $memberIDs);
foreach ($members as $memberArrayKey => $result1) {
$stmt2->bindParam(1, $memberlist, PDO::PARAM_STR);
try {
$stmt2->execute();
$photos = $stmt2->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
if ($showError === true) {
var_dump("error query 2:" . __LINE__ . "-------" . __FUNCTION__ . "-------" . $e->getMessage());
exit;
}
}
if (count($photos) !== 0) {
$memberArrayKey = multi_arr_search($p['member'], 'id', $members);
$members[$memberArrayKey]['photos'][] = $p;
}
}
}
Well, hard truth: none is actually scalable. If you're working with large sets of data, PHP will need quite a bit of memory to perform all the operations to link users and photos and will do it in a much more inefficient way than the DB can.
You should have the tool you have that is best at doing these kind of things, which is your DB: the DB should join the users & photos and get you that set.. anything you do differently will mean reduced performance and scalability.
1- It depends on many factors how are you working with MySQL, if you connect and disconnect connection with the DB at each call.
2- MySQL Server is in the same PHP.
Ideally, connect 1 time mysql to retrieve information with a single Query. And in your case, you can solucuonar with a JOIN. Which it is best to disconnect as quickly as possible and continue with MySQL PHP.
Another recommendation is to not use " * " in the SELECT. Only puts the fields that you go to work. This eat fewer resources because only you use is requested. It is optimal in Memory, CPU, Data via LAN

PDO statement not retrieving any data [duplicate]

I am trying to do a search through php's PDO class (mysql driver). I have the following query working with the MySQL client (table names changed to protect the innocent):
SELECT hs.hs_pk,
hs.hs_text,
hs.hs_did,
hd.hd_did,
hd.hd_text,
hv.hv_text,
hc.hc_text
FROM hs
LEFT JOIN hd
ON hs.hs_did = hd.hd_did
LEFT JOIN hd
ON hd.hd_vid = hv.hv_id
LEFT JOIN hc
ON hd.hd_pclass = hc.hc_id
WHERE hs.hs_text LIKE "%searchTerm%"
LIMIT 25;
This works like a charm regardless of the search term that I use. However, when I move to php, I can't get it to return anything. I have tried several different syntaxes that seem logical to work, but nothing I have tried works. here's my existing code:
$handle = fopen('/foo/bar/test.log', 'w+');
fwrite($handle, "doSearch, with search term: $searchTerm\n");
$sql =
'SELECT hs.hs_pk,
hs.hs_text,
hs.hs_did,
hd.hd_did,
hd.hd_text,
hv.hv_text,
hc.hc_text
FROM hs
LEFT JOIN hd
ON hs.hs_did = hd.hd_did
LEFT JOIN hd
ON hd.hd_vid = hv.hv_id
LEFT JOIN hc
ON hd.hd_pclass = hc.hc_id
WHERE hs.hs_text LIKE :searchTerm
LIMIT 25';
try {
$dbh = new PDO('mysql:host=localhost;dbname=awdb', "user", "password");
fwrite($handle, "connected to DB\n");
$prep = $dbh->prepare($sql);
$ret = $prep->execute(array(':searchTerm' => '"%'.$searchTerm.'%"'));
while ($row = $prep->fetch(PDO::FETCH_ASSOC)) {
$i++;
$result[$i]['subText'] = $row['hs_pk'];
$result[$i]['subText'] = $row['hs_text'];
$result[$i]['subDid'] = $row['hs_did'];
$result[$i]['devDid'] = $row['hd_did'];
$result[$i]['devText'] = $row['hd_text'];
$result[$i]['vendorText'] = $row['hv_text'];
$result[$i]['classText'] = $row['hc_text'];
}
$dbh = null;
}
catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
I've tried the following as well (SQL WHERE clause & prep->execute lines are all that change):
WHERE hs.hs_text LIKE CONCAT(\'%\', ?, \'%\')
$ret = $prep->execute(array($searchTerm));
WHERE hs.hs_text LIKE "%:searchTerm%"
$ret = $prep->execute(array(':searchTerm' => $searchTerm));
WHERE hs.hs_text LIKE ":searchTerm"
$ret = $prep->execute(array(':searchTerm' => '%'.$searchTerm.'%'));
etc...
$ret = $prep->execute(array(':searchTerm' => '"%'.$searchTerm.'%"'));
This is wrong. You don't need the double quotes.
WHERE hs.hs_text LIKE ":searchTerm"
$ret = $prep->execute(array(':searchTerm' => '%'.$searchTerm.'%'));
This is also wrong.
Try with:
$prep = $dbh->prepare($sql);
$ret = $prep->execute(array(':searchTerm' => '%'.$searchTerm.'%'));
Explanation: Prepared statements don't simply do a string-replace. They transport the data completely separate from the query. Quotes are only needed when embedding values into a query.
$prep = $dbh->prepare($sql);
$ret = $prep->execute(array('searchTerm' => $searchTerm));
Well, I solved this one. And quite frankly, I'm an idiot... Thank you all for seeing this and giving good feedback. The problem was a typo in a table name (which I changed, so nobody here would be able to see my issue to begin with...). The suggestions did lead me to find the issue, though, so thank you adam, jkndrkn and troelskn.
For the record, the following combination works well:
WHERE aw_hcl_subdevices.hs_text LIKE CONCAT(\'%\', ?, \'%\')
$ret = $prep->execute(array($searchTerm));

About the mysql_query -> mysql_fetch_array() procedure

Sample code:
$infoArray = array();
require_once("connectAndSelect.php");
// Connects to mysql and selects the appropriate database
$sql = "SOME SQL";
if($results = mysql_query($sql))
{
while($result = mysql_fetch_array($results, MYSQL_ASSOC))
{
$infoArray[] = $result;
}
}
else
{
// Handle error
}
echo("<pre>");
print_r($infoArray);
echo("</pre>");
In this sample code, I simply want to get the result of my query in $infoArray. Simple task, simple measures... not.
I would have enjoyed something like this:
$sql = "SOME SQL";
$infoArray = mysql_results($sql);
But no, as you can see, I have two extra variables and a while loop which I don't care for too much. They don't actually DO anything: I'll never use them again. Furthermore, I never know how to call them. Here I use $results and $result, which kind of represents what they are, but can also be quite confusing since they look so much alike. So here are my questions:
Is there any simpler method that I
don't know about for this kind of
task?
And if not, what names do you
give those one-use variables? Is
there any standard?
The while loop is really only necessary if you are expecting multiple rows to be returned. If you are just getting one row you can simply use mysql_fetch_array().
$query = "SOME SQL";
$result = mysql_query($query);
$row = mysql_fetch_array($result);
For single line returns is the standard I use. Sure it is a little clunky to do this in PHP, but at least you have the process broken down into debug-able steps.
Use PDO:
<?php
/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
$dbh = new PDO("mysql:host=$hostname;dbname=mysql", $username, $password);
$sql = "SELECT * FROM myTable";
$result = $dbh->query($sql)
//Do what you want with an actual dataset
}
catch(PDOException $e) {
echo $e->getMessage();
}
?>
Unless you are legacied into it by an existing codebase. DONT use the mysql extension. Use PDO or Mysqli. PDO being preferred out of the two.
Your example can be come a set of very consise statements with PDO:
// create a connection this could be done in your connection include
$db = new PDO('mysql:host=localhost;dbname=your_db_name', $user, $password);
// for the first or only result
$infoArray = $db->query('SOME SQL')->fetch(PDO::FETCH_ASSOC);
// if you have multiple results and want to get them all at once in an array
$infoArray = $db->query('SOME SQL')->fetchAll(PDO::FETCH_ASSOC);
// if you have multiple results and want to use buffering like you would with mysql_result
$stmt = $db->query('SOME SQL');
foreach($stmt as $result){
// use your result here
}
However you should only use the above when there are now variables in the query. If there are variables they need to be escaped... the easiest way to handle this is with a prepared statement:
$stmt = $db->prepare('SELECT * FROM some_table WHERE id = :id');
$stmt->execute(array(':id' => $id));
// get the first result
$infoArray = $stmt->fetch(PDO::FETCH_ASSOC);
// loop through the data as a buffered result set
while(false !== ($row = $stmt->fetch(PDO::FETCH_ASSOC))){
// do stuff with $row data
}

Store elememts of a datatable into an array

I have a database table made in phpmyadmin. I want the elements of the table to be stored row-wise in an array. I have three columns named edge_id, vertexA and VertexB. I want the elements of these three columns to be stored into an array.
I don't know those commands for PHP. Please help me out.
i have columns in my table named
"vertexa" and "vertexb",i want to
store the colums in two separate
arrays ...how can i do it??
The simplest way would be:
$db = new PDO('mysql:host=localhost;dbname=your_db_name;', $user, $password);
$vertices_a = array();
$vertices_b = array();
foreach($db->query('SELECT * from your_table_name') as $row){
$id = $row['edge_id'];
// add them to the proper array using the edge_id as the index -
// this assumes edge_id is unique
$vertices_a[$id] = $row['vertexa'];
$vertices_b[$id] $row['vertexb'];
}
So with PDO:
$db = new PDO('mysql:host=localhost;dbname=your_db_name;', $user, $password);
foreach($db->query('SELECT * from your_table_name') as $row){
echo $row['edge_id'];
echo $row['vertexA'];
echo $row['vertexB'];
}
Now if you need to use input data you need to escape it. The best way to do this is to use a prepared statement because escaping is handle when the parameters are bound to the query.
Lets say for example you want to use the edge_id supplied from a get request like mysite.com/show-boundry.php?edge=5...
$db = new PDO('mysql:host=localhost;dbname=your_db_name;', $user, $password);
// create a prepared statement
$stmt = $db->prepare('SELECT * from your_table_name WHERE edge_id = ?');
// execute the statement binding the edge_id request parameter to the prepared query
$stmt->execute(array($_GET['edge']));
// loop through the results
while(false !== ($row = $stmt->fetch(PDO::FETCH_ASSOC))){
echo $row['edge_id'];
echo $row['vertexA'];
echo $row['vertexB'];
}
Also you shouldnt use mixed case column/table names like vertexA instead use underscore separators like vertex_a.
You first need to connect to the database:
$link = mysql_connect('localhost','username','password');
if (!$link) {
// Cannot connect
}
$ret = mysql_select_db('database-name', $link);
if (!$ret) {
// Cannot select database
}
Then you need to execute your query:
$ret = mysql_query('SELECT * FROM `table`;', $link);
if (!$ret) {
// Query failed
}
Then you simply load each row:
$data = array();
while ($row = mysql_fetch_assoc($ret)) {
$data[] = $row;
}
Then you should free your request results
mysql_free_result($ret);
And voilĂ .
$res = mysql_query("............");
$row = mysql_fetch_array($res);
Checkout: mysql_fetch_array PHP page

using mysql_real_escape_string to cleans my queries

Is this the best way to do it?
Should I do this to every value in every query? GET and POST?
Is addslashes(mysql_real_escape_string()) overkill?
If you're using mysql_query() then yes, mysql_real_escape_string() is the best way. And yes, you have to apply it to every parameter you mix into your sql statement. E.g.
$query = "
SELECT
x,y,z
FROM
foo
WHERE
a = '". mysql_real_escape_string($_POST['a'], $mysql) . "'
AND b = '". mysql_real_escape_string($_POST['b'], $mysql) . "'
";
But you can also use prepared statements, e.g. with the pdo module. The parameters are transferred apart from the sql statement and therefore do not require escaping. It's more secure (since you can't forget the escaping or doing it wrong/for the wrong charset), often faster and very often even simpler than mixing the parameters into the statement. e.g.
$pdo = new PDO('mysql:host=localhost;dbname=test', '..', '..');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare("
INSERT INTO
foo (x,y,z)
VALUES
(:x,:y,:z)
");
$stmt->bindParam(':x', $x);
$stmt->bindParam(':y', $y);
$stmt->bindParam(':z', $z);
// insert all records (0,0,0) ... (0,1,0) ... (9,9,9)
for($x=0; $x<10; $x++) {
for($y=0; $y<10; $y++) {
for($z=0; $z<10; $z++) {
$stmt->execute();
}
}
}
edit: and addslashes(mysql_real_escape_string()) is not only overkill but also wrong.
You need to have a mysql connection already established in your script to use mysql_real_escape_string().
Have you tried PDO?
It prevents mysql injection attacks automatically.
$db = new PDO("mysql:host=$dbhost;dbname=$default_dbname", $dbusername, $dbuserpassword);
$st = $db->prepare ('SELECT * from my_table WHERE owner= ? and dog = ?');
$st->execute (array('Bob', 'Rex'));
$data = $st->fetchAll();
Might want to use htmlentities() on output though.

Categories