Is there any MySQL function that will optimize this code? A child ID getting all parent ID
function get_parents() {
$ids = array();
while($id) :
$query = "SELECT placement_id FROM referrals WHERE user_id = $id";
$query = $this->db->query($query);
$result = $query->row();
if(!isset($result->placement_id)) :
break;
elseif(isset($result->placement_id) && $result->placement_id == 2) :
break;
endif;
$id = $result->placement_id;
array_push($ids, $id);
if($result) :
continue;
endif;
break;
endwhile;
return $ids;
}
The code above will return all parent ID of given user_id, this will stop if nothing is found. I found this code too slow and heavy load.
My Table
relations table
| id | user_id | placement_id |
| 1 | 2 | NULL |
| 2 | 3 | 2 |
| 3 | 4 | 2 |
| 4 | 5 | 3 |
| 5 | 6 | 4 |
| 6 | 7 | 3 |
| 7 | 8 | 3 |
| 8 | 9 | 3 |
| 9 | 10 | 6 |
| 10 | 11 | 5 |
| 11 | 12 | 6 |
| 12 | 13 | 4 |
| 13 | 14 | 3 |
| 14 | 15 | 9 |
| 15 | 16 | 10 |
user_id is the child and parent is placement_id
You can rewrite you code as:
function get_parents() {
$ids = array();
while($id){
$query = "SELECT placement_id FROM referrals WHERE user_id = $id";
$query = $this->db->query($query);
$result = $query->row();
if(isset($result->placement_id) && $result->placement_id !== 2)
{
$id = $result->placement_id;
array_push($ids, $id);
}
}
return $ids;
}
It excludes some additional function calls such continue,break etc. Also, Make sure you have INT as type of user_id with indexing on this column.
I would personally do something like this:
<?php
define('MAX_NEST_DEPTH', 15);
function get_parent($child_id) {
$child_id = (int) $child_id;
$parents = array();
$counter = 0;
do {
$sql = "SELECT placement_id
FROM referrals
WHERE user_id = {$child_id}";
$query = $this->db->query($query);
$result = $query->row();
$child_id = (int) $result->placement_id;
$parent[] = $child_id;
$counter++;
}
while ($child_id != 0 || $counter == MAX_NEST_DEPTH);
return $parents;
}
You wont get around the query in a loop here, mysql does not support n-level-nested SELECT, otherwise we could just do it in one go.
Related
I'm working on a venue programming system for festivals to update our current system of just using loads of spreadsheets. I'm trying to figure out a way to display a table that shows all subvenues related to a festival as table headings and then all timeslots related to that subvenue as table columns. I want it to look something like this at the end.
screenshot of current spreadsheet used:
The idea is that you will be able to click on one of the free timeslots, and open a modal to allocate a show to that slot or display shows already attached to it. Ideally each subvenue will be drag and dropped into order but these are problems for later.
So far I'm trying to use a loop to create a table with only 1 column. have an sql query return the header and then inside that return loop have another sql query that returns all of the timeslots, then close the first loop. but this is only displaying 1 table and not looping round to return the others.
Code is
<?php
//selects subvenue
$sql = "SELECT *
FROM Subvenue S
JOIN Venue V
ON S.venueId = V.venueId
JOIN festvenue FV
ON V.venueId = FV.venueId
WHERE FV.festId = $festId;";
$result = mysqli_query($conn, $sql);
if (!$result) die ("Database access failed");
$rows = $result->num_rows;
//starts loop to display subvenues
for ($j = 0 ; $j < $rows ; ++$j) {
$row = $result->fetch_array(MYSQLI_NUM);
$subvenueId = htmlspecialchars($row[0]);
$subvenueName = htmlspecialchars($row[2]);
echo <<<_END
<table>
<tr>
<th id="$subvenueId">$subvenueName<th>
</tr>
_END;
$sql = "SELECT * FROM TimeSlot
WHERE subVenId = $subvenueId
ORDER BY (start >= '05:00:00') desc, start;";
$result = mysqli_query($conn, $sql);
if (!$result) die ("Database access failed");
$rows = $result->num_rows;
for ($j = 0 ; $j < $rows ; ++$j) {
$row = $result->fetch_array(MYSQLI_NUM);
$timeId = htmlspecialchars($row[0]);
$type = htmlspecialchars($row[3]);
$start = htmlspecialchars($row[4]);
$end = htmlspecialchars($row[5]);
$length = htmlspecialchars($row[8]);
echo <<<_END
<tr id="$timeId" class="timeslot-time">
<td class="$type-$length">$start - $end</td>
</tr>
_END;
}
echo "</table>";
}
?>
The sample data I have is below
Subvenue Table
+----------+---------+------------------------+
| subVenId | venueId | subVenName |
+----------+---------+------------------------+
| 1 | 2 | Subvenue 1 |
| 2 | 2 | subvenue 2 |
+----------+---------+------------------------+
timeslot Table
+--------+--------+----------+-------+----------+----------+--------+
| timeId | festId | subVenId | type | start | end | length |
+--------+--------+----------+-------+----------+----------+--------+
| 1 | 11 | 1 | show | 12:00:00 | 13:00:00 | 60 |
| 2 | 11 | 1 | show | 13:30:00 | 14:30:00 | 60 |
| 3 | 11 | 1 | break | 13:00:00 | 13:30:00 | 30 |
| 4 | 11 | 1 | break | 14:30:00 | 15:00:00 | 30 |
| 5 | 11 | 1 | show | 15:00:00 | 16:00:00 | 60 |
| 6 | 11 | 2 | show | 16:30:00 | 17:30:00 | 60 |
| 7 | 11 | 2 | show | 18:00:00 | 19:00:00 | 60 |
| 8 | 11 | 2 | show | 19:30:00 | 20:30:00 | 60 |
| 9 | 11 | 1 | show | 21:00:00 | 22:00:00 | 60 |
| 10 | 11 | 2 | show | 22:30:00 | 23:30:00 | 60 |
+--------+--------+----------+-------+----------+----------+--------+
I'm not even sure a table it the best thing for this or would lists or something else be better?
At the end I want it to display
+-------------------+. +-------------------+
| subvenue 1 | | subvenue 2 |
+-------------------+. +-------------------+
| 12:00:00-13:00:00 | | 16:30:00-17:30:00 |
| 13:30:00-14:30:00 | | 18:00:00-19:00:00 |
| 13:00:00-13:30:00 | | 19:30:00-20:30:00 |
| 14:30:00-15:00:00 | | 22:30:00-23:30:00 |
| 15:00:00-16:00:00 |. +-------------------+
| 21:00:00-22:00:00 |
+-------------------+
etc
I've managed to figure this out. I decided to output a series of lists instead of using a table.
Apart from that my main fix was instead of using the same $sql and $stmt variables in the mySQL query I used $subSql and $subStmt for the second query.
require_once "header.php";
$festId = mysqli_real_escape_string($conn, $_GET["festival_Id"]);
?>
<div id="programming" class="tabcontent">
<h2 >Programming</h2><br>
<?php
//gets the subvenue and starts first loop
$sql = "SELECT *
FROM Subvenue S
JOIN Venue V
ON S.venueId = V.venueId
JOIN festvenue FV
ON V.venueId = FV.venueId
WHERE FV.festId = ?;";
$stmt = mysqli_stmt_init($conn);
if(!mysqli_stmt_prepare($stmt, $sql)) {
header("location: ../festival.php?error=sqlerror&festival_Id".$festId);
exit();
}
else {
mysqli_stmt_bind_param($stmt, "s", $festId);
mysqli_stmt_execute($stmt);
$result = $stmt->get_result();
$rows = $result->num_rows;
for ($j = 0 ; $j < $rows ; ++$j)
{
$row = $result->fetch_array(MYSQLI_NUM);
$subVenueId = htmlspecialchars($row[0]);
$subVenueName = htmlspecialchars($row[2]);
echo <<<_END
<div id="$subVenueId" class="programme">
<ul>
<li class="programme-heading">$subVenueName</li>
<ul>
_END;
//select all timeslots with that subvenue
$subSql = "SELECT * FROM TimeSlot
WHERE subVenId = ?
ORDER BY (start >= '05:00:00') desc, start;";
$subStmt = mysqli_stmt_init($conn);
if(!mysqli_stmt_prepare($subStmt, $subSql)) {
header("location: ../festival.php?
error=sqlerror&festival_Id".$festId);
exit();
}
else {
mysqli_stmt_bind_param($subStmt, "s", $subVenueId);
mysqli_stmt_execute($subStmt);
$subResult = $subStmt->get_result();
while ($row = mysqli_fetch_assoc($subResult)) {
$timeId = htmlspecialchars($row['timeId']);
$type = htmlspecialchars($row['type']);
$start = htmlspecialchars($row['start']);
$end = htmlspecialchars($row['end']);
$length = htmlspecialchars($row['length']);
echo <<<_END
<li id="[$timeId" class="$type-$length">$start - $end</li>
_END;][1]
}
}
echo "</ul></ul></div>";
}
}
That looks exactly how I wanted it. in the initial question
I know there are plenty of questions on recursive queries, but this isn't a "how to" question so much as, "what am I missing?".
I feel like I'm close, but this just isn't working for me. I'm trying to get all the parents in the line of a sub-category:
Data
cat_id | cat_parent | cat_name | cat_description | cat_sort | cat_active
1 | 0 | Home | Primary Forum Location | 6 | 1
2 | 0 | Software | Software Discussions | 1 | 1
17 | 1 | Programming | | 1 | 1
19 | 1 | MySql Workbench | Description | 1 | 1
20 | 2 | SQL Tables | Description | 2 | 1
21 | 2 | Another Test | | 1 | 1
22 | 21 | Sub-Sub Cat | | 1 | 1
23 | 22 | Sub-Sub-Sub Cat | | 1 | 1
Controller
function getparents($catid = 1) {
$parents = $this->forum_model->get_parent($catid);
echo "<pre>" . print_r($parents, TRUE) . "</pre>";
}
Model
function get_parent($catid = 0) {
$parent = array();
$this->db->select('*');
$this->db->from(TBL_FORUM_CATEGORIES);
$this->db->where('cat_id',$catid);
$child = $this->db->get()->row_array();
$parent[] = $child;
if ($child['cat_parent'] == 0) {
return $parent;
} else {
$push = $this->get_parent($child['cat_parent']);
array_push($parent,$push);
}
}
Result
<pre></pre>
I know it's doing the queries at least partly right, because I turned on the profiler and I can see the queries happening.
Running the controller with ID=22:
0.0007 SELECT * FROM `forum_categories` WHERE `cat_id` = '22'
0.0004 SELECT * FROM `forum_categories` WHERE `cat_id` = '21'
0.0004 SELECT * FROM `forum_categories` WHERE `cat_id` = '2'
After array push for $push variable, write:
return $parent;
That should be enough for your function to act as it's own loop.
below is my table data
+-------------+-----------+----------------+
| customer_id | parent_id | node_direction |
+-------------+-----------+----------------+
| 1 | 0 | T |
| 2 | 1 | L |
| 3 | 1 | R |
| 4 | 2 | L |
| 5 | 2 | R |
| 6 | 4 | L |
+-------------+-----------+----------------+
Which represents the following tree
1
|
---------
| |
2 3
|
-------
| |
4 5
|
-----
|
6
I need to find the position for insertion by parent id
For Example:
1) if parent id is 1 then insert position will be root-3 position-L
2) if parent_id is 2 then insert position will be root-4 position-R
3) if parent_id is 3 then insert position will be root-3 position-L
The thing is it need to follow the binary structure
I also need to have count of sub nodes by parent node for example:
1 - 5
2 - 3
3 - 0
4 - 1
5 - 0
I need to accomplish this in php and mysql.
Can anyone suggest to me the easiest way to achieve this?
function getNodeInsertPostionByParentId($parentId){
$position = array('status'=>false);
$parents = array();
foreach($parentId as $parent){
$qry = "select customer_id,node_direction from mlm_nodes where parent_id =".$parent." order by node_direction";
$rst = mysql_query($qry);
$count = mysql_num_rows($rst);
if($count==2){
while($row = mysql_fetch_assoc($rst)){
$parents[$parent][] = $row['customer_id'];
}
}elseif($count==1){
$position['status'] = true;
$position['parentId'] = $parent;
$position['node'] = 'R';
//echo '<pre>1';print_r($position);echo '</pre>';
return $position;
}else{
$position['status'] = true;
$position['parentId'] = $parent;
$position['node'] = 'L';
//echo '<pre>2';print_r($position);echo '</pre>';
return $position;
}
}
return $this->searchByParents($parents);
}
function searchByParents($parents){
foreach($parents as $parent){
return $this->getNodeInsertPostionByParentId($parent);
}
}
echo '<pre>';print_r($this->getNodeInsertPostionByParentId(array('4')));die;
This works as expected for finding node position by parent id
My Database table looks like this (rough outline):
+--------+-----------+-------------+---------+------------+------------+
| log_id | host_name | status | host_id | profile_id | event_date |
+--------+-----------+-------------+---------+------------+------------+
| 1 | site1 | Online | 2 | 1 | <*date*> |
+--------+-----------+-------------+---------+------------+------------+
| 2 | site1 | Online | 2 | 1 | <*date*> |
+--------+-----------+-------------+---------+------------+------------+
| 3 | site1 | Offline | 2 | 1 | <*date*> |
+--------+-----------+-------------+---------+------------+------------+
| 4 | site2 | Online | 4 | 1 | <*date*> |
+--------+-----------+-------------+---------+------------+------------+
| 5 | site2 | Maintenance | 4 | 1 | <*date*> |
+--------+-----------+-------------+---------+------------+------------+
Here are my codes:
VIEW:
<?php foreach($result as $row): ?>
['<?php echo $row->host_name;?>',<?php echo $this->help_model->filter_online($id,$row->host_id, 'Online');?>,
<?php echo $this->help_model->filter_online($id,$row->host_id, 'Offline');?>,
<?php echo $this->help_model->filter_online($id,$row->host_id,'Maintenance');?>],
<?php endforeach; ?>
CONTROLLER:
public function userreports()
{
$data['id']=$this->session->userdata('profile_id');
$this->session->set_userdata('from_date', '2015-01-01');
$from_date = $this->session->userdata('from_date');
$date = date('Y-m-d',now());
$this->session->set_userdata('to_date', $date);
$to_date = $this->session->userdata('to_date');
$data['result'] = $this->help_model->all_logs($this->session->userdata('profile_id'));
$this->load->view('squidtopus1-host-reports', $data);
}
MODEL:
function all_logs($id)
{
$id = $this->session->userdata('profile_id');
$this->db->select("*");
$this->db->from('server_log');
$this->db->where('profile_id',$id);
$query = $this->db->get()->result();
return $query;
}
function filter_online($data, $host_id, $status)
{
$from_date = $this->session->userdata('from_date');
$to_date = $this->session->userdata('to_date');
$id = $this->session->userdata('profile_id');
$this->db->select()->from('server_log');
$this->db->where('profile_id',$id);
$this->db->where('status',$status);
$this->db->where('host_id',$host_id);
$this->db->where('event_date >=',$from_date);
$this->db->where('event_date <=',$to_date);
$data = $this->db->get()->result();
$stat_count = 0;
foreach ($data as $row) {
$stat_count++;
}
return $stat_count;
}
The information that needs to displayed is shown properly, but the problem is that it repeatedly shows it for each row in the database that matches 'profile_id = $id'(in this case, profile_id=1).
Ideally it should show:
['site1', 2,1,0], ['site2', 1,0,1]
But instead it shows:
['site1', 2,1,0],['site1', 2,1,0],['site1', 2,1,0],['site2', 1,0,1],['site2', 1,0,1]
I can't determine where I've gone wrong... So any insight into solving this issue is greatly appreciated. Thanks ahead!
function all_logs($id)
{
$id = $this->session->userdata('profile_id');
$this->db->select("DISTINCT(host_id), host_name");
$this->db->from('server_log');
$this->db->where('profile_id',$id);
$query = $this->db->get()->result();
return $query;
}
try to change your function like above
i have table in database:
Group:
| id | Category | title |
| 1 | 1 | group1 |
| 2 | 2 | group2 |
| 3 | 1 | group3 |
| 4 | 3 | group4 |
| 5 | 2 | group5 |
| 6 | 1 | group6 |
News:
| id | Group | title | body |
| 1 | 3 | title1 | body1 |
| 2 | 2 | title2 | body2 |
| 3 | 1 | title3 | body3 |
| 4 | 4 | title4 | body4 |
| 5 | 1 | title5 | body5 |
| 6 | 5 | title6 | body6 |
| 7 | 3 | title7 | body7 |
| 8 | 2 | title8 | body8 |
| 9 | 1 | title9 | body9 |
| 10 | 6 | title10| body10 |
| 11 | 1 | title11| body11 |
| 12 | 5 | title12| body12 |
how can i show this as:
-GROUP1, GROUP3 and GROUP6
//GROUP1 (category1)
--title3
--title5
--title9
//GROUP3 (category1)
--title1
--title7
//GROUP6 (category1)
--title10
-GROUP2 and GROUP5
//GROUP2 (category2)
--title2
--title8
//GROUP5 (category2)
--title6
--titl12
-GROUP4
//GROUP4 (category3)
--title4
i will make this in foreach. thanks for help!
Your exact requested output makes this complicated.
$sql = 'SELECT n.title, n.Group AS group_id, g.Category AS cat_id
FROM News AS n
JOIN Group AS g ON g.id = group_id
ORDER BY cat_id, group_id, n.id';
$result = mysql_query($query);
$categories = array();
while ($row = mysql_fetch_assoc($result)) {
$catID = $row['cat_id'];
$groupID = $row['group_id'];
$title = $row['title'];
$categories[$catID]['groups'][$groupID]['titles'][] = $title;
}
foreach ($categories as $catID => $groups) {
$catGroups = '-GROUP'.implode(', GROUP',array_keys($groups)).PHP_EOL;
$lastComma = strrpos($catGroups,',');
if ($lastComma !== false) {
$catGroups = substr($catGroups,0,$lastComma-1).
' AND ' .substr($catGroups,$lastComma+1);
}
echo $catGroups;
foreach ($groups as $groupID => $titles) {
echo "//GROUP$groupID (category$catID)".PHP_EOL;
foreach ($groups as $group => $titles) {
echo '--'.$title.PHP_EOL;
}
}
}
If you didn't need such fancy output, this would be much simpler.
$sql = 'SELECT n.title, n.Group AS group_id, g.Category AS cat_id
FROM News AS n
JOIN Group AS g ON g.id = group_id
ORDER BY cat_id, group_id, n.id';
$result = mysql_query($query);
$lastCatID = null;
$lastGroupID = null;
while ($row = mysql_fetch_assoc($result)) {
$catID = $row['cat_id'];
$groupID = $row['group_id'];
$title = $row['title'];
if ($catID !== $lastCatID){
echo "*** CATEGORY $catID\n";
$lastCatID = $catID;
}
if ($groupID !== $lastGroupID){
echo "GROUP $groupID\n";
$lastGroupID = $groupID;
}
echo "-- $title\n";
}
You told, you have your values in the database. So you have to get them first, e.g. with the following database query:
SELECT
g.`title` AS `group_title`
, n.`title` AS `news_title`
FROM
`Group` AS g
INNER JOIN
`News` AS n
ON
g.`id` = n.`Group`
ORDER BY
g.`Category`
, n.`Group`
, n.`title`
Store the data in an array. Now you can use a foreach loop to iterate over the array.
===
Here my update:
First fill the array while reading from the database (example query see above).
<?php
$data = array();
$res = mysql_query('SELECT ...');
while (($row = mysql_fetch_assoc($res)) !== false) {
$data[$row['group_title']][] = $row['news_title'];
}
?>
Then write the array to the screen:
<?php
foreach ($data as $group_title => $groups) {
echo $group_title . "\n";
foreach ($groups as $news) {
echo "\t" . $news . "\n";
}
}
?>