Load optimisation of a logic where query is running inside loop - php

I have a rest api code block which is then being used for a report. The api returns collection of invoices, but adds few fields to individual invoices.
The fields are of order item level.
Currently this code works fetching the items information
private function pushAdditionalData($invoicedetails): array
{
foreach($invoicedetails as &$invoice) {
$connection = $this->resource->getConnection();
$sql = "SELECT SUM(TRUNCATE(soi.original_price * soi.qty_invoiced,2)) AS tot_original_price,SUM(TRUNCATE(cped.value * soi.qty_invoiced,2)) AS tot_cost,SUM(TRUNCATE(soi.price_incl_tax * soi.qty_invoiced,2)) AS tot_discount_price,sal.customer_id AS customer_id,sal.increment_id AS order_id FROM sales_order_item soi INNER JOIN catalog_product_entity_decimal cped ON soi.product_id = cped.entity_id INNER JOIN sales_order sal ON soi.order_id = sal.entity_id WHERE cped.attribute_id = 81 AND soi.order_id = ".$invoice['order_id'];
$data = $connection->fetchRow($sql);
$invoice['customer_id'] = $data['customer_id'];
$invoice['order_increment_id'] = $data['order_id'];
$invoice['product_cost'] = $data['tot_cost'];
$invoice['product_discounts'] = (string)abs($data['tot_original_price'] - $data['tot_discount_price']);
}
return $invoicedetails;
}
But as this is a query running inside a loop, I am trying to find a better way of doing this.
I tried using the same query with IN statement. But the data manipulation after that is getting too heavy and complex. This code is returning a similar result
private function testPushAdditionalData($invoicedetails)
{
foreach($invoicedetails as $invoice) {
$invoiceData[] = $invoice['order_id'];
}
$invoiceStr = implode(',', $invoiceData);
$connection = $this->resource->getConnection();
$sql = "SELECT sii.parent_id AS invoice_id,TRUNCATE(soi.original_price * soi.qty_invoiced,2) AS tot_original_price,TRUNCATE(cped.value * soi.qty_invoiced,2) AS tot_cost,TRUNCATE(soi.price_incl_tax * soi.qty_invoiced,2) AS tot_discount_price,sal.customer_id AS customer_id,sal.increment_id AS order_id FROM sales_order_item soi INNER JOIN catalog_product_entity_decimal cped ON soi.product_id = cped.entity_id INNER JOIN sales_order sal ON soi.order_id = sal.entity_id INNER JOIN sales_invoice_item sii ON soi.item_id = sii.order_item_id WHERE cped.attribute_id = 81 AND soi.order_id IN (".$invoiceStr.")";
$additionalInvoiceData = $connection->fetchAll($sql);
foreach($invoiceData as $invD) {
$totOrgPrice = $totCost = $totDisc = [];
foreach($additionalInvoiceData as $addInvD) {
if($invD == $addInvD['main_id']) {
$totOrgPrice[] = $addInvD['tot_original_price'];
$totCost[] = $addInvD['tot_cost'];
$totDisc[] = $addInvD['tot_discount_price'];
$customerId = $addInvD['customer_id'];
$orderId = $addInvD['order_id'];
}
}
$data[$invD] = [
'product_cost'=>number_format(array_sum($totCost),2),
'product_discounts'=>number_format(abs(array_sum($totOrgPrice) - array_sum($totDisc)),2),
'customer_id'=>$customerId,
'order_id'=>$orderId
];
}
foreach($invoicedetails as &$invoice) {
$invoice['customer_id'] = $data[$invoice['order_id']]['customer_id'];
$invoice['order_increment_id'] = $data[$invoice['order_id']]['order_id'];
$invoice['product_cost'] = (string)$data[$invoice['order_id']]['product_cost'];
$invoice['product_discounts'] = (string)$data[$invoice['order_id']]['product_discounts'];;
}
return $invoicedetails;
}
But the execution time of this is twice of the original one.
What is the best way I can optimise this code?

Related

MySQL - Join part of query to a new query?

I've got the following code which queries a table. Then it uses the result to make another query. That result is then used to make a third query.
But how do I grab the userid field from the 2nd query in order to grab a name from a users table and join that to the result of the 3rd query?
Please note once I figure out the code I will convert this to a prepared statement. It's just easier for me to work with legacy code when figuring out queries.
$selectaudioid = "SELECT audioid FROM subscribe WHERE userid = $userid";
$audioResult=$dblink->query($selectaudioid);
if ($audioResult->num_rows>0) {
while ($row = $audioResult->fetch_assoc()) {
$newaudio = $row[audioid];
$getallaudio = "SELECT opid, userid from audioposts WHERE audioid = $newaudio" ;
$getallresult = $dblink->query($getallaudio);
if ($getallresult->num_rows>0) {
while ($row = $getallresult->fetch_assoc()) {
$opid = $row[opid];
$opuserid = $row[userid];
$getreplies =
"SELECT * from audioposts ap WHERE opid = $opid AND opid
NOT IN (SELECT opid FROM audioposts WHERE audioposts.opid = '0' )";
$getreplyresults = $dblink->query($getreplies);
if ($getreplyresults->num_rows>0) {
while ($row = $getreplyresults->fetch_assoc()) {
$dbdata[]=$row;
}
}
}
}
}
} "SELECT * from audioposts ap WHERE opid = $opid AND opid
NOT IN (SELECT opid FROM audioposts WHERE audioposts.opid = '0' )";
$getreplyresults = $dblink->query($getreplies);
if ($getreplyresults->num_rows>0) {
while ($row = $getreplyresults->fetch_assoc()) {
$dbdata[]=$row;
}
}
}
}
}
}
echo json_encode($dbdata);
The result I need are rows of json encoded instances of $getreplyresults with the $row[userid] from the original result joined to each row.
Here's what I did in the end. Now I just have to figure out how to convert this to a prepared statement in order to avoid malicious injection.
$selectaudioid = "SELECT audioid FROM subscribe WHERE userid = $userid";
$audioResult=$dblink->query($selectaudioid);
if ($audioResult->num_rows>0) {
while ($row = $audioResult->fetch_assoc()) {
$newaudio = $row[audioid];
$getallaudio = "
SELECT ap.audioid, ap.title, us.name FROM audioposts ap
INNER JOIN audioposts a2 ON a2.audioid = ap.opid
INNER JOIN users us ON us.id = a2.userid
WHERE ap.opid = $newaudio AND ap.opid <> '0'
";
$getallresult = $dblink->query($getallaudio);
if ($getallresult->num_rows>0) {
while ($row = $getallresult->fetch_assoc()) {
$dbdata[]=$row;
}}}}

php if else statement: display data if there are results from either 2 functions

I'd really appreciate some help with this code as I can't get it to work properly.
I have two separate functions that both check a table in my database for data against an ID that is fetched from the page's URL. On displaying the information, I want to use an IF ELSE statement to check if there are results from either of those functions, and if there are no results, post nothing, and if there are results, post the results.
Below are my functions:
function getArtistsBySongId($id) {
$query = "SELECT * FROM `Credit_To_Artist` AS c2a
INNER JOIN `Credits` AS cr ON cr.credit_id = c2a.credit_id
INNER JOIN `Artist` AS a ON a.artist_id = c2a.artist_id
LEFT OUTER JOIN `Song` AS s ON s.song_id = c2a.song_id
LEFT OUTER JOIN `Remix` AS r ON r.remix_id = c2a.remix_id
LEFT OUTER JOIN `Project` AS p ON p.project_id = s.project_id
WHERE c2a.song_id = $id
ORDER BY a.artist_name ASC";
$res = mysql_query($query);
$artists = Array();
$artisttoid = Array();
$songtoid = Array();
while( $row = mysql_fetch_array($res) ) {
$artist = $row[artist_name];
$credit = $row[credit_name];
$songcr = $row[song_id];
if(!array_key_exists($artist, $artists) ) {
$artists[$artist] = Array();
$artisttoid[$artist] = $row[artist_id];
$songtoid[$songcr] = $row[song_id];
}
$artists[$artist][] = $credit;
}
return array($artists, $artisttoid, $songtoid);
}
function getGroupsBySongId($id) {
$query = "SELECT * FROM `Credit_To_Artist` AS c2a
INNER JOIN `Credits` AS cr ON cr.credit_id = c2a.credit_id
INNER JOIN `Artist_Group` AS ag ON ag.group_id = c2a.group_id
LEFT OUTER JOIN `Song` AS s ON s.song_id = c2a.song_id
LEFT OUTER JOIN `Remix` AS r ON r.remix_id = c2a.remix_id
LEFT OUTER JOIN `Project` AS p ON p.project_id = s.project_id
WHERE c2a.song_id = $id
ORDER BY ag.group_name ASC";
$res = mysql_query($query);
$groups = Array();
$grouptoid = Array();
$song2id = Array();
while( $row = mysql_fetch_array($res) ) {
$group = $row[group_name];
$credits = $row[credit_name];
$songcred = $row[song_id];
if(!array_key_exists($group, $groups) ) {
$groups[$group] = Array();
$grouptoid[$group] = $row[group_id];
$song2id[$songcred] = $row[song_id];
}
$groups[$group][] = $credits;
}
return array($groups, $grouptoid, $song2id);
}
At the moment I have this code:
<?php
if ((getArtistsBySongId($id) != NULL) OR (getGroupsBySongId($id) != NULL)) {
include 'songs/getsongcredits.php';
}
?>
While the code works in displaying my data, it seems to be ignoring my IF statement, and just posting what's in the include. Would someone be able to let me know the correct way to do this? Thanks in advance.
Both of your functions are returning an array regardless of the outcome of the query. Therefore you should check if the result returned from your functions are empty or not.
<?php
if (!empty(getArtistsBySongId($id)) OR !empty(getGroupsBySongId($id))) {
include 'songs/getsongcredits.php';
}
?>
Since both of your functions return arrays I would consider checking the size of the arrays returned. If you have data then the array size would be greater than 0 otherwise it would be 0.
<?php
$artistsBySongId = count(getArtistsBySongId($id));
$groupsBySongId = count(getGroupsBySongId($id));
if (($artistsBySongId != 0) || ($groupsBySongId != 0)) {
include 'songs/getsongcredits.php';
}
?>
Thanks all for taking the time to answer my question. However, neither of the codes worked in my site. A friend of mine has helped me though and it is now working. This is the code he used:
<?php
$errors = array_filter(getArtistsBySongId( $id ));
$errors1 = array_filter(getGroupsBySongId( $id ));
if (empty($errors) AND empty($errors1)) {
} else {
include 'songs/getsongcredits.php';
}
?>

SQL Optimisation, inner ? (RBAC)

Can you tell me a better choice for doing nested select statements?
Am working on rbac project and I need to get privilege on tables.
Now this code works perfectly but if I have many data in table, the query count gets bigger.
$DB_Query_AID = $DB_Cnx->query("SELECT * FROM stackover_link WHERE link_from='123456' AND link_level='0';");
while($DB_DataAID = $DB_Query_AID->fetch()) {
if(!empty($DB_DataAID['LID'])) {
$AID = $DB_DataAID['link_to'];
$DB_Query_BID = $DB_Cnx->query("SELECT * FROM stackover_link WHERE link_from='$AID' AND link_level='1';");
while($DB_DataBID = $DB_Query_BID->fetch()) {
if(!empty($DB_DataBID['LID'])) {
$BID = $DB_DataBID['link_to'];
$DB_Query_CID = $DB_Cnx->query("SELECT * FROM stackover_link WHERE link_from='$BID' AND link_level='2';");
while($DB_DataCID = $DB_Query_CID->fetch()) {
if(!empty($DB_DataCID['LID'])) {
$CID = $DB_DataCID['link_to'];
$DB_Query_DID = $DB_Cnx->query("SELECT * FROM stackover_link WHERE link_from='$CID' AND link_level='3';");
while($DB_DataDID = $DB_Query_DID->fetch()) {
if(!empty($DB_DataDID['LID'])) {
//foooooooooooooooo........
}
}
}
}
}
}
}
}
Is possible do to same with only 1 query ?
Thanks for you help.
This looks like an INNER JOIN is possible:
SELECT sl0.*, sl1.* FROM stackover_link sl0
INNER JOIN stackover_link sl1 ON sl0.link_to = sl1.link_from and sl1.link_level = 1
...
WHERE sl0.link_from='123456' AND sl0.link_level='0';

Display value instead of ID mysqli

When I add $elan_category, I get category_id, instead category_title.
Tried to apply "left join" but no success. I have following tables in database:
function getElanDetail()
{
global $con;
if (isset($_GET['elan_id'])) {
$elan_id = $_GET['elan_id'];
$get_elan = "select * from elan where elan_id='$elan_id'";
$run_elan = mysqli_query($con, $get_elan);
while ($row_elan = mysqli_fetch_array($run_elan)) {
$elan_id = $row_elan['elan_id'];
$elan_category = $row_elan['elan_category'];
$elan_title = $row_elan['elan_title'];
$elan_description = $row_elan['elan_description'];
$elan_image = $row_elan['elan_image'];
$elan_contact = $row_elan['elan_contact'];
echo "
$elan_category //Getting ID of category instead Title :(
$elan_title
$elan_description
$elan_image
$elan_contact
";
}
}
}
With join you can do something like:
$elan_id = $_GET['elan_id'];
$get_elan = "SELECT * FROM `elan`
JOIN `categories` ON `categories`.category_id = `elan`.elan_category
WHERE `elan`.elan_id='$elan_id'";
$run_elan = mysqli_query($con, $get_elan);
while ($row_elan=mysqli_fetch_array($run_elan)){
print_r($row_elan);
// see the keys in $row_elan and use them accordingly
}
For subcategories try this query:
SELECT * FROM `elan`
JOIN `categories` ON `categories`.category_id = `elan`.elan_category
JOIN `subcategories` ON `subcategories`.subcategory_id = `elan`.elan_subcategory
WHERE `elan`.elan_id='$elan_id'
$elan_category = $row_elan['elan_category'];
add these two lines after above code
$cat = mysqli_fetch_row(mysqli_query($con,"SELECT category_title FROM categories WHERE category_id = $elan_category"));
$cat_name = $cat[0];
$cat_name is your category name enjoy

Inner Joining 3 queries

I would like to know how I could join these 3 queries together as I'm wanting only one JSON output, I thought INNER JOIN would do this. But don't know how to use this. Can someone guide me onto the right path please?
$json = array();
$following_string = mysqli_real_escape_string($mysqli,$_SESSION['id']);
$call="SELECT * FROM streamdata WHERE streamitem_id < '$lastID' AND streamitem_target=".$following_string." OR streamitem_creator=".$following_string." ORDER BY streamitem_id DESC LIMIT 10";
$chant = mysqli_query($mysqli, $call) or die(mysqli_error($mysqli));
$json['streamdata'] = array();
while ($resultArr = mysqli_fetch_assoc($chant)) {
$json['streamitem_id'] = $resultArr['streamitem_id'];
$json['streamitem_content'] = $resultArr['streamitem_content'];
$json['streamitem_timestamp'] = Agotime($resultArr['streamitem_timestamp']);
$json['streamdata'] = $json;
}
/***** COMMENTS *****/
$check = "SELECT comment_id, comment_datetime, comment_streamitem, comment_poster, comment_content FROM streamdata_comments WHERE comment_poster=".$following_string." ";
$check1 = mysqli_query($mysqli,$check);
$json['streamdata_comments'] = array();
while ($resultArr = mysqli_fetch_assoc($check1)) {
$json['comment_id'] = $resultArr['comment_id'];
$json['comment_content'] = $resultArr['comment_content'];
$json['comment_poster'] = $resultArr['comment_poster'];
$json['comment_datetime'] = Agotime($resultArr['comment_datetime']);
$json['comment_streamitem'] = $resultArr['comment_streamitem'];
$json['streamdata_comments'] = $json;
}
/***** USERS *****/
$check = "SELECT * FROM users WHERE id=".$following_string."";
$check1 = mysqli_query($mysqli,$check);
$json['users'] = array();
while ($resultArr = mysqli_fetch_assoc($check1)) {
$json['username'] = $resultArr['username'];
$json['id'] = $resultArr['id'];
$json['first'] = $resultArr['first'];
$json['middle'] = $resultArr['middle'];
$json['last'] = $resultArr['last'];
$json['users'] = $json;
}
echo json_encode($json);
}
?>
You're fetching unrelated data, so you can't use a join at the SQL level.
But JSON couldn't care less WHAT you feed it, or how. Just build the appropriate PHP-level data structure, e.g.
$data = array();
$data['streamdata'] = array();
... insert data from 'streamdata' query...
$data['streamdata_comments'] = array();
... insert comment data ...
$data['users'] = array();
... insert user data ...
which will give you a 3-way array containing the data from each of your queries. You then pass that entire $data structure to json_encode, and boom - you've got your 3 unrated queries in a single data structure, without every touching an SQL join.
Some previous answers have suggested that you can't join unrelated tables, but these are clearly not unrelated tables. The streamdata and streamdata_comments tables are quite closely related, and the users table maps user ID values in the other tables to names.
At the SQL level, these can be combined easily:
SELECT d.*, c.*, u.*
FROM streamdata AS d
JOIN streamdata_comments AS c ON d.streamitem_ID = c.comment_streamitem
JOIN users AS u ON u.user_id = c.comment_poster
WHERE c.comment_poster = '$following_string'
AND d.streamitem_id < '$lastID'
AND (d.streamitem_target = '$following_string' OR
d.streamitem_creator = '$following_string');
Whether the result makes sense for wrapping into a JSON string is a different matter, on which I can't pontificate. This would give you one record from the comments information for each comment associated with each stream item.
You are fetching unrelated data. Joining data is only usefull when the data to join has a relation.
You can't join apples, cows and monkeys.

Categories