This question already has answers here:
Search Form with One or More (Multiple) Parameters
(2 answers)
Closed 4 years ago.
I have search field in which user can specify a lot of values to search like price, surface, year, garden, balcony etc.
In my search there is not even one field required every one is optional so user can provide 0 inputs filled or all.
Basically all this info's are saved in my database but I don't really know how to structure my code.
At the moment I have PHP file which I call from front and in this file I'm checking which field was filled and I'm executing method from class which do select to db and return data. This is working fine for every input separately but when I combain for example 2 different fields like price and surface then none of methods will be executed.
Im basically asking about an idea for architecture of search where user can fullfill many different fields. Im not using any PHP framework.
I could do something like:
if(a & b & c & d & e & f) then execute method a
if(a & b & c & d & e) then execute method b
if(a & b & c & d) then execute method c
and so on.. where this letters(a, b, c etc...) are $_POST['something'] but I would have a lots of if's to check which POST (which inputs) user fullfill and sent. Later on I would need to create a lot of methods in class with different SELECTs to db basing on which POST we have... I don't think that's best solution because I would basically repeat my code.
Something like this
$sql = 'SELECT * FROM sometable';
$where = [];
$params = [];
if($a){
$where[] = 'a = :a';
$params[':a'] = $a;
}
if($b){
$where[] = 'b = :b';
$params[':b'] = $b;
}
if(!empty($where)){
$sql .= ' WHERE '.implode(' AND ', $where);
}
$stmt = $PDO->prepare($sql);
$res = $stmt->execute($params);
And so On.
It almost always preferable to use and array and implode for things like this instead of concatenation. Often concatenation will leave you with a hanging "separator" in this case " AND ". For example if we tried this with concatenation:
//if we put WHERE here and then nothing passes our conditions we wind up with:
//"SELECT * FROM sometable WHERE" which wont work
$sql = 'SELECT * FROM sometable ';
//we still need something like an array if we want to prepare our query.
//which is something we should always do
$params = [];
if($a){
//if we put WHERE here, then what if this condition doesn't pass
//do we put it in the next condition? How do we tell. .
$sql .= 'WHERE a = :a AND ';
$params[':a'] = $a;
}
if($b){
//again if the first condition didn't pass how do we know to put "WHERE" here.
//"SELECT * FROM sometable b = :b AND" which wont work
$sql .= 'b = :b AND ';
$params[':b'] = $b;
}
if($c){
//lets say the first 2 conditions passes but this last one failed
//"SELECT * FROM sometable WHERE a = :a AND b = :b AND" which wont work
$sql .= 'c = :c';
$params[':c'] = $c;
}
//we would need to do something like this to trim the last "AND" off
$sql = preg_replace('/\sAND\s$/', '', $sql);
//--------------------
//now if we were prepending "AND" instead of appending it, we're no better off.
//--------------------
//we can fix the where issue by using a string variable (and testing it latter)
$where = '';
if($a){
$where .= 'a = :a';
$params[':a'] = $a;
}
if($b){
//However lets say the first condition failed, we get this:
//"SELECT * FROM sometable WHERE AND b = :b" which wont work
$where .= ' AND b = :b';
$params[':b'] = $b;
//--------------------------
//so in every condition following we would have to test $where
//and if its not empty then we can prepend "AND"
if(!empty($where)) $where .= ' AND ';
$where .= 'b = :b';
$params[':b'] = $b;
}
if($c){
if(!empty($where)) $where .= ' AND ';
$where .= 'c = :c';
$params[':c'] = $c;
}
//finally to fix the "WHERE" issue we need to do something like this:
if(empty($where)) $sql .= ' WHERE '.$where;
//we could also try something like this in every condition:
if($d){
if(empty($where)) $where .= ' WHERE ';
//However, this breaks our fix for prepending "AND", because
//$where will never be empty when we test it.
//if(!empty($where)) $where .= ' AND ';
$where .= 'd = :d';
$params[':d'] = $d;
}
Hopefully that all makes sense. It's just so much easier to use an array and implode it later.
I just wanted to show that to help visualize the issues with concatenation. We wind writing more code, using the same number of variables and double the conditional logic. Or we can get into complicated things like Regex to trim the hanging AND off etc.
Hope that helps!
BECAUSE I mentioned it in the comments.
If you are using "OR" you can of course do the same thing with that, but typically "OR" will cause a full scan of the DB. It's just the way OR works. When we use "AND" the DB (basically) takes the return set and applies the next condition to that, because both have to pass. However, with "OR" rows that failed the first condition could still pass if the second condition passes. So the DB must scan the full record set for each or, as well as keep track of all the rows that passed in the previous conditions. It's just the way the logic works for "OR".
Now for improved "OR" performance we can use a sub-query that is a union. Like this:
$sql = 'SELECT * FROM sometable AS t';
$union = [];
$params = [];
if($a){
$union[] = 'SELECT id FROM sometable WHERE a = a:';
$params[':a'] = $a;
}
if($b){
$union[] = 'SELECT id FROM sometable WHERE b = b:';
$params[':b'] = $b;
}
if(!empty($union)){
$sql .= '
JOIN( '.
implode(' UNION ', $union).
' ) AS u ON t.id = u.id
}
What we wind up with is something like this query:
SELECT
*
FROM
sometable AS t
JOIN (
SELECT id FROM sometable WHERE a = a:
UNION
SELECT id FROM sometable WHERE b = b:
) AS u ON t.id = u.id
When we use "OR" as our dataset grows the DB must store these results in temp table as well as search the entire dataset. Because we are pulling all the columns in the table, this dataset will quickly grow. Once it hits a certian size it will get swapped to Disc and our performance will take a big hit for that.
With the Union query, we also create a temp table. But because we are only concerned with pulling out the ids this temp table will be very small. Union unlike Union ALL will also automatically remove duplicate records further reducing our dataset. So we want to use Union and not Union ALL.
Then we join this back on the table in the outer query and use that to pull the all the columns from just the rows that we need.
Basically we are accepting the fact that we need a temp table and minimizing the impact of that.
This might not seem like it would be much faster, and in some cases it might not be (when no swapping happens). But for me, using a query like you describe where users can search on multiple fields, I was able to reduce the time it took from about 15 seconds to under 1 second. My query had several joins in it such as if a user put in a state, I had to join on participant then participants_addresses (junction table) and then addresses and then finally on states. But if they put in a phone I had to join participant > participants_phones > phone etc.
I can't guarantee this will work in every case and you should use Explain and SQL_NO_CACHE when benchmarking your queries. For example EXPLAIN SELECT SQL_NO_CACHE * FROM .... Explain will tell you how the indexes are working and No Cache prevents the DB from caching the query if you run it multiple times. Caching will make it look like it is fast when it's really not.
You can do something similar when sorting, which also kills performance.
SELECT
*
FROM
sometable AS t
JOIN (
SELECT id FROM sometable WHERE a = a: ORDER BY date DESC
) AS u ON t.id = u.id
This has a similar effect of only sorting the id's in the temp table (instead of the whole dataset), and then when we join it, it actually keeps the order the ids are in. I forget if the order of the subquery vs the outer query matter.
For fun you can even combine the two with 2 nested sub-queries, with the Union as the deepest query (it's something like this).
SELECT
*
FROM
sometable AS t
JOIN (
SELECT id FROM sometable AS t0 JOIN (
SELECT id FROM sometable WHERE a = a:
UNION
SELECT id FROM sometable WHERE b = b:
) AS u ON t0.id = u.id
ORDER BY t0.date DESC
) AS t1 ON t.id = t1.id
It can get pretty complicated though ... lol.
Anyway, I was bored and maybe, just maybe, it will work for someone like it did for me. (this is what happens when I don't get sleep) :)
UPDATE
IF you have problems with the parameters you can output the SQL with the values filled in by doing this:
echo str_replace(array_keys($params), $params, $sql)."\n";
But use this only for Debugging, not for putting the data into the query because that would defeat the purpose of using prepared statements and open you up to SQLInjection attacks. That said, it can make it easier to see if you are missing anything or have any spelling errors. I also use this when I just want to test the query in PHPMyAdmin, but am to lazy to cut an paste the data into it. Then I just copy the output put it in PHPMyAdmin and then I can rule out any issues with PHP or tweak the query if need be.
You can also have issues if you have to many elements in the array, AKA extra placeholders that are not in the query.
For that you can do
//count the number of : in the query
$num_placeholders = substr_count(':', $sql);
//count the elements in the array
$num_params = count($params);
if($num_placeholders > $num_params ) echo "to many placeholders\n";
else if($num_placeholders < $num_params ) echo "to many params\n";
One last thing to be mindful of when mixing "AND" and "OR" is stuff like this
SELECT * FROM foo WHERE arg1 = :arg1 OR arg2 = :arg2 AND arg3 = :arg3
The way it executes this is like this
SELECT * FROM foo WHERE arg1 = :arg1 OR (arg2 = :arg2 AND arg3 = :arg3)
This will return all rows that match arg1 regardless of the rest of the query.
Most of the time this would not be what you want. You would actually want it to do it this way:
SELECT * FROM foo WHERE (arg1 = :arg1 OR arg2 = :arg2) AND arg3 = :arg3
Which is called an "Exclusive OR". This will return all rows that match arg1 OR arg2 AND arg3
Hope that helps.
You could also create an wanted list of nesseccary items and Check If each Item is Set by the PHP function isset().
There are many conflicting statements around. What is the best way to get the row count using PDO in PHP? Before using PDO, I just simply used mysql_num_rows.
fetchAll is something I won't want because I may sometimes be dealing with large datasets, so not good for my use.
Do you have any suggestions?
$sql = "SELECT count(*) FROM `table` WHERE foo = ?";
$result = $con->prepare($sql);
$result->execute([$bar]);
$number_of_rows = $result->fetchColumn();
Not the most elegant way to do it, plus it involves an extra query.
PDO has PDOStatement::rowCount(), which apparently does not work in MySql. What a pain.
From the PDO Doc:
For most databases,
PDOStatement::rowCount() does not
return the number of rows affected by
a SELECT statement. Instead, use
PDO::query() to issue a SELECT
COUNT(*) statement with the same
predicates as your intended SELECT
statement, then use
PDOStatement::fetchColumn() to
retrieve the number of rows that will
be returned. Your application can then
perform the correct action.
EDIT: The above code example uses a prepared statement, which is in many cases is probably unnecessary for the purpose of counting rows, so:
$nRows = $pdo->query('select count(*) from blah')->fetchColumn();
echo $nRows;
As I wrote previously in an answer to a similar question, the only reason mysql_num_rows() worked is because it was internally fetching all the rows to give you that information, even if it didn't seem like it to you.
So in PDO, your options are:
Use PDO's fetchAll() function to fetch all the rows into an array, then use count() on it.
Do an extra query to SELECT COUNT(*), as karim79 suggested.
Use MySQL's FOUND_ROWS() function UNLESS the query had SQL_CALC_FOUND_ROWS or a LIMIT clause (in which case the number of rows that were returned by the query and the number returned by FOUND_ROWS() may differ). However, this function is deprecated and will be removed in the future.
As it often happens, this question is confusing as hell. People are coming here having two different tasks in mind:
They need to know how many rows in the table
They need to know whether a query returned any rows
That's two absolutely different tasks that have nothing in common and cannot be solved by the same function. Ironically, for neither of them the actual PDOStatement::rowCount() function has to be used.
Let's see why
Counting rows in the table
Before using PDO I just simply used mysql_num_rows().
Means you already did it wrong. Using mysql_num_rows() or rowCount() to count the number of rows in the table is a real disaster in terms of consuming the server resources. A database has to read all the rows from the disk, consume the memory on the database server, then send all this heap of data to PHP, consuming PHP process' memory as well, burdening your server with absolute no reason.
Besides, selecting rows only to count them simply makes no sense. A count(*) query has to be run instead. The database will count the records out of the index, without reading the actual rows and then only one row returned.
For this purpose the code suggested in the accepted answer is fair, save for the fact it won't be an "extra" query but the only query to run.
Counting the number rows returned.
The second use case is not as disastrous as rather pointless: in case you need to know whether your query returned any data, you always have the data itself!
Say, if you are selecting only one row. All right, you can use the fetched row as a flag:
$stmt->execute();
$row = $stmt->fetch();
if (!$row) { // here! as simple as that
echo 'No data found';
}
In case you need to get many rows, then you can use fetchAll().
fetchAll() is something I won't want as I may sometimes be dealing with large datasets
Yes of course, for the first use case it would be twice as bad. But as we learned already, just don't select the rows only to count them, neither with rowCount() nor fetchAll().
But in case you are going to actually use the rows selected, there is nothing wrong in using fetchAll(). Remember that in a web application you should never select a huge amount of rows. Only rows that will be actually used on a web page should be selected, hence you've got to use LIMIT, WHERE or a similar clause in your SQL. And for such a moderate amount of data it's all right to use fetchAll(). And again, just use this function's result in the condition:
$stmt->execute();
$data = $stmt->fetchAll();
if (!$data) { // again, no rowCount() is needed!
echo 'No data found';
}
And of course it will be absolute madness to run an extra query only to tell whether your other query returned any rows, as it suggested in the two top answers.
Counting the number of rows in a large resultset
In such a rare case when you need to select a real huge amount of rows (in a console application for example), you have to use an unbuffered query, in order to reduce the amount of memory used. But this is the actual case when rowCount() won't be available, thus there is no use for this function as well.
Hence, that's the only use case when you may possibly need to run an extra query, in case you'd need to know a close estimate for the number of rows selected.
I ended up using this:
$result = $db->query($query)->fetchAll();
if (count($result) > 0) {
foreach ($result as $row) {
echo $row['blah'] . '<br />';
}
} else {
echo "<p>Nothing matched your query.</p>";
}
This post is old but Getting row count in php with PDO is simple
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
This is super late, but I ran into the problem and I do this:
function countAll($table){
$dbh = dbConnect();
$sql = "select * from `$table`";
$stmt = $dbh->prepare($sql);
try { $stmt->execute();}
catch(PDOException $e){echo $e->getMessage();}
return $stmt->rowCount();
It's really simple, and easy. :)
This is an old post, but getting frustrated looking for alternatives. It is super unfortunate that PDO lacks this feature, especially as PHP and MySQL tend to go hand in hand.
There is an unfortunate flaw in using fetchColumn() as you can no longer use that result set (effectively) as the fetchColumn() moves the needle to the next row. So for example, if you have a result similar to
Fruit->Banana
Fruit->Apple
Fruit->Orange
If you use fetchColumn() you can find out that there are 3 fruits returned, but if you now loop through the result, you only have two columns, The price of fetchColumn() is the loss of the first column of results just to find out how many rows were returned. That leads to sloppy coding, and totally error ridden results if implemented.
So now, using fetchColumn() you have to implement and entirely new call and MySQL query just to get a fresh working result set. (which hopefully hasn't changed since your last query), I know, unlikely, but it can happen. Also, the overhead of dual queries on all row count validation. Which for this example is small, but parsing 2 million rows on a joined query, not a pleasant price to pay.
I love PHP and support everyone involved in its development as well as the community at large using PHP on a daily basis, but really hope this is addressed in future releases. This is 'really' my only complaint with PHP PDO, which otherwise is a great class.
Answering this because I trapped myself with it by now knowing this and maybe it will be useful.
Keep in mind that you cant fetch results twice. You have to save fetch result into array, get row count by count($array), and output results with foreach.
For example:
$query = "your_query_here";
$STH = $DBH->prepare($query);
$STH->execute();
$rows = $STH->fetchAll();
//all your results is in $rows array
$STH->setFetchMode(PDO::FETCH_ASSOC);
if (count($rows) > 0) {
foreach ($rows as $row) {
//output your rows
}
}
If you just want to get a count of rows (not the data) ie. using COUNT(*) in a prepared statement then all you need to do is retrieve the result and read the value:
$sql = "SELECT count(*) FROM `table` WHERE foo = bar";
$statement = $con->prepare($sql);
$statement->execute();
$count = $statement->fetch(PDO::FETCH_NUM); // Return array indexed by column number
return reset($count); // Resets array cursor and returns first value (the count)
Actually retrieving all the rows (data) to perform a simple count is a waste of resources. If the result set is large your server may choke on it.
Have a look at this link:
http://php.net/manual/en/pdostatement.rowcount.php
It is not recommended to use rowCount() in SELECT statements!
When it is matter of mysql how to count or get how many rows in a table with PHP PDO I use this
// count total number of rows
$query = "SELECT COUNT(*) as total_rows FROM sometable";
$stmt = $con->prepare($query);
// execute query
$stmt->execute();
// get total rows
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$total_rows = $row['total_rows'];
credits goes to Mike # codeofaninja.com
To use variables within a query you have to use bindValue() or bindParam(). And do not concatenate the variables with " . $variable . "
$statement = "SELECT count(account_id) FROM account
WHERE email = ? AND is_email_confirmed;";
$preparedStatement = $this->postgreSqlHandler->prepare($statement);
$preparedStatement->bindValue(1, $account->getEmail());
$preparedStatement->execute();
$numberRows= $preparedStatement->fetchColumn();
GL
A quick one liner to get the first entry returned. This is nice for very basic queries.
<?php
$count = current($db->query("select count(*) from table")->fetch());
?>
Reference
I tried $count = $stmt->rowCount(); with Oracle 11.2 and it did not work.
I decided to used a for loop as show below.
$count = "";
$stmt = $conn->prepare($sql);
$stmt->execute();
echo "<table border='1'>\n";
while($row = $stmt->fetch(PDO::FETCH_OBJ)) {
$count++;
echo "<tr>\n";
foreach ($row as $item) {
echo "<td class='td2'>".($item !== null ? htmlentities($item, ENT_QUOTES):" ")."</td>\n";
} //foreach ends
}// while ends
echo "</table>\n";
//echo " no of rows : ". oci_num_rows($stmt);
//equivalent in pdo::prepare statement
echo "no.of rows :".$count;
For straight queries where I want a specific row, and want to know if it was found, I use something like:
function fetchSpecificRow(&$myRecord) {
$myRecord = array();
$myQuery = "some sql...";
$stmt = $this->prepare($myQuery);
$stmt->execute(array($parm1, $parm2, ...));
if ($myRecord = $stmt->fetch(PDO::FETCH_ASSOC)) return 0;
return $myErrNum;
}
The simplest way, it is only 2 lines,
$sql = $db->query("SELECT COUNT(*) FROM tablename WHERE statement='condition'");
echo $sql->fetchColumn();
So, the other answers have established that rowCount() shouldn't be used to count the rows of a SELECT statement. The documentation even says, that :
PDOStatement::rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed by the corresponding PDOStatement object.
https://web.archive.org/web/20220409162106/https://www.php.net/manual/en/pdostatement.rowcount.php
So it's okay for other queries, but not great for SELECT. Most answers suggest that you should make two queries, one to count rows, and one to get the subset of records you need. However, you could query the row count and your subset of the data in one request. This is a bit of an exercise in code golf, but could actually prove more efficient than two requests if the request time is a bit costly and these requests are made frequently.
If you're in PostgreSQL you can provide clean JSON output, like so:
WITH mytable as (VALUES(1,2,3),(4,5,6),(7,8,9),(10,11,12))
SELECT
jsonb_build_object(
'rowcount', (SELECT count(1) FROM mytable)
,'data', (
SELECT jsonb_agg(data.*)
FROM (
SELECT *
FROM mytable
WHERE column1 > 1 -- pagination offset
ORDER BY column1
LIMIT 2 -- page size
) as data
)
) jsondata
Output:
{"data": [
{
"column1": 4,
"column2": 5,
"column3": 6
},
{
"column1": 7,
"column2": 8,
"column3": 9
}
],
"rowcount": 4
}
If you're not in postgres, those functions won't be available, but you could do this:
WITH mytable as (VALUES(1,2,3),(4,5,6),(7,8,9),(10,11,12))
SELECT
(SELECT count(1) FROM mytable) as rowcount
,data.*
FROM (
SELECT *
FROM mytable as mytable(column1, column2, column3)
WHERE column1 > 1 -- pagination offset
ORDER BY column1
LIMIT 2 -- page size
) as data
but it will return the rowcount on every row, which might be a bit wasteful:
rowcount
column1
column2
column3
4
4
5
6
4
7
8
9
when you make a COUNT(*) in your mysql statement like in
$q = $db->query("SELECT COUNT(*) FROM ...");
your mysql query is already counting the number of result why counting again in php? to get the result of your mysql
$q = $db->query("SELECT COUNT(*) as counted FROM ...");
$nb = $q->fetch(PDO::FETCH_OBJ);
$nb = $nb->counted;
and $nb will contain the integer you have counted with your mysql statement
a bit long to write but fast to execute
Edit:
sorry for the wrong post but as some example show query with count in, I was suggesting using the mysql result, but if you don't use the count in sql fetchAll() is efficient, if you save the result in a variable you won't loose a line.
$data = $dbh->query("SELECT * FROM ...");
$table = $data->fetchAll(PDO::FETCH_OBJ);
count($table) will return the number of row and you can still use the result after like $row = $table[0] or using a foreach
foreach($table as $row){
print $row->id;
}
Here's a custom-made extension of the PDO class, with a helper function to retrieve the number of rows included by the last query's "WHERE" criteria.
You may need to add more 'handlers', though, depending on what commands you use. Right now it only works for queries that use "FROM " or "UPDATE ".
class PDO_V extends PDO
{
private $lastQuery = null;
public function query($query)
{
$this->lastQuery = $query;
return parent::query($query);
}
public function getLastQueryRowCount()
{
$lastQuery = $this->lastQuery;
$commandBeforeTableName = null;
if (strpos($lastQuery, 'FROM') !== false)
$commandBeforeTableName = 'FROM';
if (strpos($lastQuery, 'UPDATE') !== false)
$commandBeforeTableName = 'UPDATE';
$after = substr($lastQuery, strpos($lastQuery, $commandBeforeTableName) + (strlen($commandBeforeTableName) + 1));
$table = substr($after, 0, strpos($after, ' '));
$wherePart = substr($lastQuery, strpos($lastQuery, 'WHERE'));
$result = parent::query("SELECT COUNT(*) FROM $table " . $wherePart);
if ($result == null)
return 0;
return $result->fetchColumn();
}
}
You can combine the best method into one line or function, and have the new query auto-generated for you:
function getRowCount($q){
global $db;
return $db->query(preg_replace('/SELECT [A-Za-z,]+ FROM /i','SELECT count(*) FROM ',$q))->fetchColumn();
}
$numRows = getRowCount($query);
There is a simple solution. If you use PDO connect to your DB like this:
try {
$handler = new PDO('mysql:host=localhost;dbname=name_of_your_db', 'your_login', 'your_password');
$handler -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo $e->getMessage();
}
Now, if you want to know how many rows are existing in your table and you have for example column 'id' as the primary key, the query to DB will be:
$query = $handler->query("SELECT id FROM your_table_name");
And finally, to get the amount of the rows matching your query, write like this:
$amountOfRows = $query->rowCount();
Or you can write:
$query = $handler ->query("SELECT COUNT(id) FROM your_table_name");
$amountOfRows = $query->rowCount();
Or, if you want to know how many products there are in the table 'products' have the price between 10 and 20, write this query:
$query = $handler ->query("SELECT id FROM products WHERE price BETWEEN 10 AND
20");
$amountOfRows = $query->rowCount();
I have to update the same value of multiple rows in a table and i would like to avoid multiple mysql_query using a foreach loop that scan every value of an array.
I try to explain using an example.
This is the solution of the problem using a foreach loop
$array=array(1,2,3);
foreach($array as $val){
$sql = "UPDATE mytable SET val_to_update = XX WHERE id = $val";
mysql_query($sql);
}
I didn't like to start hammering database with a crazy number of queries, because the number of element of the array is not fixed, and can also be large.
I have considered using the IN clause of SQL language but without knowing the number of parameters can not seem to find a solution.
Thinked at something like this, but I do not know if it is achievable:
$sql= "UPDATE Illustration SET polyptychID = $id_polyptych WHERE illustrationID IN (?,?,?);
and then bind all the parameters using a foreach loop for scan the array of parameters.
The problem, as I said, is that i don't know the number, so i can't place the right number of ? in sql query and, if I'm not mistaken, the number of occurrences of ? parameters must be the same as the binded parameters.
Anyone have solved a problem like this?
If you are sure, that the array is containing integers, why don't you do it like this:
$array=array(1,2,3);
if (sizeof($array) > 0 {
$sql = "UPDATE mytable SET val_to_update = XX WHERE id IN(".implode(',', $array).")";
mysql_query($sql);
}
If you want to use prepared statement you could create your sql using this code:
$array=array(1,2,3);
if (sizeof($array) > 0 {
$placeholders = array();
for($i=0; $i<sizeof($array); $i++) {
$placeholders[] = '?';
}
$sql = "UPDATE mytable SET val_to_update = XX WHERE id IN(".implode(',', $placeholders).")";
// .....
}
If the values in the $array exists in another table you could use something like this:
$sql = "UPDATE mytable SET val_to_update = XX WHERE id IN (SELECT id FROM another_table WHERE condition = 1)";
I am trying to run an update on a MySQL table that will swap the contents of 1 field with the contents of another (both in the same table).
Here is the basic structure of the table:
id SKU related
== === =======
1234 SKU001 1234,8889,2209
5544 SKU855 2209,1234
2209 SKUYYT 5544
What I need to do is swap the related field for the relavent SKU (so that it lists the sku's instead of the id's). So it would end up looking like this:
id SKU related
== === =======
1234 SKU001 SKU001,SKU111,SKUYYT
5544 SKU855 SKUYYT,SKU001
2209 SKUYYT SKU855
So what I'm trying to achieve is the id in the related field would have been replaced with the SKU that relates to that id.
What I'm currently doing is this:
SELECT id, sku FROM my_table
Then looping through all id's with PHP and updating the table like so:
UPDATE my_table SET related = REPLACE(related, '5544', 'SKU855');
This seems to work but I have 9000+ rows and multiple id's in the related column and it's taking hours to complete
Can anyone suggest a better way of achieving this? (Ideally in MySQL)
Many thanks
If you have access to PHP with this update, I would suggest you do the following:
$rows = some_method_to_load_all_rows_from_your_table();
$id_to_sku = array();
// Loop through all rows to cache their SKU numbers and directly associate them to their row's PK.
foreach ($rows as $row) {
$id_to_sku[$row['id']] = $row['SKU'];
}
// Loop through all rows a second time to then break apart, find, and replace the related IDs.
foreach ($rows as $index => $row) {
$related_ids = explode(',', $row['related']);
foreach($related_ids as &$related_id) {
if (isset($id_to_sku[$related_id]]) {
$related_id = $id_to_sku[$related_id]];
} // else the ID couldn't be found.
}
$rows[$index]['related'] = implode(',', $related_ids);
}
// $rows now contains the related string with the SKU numbers instead of ID numbers.
method_to_update_all_rows_with_new_related_value($rows);
Essentially, doing this in raw SQL is going to be very difficult, and you will be left with a very complex query. The best way is to abstract the detecting and replacing of the IDs / SKU numbers into PHP, where this becomes a much easier and more readable platform to work on.
The code loads all rows in the table, then loops through each row to cache it’s ID and SKU number separately.
Then, we loop through each row again, break apart the related field into an array of IDs, replace those IDs with their correct SKU code using the cache we created, and insert them back into $rows.
$rows becomes the new updated table set. From there, it’s a case of creating a giant UPDATE statement, or looping through each row and executing a single UPDATE statement for each one. Looping in PHP and issuing an UPDATE for each row would be slower as making a query has overhead. But you can also implement the loop in SQL, using stored procedure, avoiding the overhead on communication between your application and database.
/*
create my_table_temp with same structure as in my_table
*/
$mapping = array();
$sth = $dbh->prepare("SELECT id, SKU FROM my_table");
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
foreach($rows as $row) {
$mapping[$row['id']] = $row['SKU'];
}
$sql = 'insert into my_table_temp(id, SKU, related) VALUES ';
$params = array();
$sth = $dbh->prepare("SELECT * FROM my_table");
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
foreach($rows as $row) {
$related = explode(',', $row['related']);
foreach($related as &$val) {
$val = $mapping[$val];
}
$sql .= '(?, ?, ?),';
$params[] = $row['id'];
$params[] = $row['SKU'];
$params[] = implode(',', $related);
}
$sql = rtrim($sql, ',');
$sth = $dbh->prepare($sql);
$sth->execute($params);
/*
update my_table
set related = q.related
from (
select sku, related
from my_table_temp
) q
where my_table.sku = q.sku;
drop table my_table_temp;
*/
When You are asking to do it in mysql , then it would be possible by using CURSOR and dynamic query.
DELIMITER //
CREATE PROCEDURE `all_update`()
BEGIN
DECLARE ids INT;
DECLARE sname VARCHAR(255);
DECLARE finished INTEGER DEFAULT 0;
DECLARE cursor1 CURSOR FOR SELECT id,SKU FROM testing;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cursor1;
update_table: LOOP
FETCH cursor1 INTO ids,sname;
IF finished = 1 THEN
LEAVE update_table;
END IF;
SET #s = CONCAT('UPDATE testing SET related = REPLACE(related,', ids,', "',sname,'")');
PREPARE stmt1 FROM #s;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END LOOP update_table;
CLOSE cursor1;
END//
Fiddle is http://sqlfiddle.com/#!2/298a7/1
I'm trying to update multiple rows in one table in MySQL database by doing this. And its not working.
$query = "UPDATE cart SET cart_qty='300' WHERE cart_id = '21';
UPDATE cart SET cart_qty='200' WHERE cart_id = '23';
UPDATE cart SET cart_qty='100' WHERE cart_id = '24';";
mysql_query($query,$link);// $link is specified above
Anyone know what is wrong with this.
From the PHP documentation:
mysql_query() sends a unique query (multiple queries are not supported)
The ; separates SQL statements, so you need to separate the queries if you want to continue using the mysql_query function...
mysql_query can't use multiple queries.
The easiest thing is to just run them separately. I believe you can do multi query but I haven't tried it.
$updateArray = array(21=>300,23=>200,24=>100);
foreach($updateArray as $id=>$value)
{
$query = "UPDATE cart SET cart_qty='$value' WHERE cart_id = '$id'";
mysql_query($query,$link);// $link is specified above
}
This will accept a combination of IDs and their corresponding cart value. Looping though, it builds the query and executes it. The array can then come from a variety of sources (results from another query, form inputs or, as in this case, hard-coded values)
Update:
If you really need to execute all in one, heres the PHP info on multi query:
mysqli::multi_query
You can do it this way:
UPDATE table
SET col1 = CASE id
WHEN id1 THEN id1_v1,
WHEN id2 THEN id2_v1
END
col2 = CASE id
WHEN id1 THEN id1_v2,
WHEN id2 THEN id2_v2
END
WHERE id IN (id1, id2)
This example shows updating two different columns in two different rows so you can expand this to more rows and columns by cludging together a query like this. There might be some scaling issues that makes the case statement unsuitable for a very large number of rows.
You'll need to send them as separate queries. Why not add the queries as strings to an array, then iterate through that array sending each query separtely?
Also check this thread for another idea
This isn't the best method.. But if you need to do multiple queries you could use something like...
function multiQuery($sql)
{
$query_arr = explode(';', $sql);
foreach ($query_arr as $query)
{
mysql_query($query);
}
}
another example of a helper query
function build_sql_update($table, $data, $where)
{
$sql = '';
foreach($data as $field => $item)
{
$sql .= "`$table`.`$field` = '".mysql_real_escape_string($item)."',";
}
// remove trailing ,
$sql = rtrim($sql, ',');
return 'UPDATE `' . $table .'` SET '.$sql . ' WHERE ' .$where;
}
echo build_sql_update('cart', array('cart_qty' => 1), 'cart_id=21');