SQL Optimisation, inner ? (RBAC) - php

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';

Related

Load optimisation of a logic where query is running inside loop

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?

How to create a PDO query for fetch with variables

I am trying to build a PDO query for a large project to fetch SQL fetch queries. These SQL queries use variables to get values. I can't use the ones I get in the tutorials - I don't understand how to modify them to be able to use variables instead of :id for example. I have many many many files to update to PDO, because currently they are written using mysql()
An example of the query (one in of hundreds of files):
if(!$GLOBALS['this_brand']->id) return false;
if(!$GLOBALS['url_lang']) return false;
if(!$GLOBALS['this_region']->id) return false;
if(!$GLOBALS['this_type']->id) return false;
if(!$GLOBALS['this_user']->group) return false;
if(!$liveid = $GLOBALS['this_config']->get('config_live_id')) return false;
if(!$draftid = $GLOBALS['this_config']->get('config_draft_id')) return false;
if(!$archiveid = $GLOBALS['this_config']->get('config_archive_id')) return false;
if($GLOBALS['this_user']->is_admin()) {
$statussql = "AND (page.status = ".$liveid." OR page.status = ".$draftid." OR page.status = ".$archiveid.")";
}
else {
$statussql = "AND page.status = ".$liveid;
}
$the_statement = "SELECT * FROM page
LEFT JOIN _page_brand ON page.id = _page_brand.page_id
LEFT JOIN _page_language ON page.id = _page_language.page_id
LEFT JOIN _page_region ON page.id = _page_region.page_id
LEFT JOIN _page_type ON page.id = _page_type.page_id
LEFT JOIN _page_group ON page.id = _page_group.page_id
LEFT JOIN _page_section ON page.id = _page_section.page_id
WHERE _page_brand.brand_id = ".$GLOBALS['this_brand']->id."
AND _page_language.language_iso = '".$GLOBALS['url_lang']."'
AND _page_region.region_id = ".$GLOBALS['this_region']->id."
AND _page_type.type_id = ".$GLOBALS['this_type']->id."
AND _page_group.group_id = ".$GLOBALS['this_user']->group."
AND _page_section.section_id = ".$GLOBALS['this_section']->id."
".$statussql."
AND page.url = '".$this->url."'
ORDER BY page.status ASC, page.moddate DESC
LIMIT 1";
My PDO attempt (it looks really lame but I did try different ways, but removed the code once they didn't work):
public function fetchRaw($query){
$stmt = $this->db->query($query);
$arr = [];
foreach ($stmt as $row) {
$rarr = [];
foreach($row as $k=>$v) {
$rarr[$k] = $v;
}
$arr[] = $rarr;
}
return $arr;
}
I tried with the fetch(), the while loop etc. I cannot go and change each current query - there are thousands of them. Any help on how this should be approached will be appreciated. I am not an expert on PDO yet, and this huge project is a bit overwhelming, but I need to do this.

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';
}
?>

Php - mysql - Return number of rows

I am trying to get the number of friends that a user has, but I can't seem to get it to work, so i'm really hoping that you can help me.
Here is the php
$findFriendssql = "select u.firstName, u.id from friends f, user u
where (f.u_ID1 = '$loggedId' and u.id = f.u_id2)
or (f.u_id2 = '$loggedId' and u.id = f.u_id1)";
$all_friends_sql = $db->getRows($findFriendssql);
//$number_of_friends = mysql_num_rows($all_friends_sql);
if ($all_friends_sql) {
$number_of_friends = mysql_num_rows($all_friends_sql);
$friends = "";
foreach ($all_friends_sql as $one_post) {
//doing stuff here.
and here is the get rows function
public function getRows($sql) {
$result = array();
$table = mysql_query($sql, $this->db_link);
if (!$table) {
die("\"$sql\" seems to be an invalid query: " . mysql_error());
}
while ($row = mysql_fetch_assoc($table)) {
array_push($result, $row);
}
return $result;
}
whatever I try - I get the error that the num rows expects parameter 1 to be resource.
thank you in advance
$findFriendssql = "select u.firstName, u.id from friends f, user u where (f.u_ID1 = '$loggedId' and u.id = f.u_id2)
or (f.u_id2 = '$loggedId' and u.id = f.u_id1)";
$all_friends_sql = $db->getRows($findFriendssql);
//$number_of_friends = mysql_num_rows($all_friends_sql);
if (count($all_friends_sql)>0) {
$number_of_friends = mysqli_num_rows($all_friends_sql);
$friends = "";
foreach ($all_friends_sql as $one_post) {
The num row is supposed to happen immediatelly after this:
$table = mysql_query($sql, $this->db_link);
$totalFirends = mysql_num_rows($table);
Use $table as input parameter. $table is supposed to be a resource. to be able to use mysql_num_rows.
The following in your function changes $table not to be a resource anymore. Besides you do not need this code to know how many friend the user has:
while ($row = mysql_fetch_assoc($table)) {
array_push($result, $row);
}
return $result;
}
Also if you just need the number then the query should change to SELECT COUNT(some-field)
And I do not know what is the content of $loggedId hope you tested that the query returns what is supposed to return. is correct the output of the query with a sample data, let say in phpmyadmin?

Alphabetically ordered list from mysqli join query

I am trying to print out an alphabetically ordered list of usernames after using a mysqli join query to select userid's from a friends table.
Friends table consists of
id - userid_one - userid_two - friendship_date - friendship_type
Users table has a pretty standard layout, and the only field I really need from it is the username (to display) and the userid to link the two tables
what i want to achieve is the below
A
Andy
Anna
B
Bobby
Brian
The code i am using is below.
$ir['userid'] //the current users id
$getfriends = mysqli_query($con,"SELECT * FROM friends INNER JOIN users ON (friends.userid_one = users.userid OR friends.userid_two = users.userid) WHERE (userid_one = '{$ir['userid']}' OR userid_two = '{$ir['userid']}') ORDER BY username") or die("query error");
$last="";
if(mysqli_num_rows($getfriends) == 0)
{
print"<li>You have no friends....sorry</li>";
}
else
{
while($gotf=mysqli_fetch_array($getfriends))
{
if($gotf['userid_one'] == $ir['userid']){$friend = $gotf['userid_two'];}else{$friend = $gotf['userid_one'];}
$printed_array = explode(",",$printed);
if(!in_array($friend,$printed_array))
{
$username_of_user = get_username_no_link($friend);
$current_letter = $username_of_user[0];
if ($last != $current_letter) {
print"<li class='Label'>{$current_letter}</li>";
$username_of_user = get_username_nd_avatar($friend);
print"<li>".$username_of_user."</li>";
$last = $current_letter;
$printed = $printed.",".$friend;
}
elseif($last == $current_letter){
$username_of_user = get_username_nd_avatar($friend);
print"<li>".$username_of_user."</li>";
$last = $current_letter;
$printed = $printed.",".$friend;
}
}
}
}
I have not got much experience of using JOIN in mysqli queries and my issue is that I am not getting the list sorted alphabetically as the below example shows.
A
Andy
T
Thomas
S
Sandra
Sally
UPDATE--
the order in which the usernames display, now seems to change randomly after reloading the page, which is further confusing the matter!
question now answered after some playing around. Ended up putting the usernames into an array, then sorting the array alphabetically by value, as well as adding a $printed_array array to add each friend to after they were printed to prevent them being printed out twice.
$usersnames = array();
while($gotf=mysqli_fetch_array($getfriends))
{
if($printed != ""){$comma = ",";}
if($gotf['userid_one'] == $ir['userid']){$friend = $gotf['userid_two'];}else{$friend = $gotf['userid_one'];}
$printed_array = explode(",",$printed);
if(!in_array($friend,$printed_array))
{
$username_of_user = get_username_no_link($friend);
$usersnames[] = $username_of_user;
$printed = $printed.$comma.$friend;
}
}
sort($usersnames);
foreach($usersnames as $name)
{
$getuserid = mysqli_query($con,"SELECT userid,avatar FROM users WHERE username = '$name' LIMIT 1");
$gotuserid = mysqli_fetch_array($getuserid);
//var_dump($gotuserid);
$displaypic = "<img style='height:30px;vertical-align:middle;margin:5px;' src='".$gotuserid['avatar']."'>";
$current_letter = $name[0];
if ($last != $current_letter)
{
print"<li class='Label'>{$current_letter}</li>";
print"<li><a href='profile/".$name."'>".$displaypic.$name."</a></li>";
$last = $current_letter;
}
elseif($last == $current_letter)
{
print"<li><a href='profile/".$name."'>".$displaypic.$name."</a></li>";
$last = $current_letter;
}
}

Categories