Optimize this SQL query - php

I've got a SQL query within a foreach loop. Sometimes there can be many, and I mean a lot of queries to do, depending on several criteria, up to 78 queries potentially.
Now, I know that premature optimisation is root cause of all evil, but I don't want to see 78 queries - it's just not healthy.
Here's the code:
$crumbs = explode(",", $user['data']['depts']);
foreach ($crumbs as &$value) {
$data = $db->query("SELECT id FROM tbl_depts WHERE id = '" . $value . "'");
$crumb = $data->fetch_assoc();
$dsn = $db->query("SELECT msg, datetime FROM tbl_motd WHERE deptid = '" . $value . "'");
$motd = $dsn->fetch_assoc();
if ($motd['msg'] != "") {
<?php echo $motd['msg']; ?>
}
}
Can I make it any better?

Use IN MySQL operator to search over a set of values for id:
$ids = '"' . implode('", "',$crumbs) . '"';
$query1 = "SELECT id FROM tbl_depts WHERE id IN (" . $ids . ")";
$query2 = "SELECT msg, datetime FROM tbl_motd WHERE deptid IN (" . $ids . ")";
And so you will not need to retrieve all data you need using foreach loop, so you will have only 2 queries instead of 78.
Example: I have a table named table with 10 records which ids are: 1,2,3,4,5,6,7,8,9,10 (auto-incremented). I know I need records with ids 1,5,8. My query will be:
$sql = "SELECT * FROM `table` WHERE id in (1,5,8);";
And I don't understand why do you need to use & operator in foreach loop if you don't modify the $crubms arrays values.

I think this is want you want.
SELECT msg, datetime
FROM tbl_depts td
INNER JOIN tbl_motd tm ON td.id = tm.deptid

Related

PHP: How to pass multiple values to SELECT query

I am new to PHP and hope someone can help me with this.
I currently use the below lines to retrieve a value from a db and to output it as an array with the item's ID and value which works as intended.
Now I would need to do the same for multiple items so my input ($tID) would be an array containing several IDs instead of just a single ID and I would need the query to do an OR search for each of these IDs.
I was thinking of using a foreach loop for this to append " OR " to each of the IDs but am not sure if this is the right way to go - I know the below is not working, just wanted to show my thoughts here.
Can someone help me with this and tell me how to best approach this ?
My current PHP:
$content = "";
$languageFrm = $_POST["languageFrm"];
$tID = $_POST["tID"];
$stmt = $conn->prepare("SELECT tID, " . $languageFrm . " FROM TranslationsMain WHERE tID = ? ORDER BY sortOrder, " . $languageFrm);
$stmt->bind_param("s", $tID);
$stmt->execute();
$result = $stmt->get_result();
while($arr = $result->fetch_assoc()){
$content[] = array("ID" => $arr["tID"], "translation" => $arr[$languageFrm]);
}
My thought:
foreach($tID as $ID){
$ID . " OR ";
}
Many thanks for any help,
Mike
There are two approaches, assuming $tID is an array of IDs
Using MySQL IN() clause
This will work also when $tID is not an array, but a single scalar value.
$tID = array_map('intval', (array)$tID); // prevent SQLInjection
if(!empty($tID)) {
$query .= ' WHERE tID IN(' . implode(',', $tId) . ')';
} else {
$query .= ' WHERE 0 = 1';
}
Using OR clause, as you suggested
A bit more complicated scenario.
$conds = array();
foreach($tID as $ID) {
$conds[] = 'tID = ' . intval($ID);
}
if(!empty($conds)) {
$query .= ' WHERE (' . implode(' OR ', $conds) . ')';
} else {
$query .= ' WHERE 0 = 1';
}
As per above conditions you can try with implode();
implode($tID,' OR ');
You can also use IN condition instead of OR something like this.
implode($tID,' , ');

PHP MySQL large quantity of queries being passed,

I have a PHP script that is passing large quantities of queries to a DB very quickly. Does a MySQL DB queue the queries as they come in if it can't process them at the same speed they are being passed, or do they get lost?
My program has written and passed syntactically correct queries to the DB, but the DB is very far behind in terms of information contained in tables and number of tables.
Some example code (I am slightly new to PHP, so my code/coding style may be horrifying):
//If table has one primary key
$val = $tblColPkArray[0];
$pkInsert = ", PRIMARY KEY (". $val['COLUMN_NAME'] .")";
$pkColName = $val['COLUMN_NAME'];
$string = ltrim($string, ",");
$oneCreateTableQuery = $beginning . $string . $pkInsert . $end;
echo $oneCreateTableQuery . "\n";
$newLink->query($oneCreateTableQuery);
$pkValuesInOld = "SELECT " . $pkColName . " FROM " . $tables . ";";
$pkValsResult = $link->query($pkValuesInOld);
while($pkValues = $pkValsResult->fetch(PDO::FETCH_ASSOC))
{
$pkRowValuesQuery = "SELECT * FROM " . $tables . " WHERE " . $pkColName . " = '" . $pkValues[$pkColName] . "';";
echo $pkRowValuesQuery . "\n";
$valsOfPkInOldTable = $link->query($pkRowValuesQuery);
while($pkVals = $valsOfPkInOldTable->fetch(PDO::FETCH_ASSOC))
{
//var_dump($ckVals);
$insertBeginning = "INSERT INTO " . $tables . "(";
$insertValuesSection = ") VALUES (";
$insertEnd = ");";
$keys = "";
$rowValues = "";
foreach($pkVals as $key => $value)
{
$keys = $keys . ", " . $key;
$rowValues = $rowValues . ", '" . $value . "'";
}
$insertStatement = $insertBeginning . ltrim($keys, ",") . $insertValuesSection . ltrim($rowValues, ",") . $insertEnd;
echo $insertStatement . "\n";
$newLink->query($insertStatement);
}//While loop: Inserting values of old table into new table via INSERT INTO statement
//unset();
} //While loop: Selecting all values from old table based on PK values per table, pass result of that query to next while loop
You can do this in a single query, instead of calling multiple insert statements.
For instance, instead of running these 3 queries,
INSERT INTO table VALUES(1, 2);
INSERT INTO table VALUES(3, 4);
INSERT INTO table VALUES(5, 6);
...
You could run this query:
INSERT INTO table VALUES(1, 2), (3, 4), (5, 6), ...;
Looks like you could do even combine the INSERT and SELECT:
INSERT INTO table (...)
SELECT ... FROM ...;
Furthermore, your nested loops look like this might work:
INSERT INTO table (...)
SELECT ... FROM ...
JOIN ...;
That would get it down to one call to ->query() and eliminate most of your code.

how can I avoid to many OR statements in a mysql query?

I am writing a code, where I have to produce a query with many OR statements, and I think there is a more comfortable way to this than:
foreach ($plz as &$value) {
if (empty($i)) {
$query = "WHERE plz='$value'";
} else {
$query = "$query OR plz='$value'";
}
$i++;
}
$sql = mysql_query("SELECT * FROM table $query");
while ($data = mysql_fetch_array($sql)) {
//do something
}
If you have multiple values a column may take, just connect them using the IN operator:
Instead of writing
... WHERE col=1 OR col=2 OR col=3
just write
... WHERE col IN (1,2,3)
To collect all entries in PHP, use an array and implode() later on:
// collecting values
$vals = array();
$vals[] = 1;
$vals[] = 2;
// ...
// add them to your query
$query .= ' WHERE col IN ( ' . implode( ',', $vals ) . ')';
// execute the query ...
In case your values are not integer, but need to be enclosed in apostrophes within the query, insert them that way into the array in the first place:
$vals[] = "'my string value'";
You're looking for ;
SELECT * FROM table WHERE plz in ('value1', 'value2', 'value3')
Be aware of SQL injections...
If the column plz is INT type, and all $plz are also integers, then:
$query = 'WHERE plz IN( ' . implode(',', $plz) . ')';
would work. Otherwise, trying this might work(not tested):
$query = 'WHERE plz IN( \'' . implode("','", $plz) . '\')';

Trying to extract 5 characters from a column when adding record mysql via php

My part_no column has the following format: 000-00000-00 for all records.
I need to extract the five middle characters from part_no and place it in the core column when I create the record.
I can't get my script to work.
I'm not getting any errors. Just not working.
$order = "INSERT INTO cartons_added (add_time, type, part_no, add_type, add_qty, add_ref, add_by, add_notes)
VALUES
('$date',
'$_POST[type]',
'$_POST[part_no]',
'$_POST[add_type]',
'$_POST[add_qty]',
'$_POST[add_ref]',
'$_POST[add_by]',
'$_POST[add_notes]')";
$result = mysql_query($order);
$query2 = "select part_no from cartons_current";
$sel = mysql_query($query2);
$res = mysql_result($sel);
while($row = mysql_fetch_row($res)) {
$core_digits = split('-',$row[0]);
$core =$core_digits[1];
$query3 = "insert into cartons_current(core) values($core)";
$sel2 = mysql_query($query3);
}
You can update your cartons_current table based on your cartons_added table with something like:
INSERT INTO cartons_current(core)
SELECT SUBSTR(part_no, 5, 5) FROM cartons_added
You will probably want to limit that with a WHERE clause or maybe deal with what happens when this value is already in cartons_current (use either INSERT IGNORE or ON DUPLICATE KEY UPDATE)
You are right, the script has no error.
I think the problem is on your SQL that made you can't insert a new row, specifically on the table structure. Maybe you defined a PRIMARY KEY without AUTO_INCREMENT, defined a INDEX or UNIQUE key that is not the core key or there have some other key that did not have default value. Remember that you can't insert a row without defining all required field.
You script is selecting all part_no and for every part_no you are inserting a new row in the same table, so maybe there is the problem.
I think what you want is update every result to add they core value, you can do that with UPDATE as this code:
function getValue($value) {
return "'" . trim(mysql_real_escape_string($value)) . "'";
}
mysql_query('INSERT INTO `cartons_added` (`add_time`, `type`, `part_no`, `add_type`, `add_qty`, `add_ref`, `add_by`, `add_notes`)
VALUES (' .
getValue($date) . ', ' .
getValue($_POST[type]) . ', ' .
getValue($_POST[part_no]) . ', ' .
getValue($_POST[add_type]) . ', ' .
getValue($_POST[add_qty]) . ', ' .
getValue($_POST[add_ref]) . ', ' .
getValue($_POST[add_by]) . ', ' .
getValue($_POST[add_notes]) .
')');
$partNoQuery = mysql_query('SELECT `part_no` FROM `cartons_current`');
while($partNoResult = mysql_fetch_assoc($partNoQuery)) {
list($prefix, $core, $suffix) = explode('-', $partNoResult['part_no']);
mysql_query('UPDATE cartons_current SET `core` = \'' . $core . '\' WHERE `part_no` = \'' . $partNoResult['part_no'] . '\'');
}
I added getValue function to escape posted data to prevent SQL injection.
Try removing this
$res = mysql_result($sel);
And change your while to reference the main query resource
while($row = mysql_fetch_row($sel)) {
I don't understand your logic with your tables though. You're inserting data into the cartons_added table but then you're selecting from cartons_current?
Also, split is deprecated as of PHP 5.3.0
You said five middle "characters", so I'd add quotes around your variable like so:
$query3 = "insert into cartons_current(core) values('$core')";
(Also, there's only about a gazillion answers on SO about SQL injection, and using pdo)
INSERT INTO cartons_current(core)
SELECT
substr(part_no,position('-' IN part_no)+1,position('-' IN substr(part_no,position('-' IN part_no)+1))-1)
FROM cartons_added;

Combine a variable with SELECT results into an INSERT statement

I have 3 tables:
Users - uID (INT AUTO_INCREMENT), name (VARCHAR)
Movies - mID (IN AUTO_INCREMENT), title (VARCHAR)
Watched - uID (INT), mID (INT)
I'm writing a php function that constructs a query which adds records of movies watched by a particular person. Here's what I've come up with so far:
function set_watched($name, $movies){
$sql = "SET #userid = (SELECT uID FROM users WHERE name = '$name' LIMIT 1); ";
$sql .= "INSERT INTO watched (uID, mID) VALUES ('";
foreach ($movies as $index => $movie){
}
}
My question:
Is there a way to combine the #userid variable with the results of a SELECT mID FROM MOVIES WHERE title = $movie OR title = $movie [generated with foreach]?
I don't want to generate separate SELECT statements for every movie title. Perhaps I don't even have to use the #userid variable at all?
Try something like this:
$sql = "INSERT INTO watched (uID, mID)
SELECT User.uID, Movies.mID
FROM (SELECT uID FROM Users WHERE Users.name = '$name' LIMIT 1) AS User, Movies
WHERE ";
foreach ($movies as $index => $movie){
$sql .= "Movies.title = '$movie' OR ";
}
$sql = substr($sql, 0, -4) . ";";
I prefer using arrays and imploding them for this sort of an application. Also, I wouldn't try and force these two things into one query. I would either:
Modify the function parameters to accept uID as its input, instead of name
Change the logic to two queries.
Besides, PHP's mysql_query function doesn't support multiple queries, so if you're using the standard mysql functions, you can't execute two queries with one call to mysql_query.
Running with case #2, you can use something like this (untested, of course):
$sql = 'SELECT uID FROM users WHERE name = "' . $name. '" LIMIT 1';
$result = mysql_query( $sql);
$row = mysql_fetch_row( $result);
mysql_free_result( $result);
$values_array = array();
foreach ($movies as $index => $movie)
{
$values_array[] = '( "' . $row['uID'] . '", "' . $movie . '")';
}
$sql = 'INSERT INTO watched (uID, mID) VALUES ' . implode( ', ', $values_array);
$result = mysql_query( $sql);

Categories