I was wondering if anybody can help me out/point me in the right direction.
I have a database with a table that includes incremental id, name, unique id, and the parent uuid of fictional characters.
The table shows the following people...
John (Parent not listed)
Steve (Parent listed as John)
Mark (Parent listed as John)
Kevin (Parent listed as Steve)
Adam (Parent listed as Mark)
**ID, NAME, UUID, PARENT_UUID**
1, John, 0001, none
2, Steve, 0002, 0001
3, Mark, 0003, 0001
4, Kevin, 0004, 0002
5, Adam, 0005, 0003
So in this example, John has 2 sons, Steve and Mark... each of whome have a son, Kevin and Adam.
What I want to do, is (on an already made profile page) show the number of family members.
So going to Johns page, I would see "John (4 Descendents)", and if I went to Marks page, I'd see "Mark (1 Descendents)"
Allowing me to list how many family members, in lower generations are found in the table.
I am able to print a list of all family members... using:
$sql = "SELECT * FROM users";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo $row["uuid"]. "<br>";
}
}
But now I want to find out for each $row['uuid'] how many descendents each entry has.
I tried doing something like the following:
$sql = "SELECT * FROM users";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$sql = "SELECT * FROM users WHERE parent_uuid = '".$row['uuid']."'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$numOfDescendents = $result->num_rows;
}
}
}
But then I realised that not only does this not work (it grabs the number of the first row and stops), that using this logic, if I have a family with 20+ generations, I'm going to need to have 20+ while loops nested within each other, checking each generation.
So I'm wondering, is this even possible?
There's probably an easy way, but after 3/4 days of headaches and frustration I'm finally asking for help =)
My end goal is to have a php file that checks through the table for all users, and totals their descendents by counting how many people in the table use their uuid as a parent_uuid... as in the example at the start... John would have 4, Mark would have 1, Steve would have 1, Kevin would have 0, and Adam would have 0.
Thanks in advance for any guidance =)
You can use a recursive function to solve this. Note - I am passing the entire input to the recursive array which I do not recommend. If you are using a class set the input as a property of the class or you can use global which is also not recommended.
$input = [
['id' => 1, 'name' => 'John', 'UUID' => 0001, 'PARENT_UUID' => null],
['id' => 2, 'name' => 'Steve', 'UUID' => 0002, 'PARENT_UUID' => 0001],
['id' => 3, 'name' => 'Mark', 'UUID' => 0003, 'PARENT_UUID' => 0001],
['id' => 4, 'name' => 'Kevin', 'UUID' => 0004, 'PARENT_UUID' => 0002],
['id' => 5, 'name' => 'Adam', 'UUID' => 0005, 'PARENT_UUID' => 0003],
];
function getDescendents($uuid, $input, $count = 0, $descendants = [])
{
foreach ($input as $user) {
if ($user['PARENT_UUID'] === $uuid) {
$count++;
$descendants[] = $user['UUID'];
}
}
if (!empty($descendants)) {
$new_uuid = array_shift($descendants);
return getDescendents($new_uuid, $input, $count, $descendants);
}
return $count;
}
$result = [];
foreach ($input as $user) {
$result[$user['name']] = getDescendents($user['UUID'], $input);
}
echo(json_encode($result));
Output -
{"John":4,"Steve":1,"Mark":1,"Kevin":0,"Adam":0}
The key in the above object is the user's name and the value is the number of descendants.
Related
So I have a
student model,
subject model,
marks model, which has the total score field
what I am trying to do is get the total score in an exam for each student in a class and then retrieve the results according to the highest score in the class and then position those scores as 1,2,3...
then finally get the position a single student and store it in an exam record table.
so far I was able to achieve getting the total score in an exam for each student, stored them in an array, sorted them according to the highest score,
the only issue now giving me a headache is getting the position of those scores from the array, my question is how can I get the position of a score for a student, or is there a way to add the positions to the scores and then retrieve the position for a student
For example
1 student_id => 2, total_score => 500
2 student_id => 3, total_score => 455
3 student_id => 5, total_score => 345
here is my code below, Please anyone with an idea how to solve this I need your help.
TextInput::make('position')->numeric(
function (Closure $set) {
// Get all students from class
$studentsInClass = $this->class->students;
//empty array to store student's total_score
$totalScore = [];
// loop through students get all their total_score on all subjects from mark table and sum it, then store it
in totalScore array.
foreach ($studentsInClass as $key => $student) {
$totalScore[] = array('student_id' => $student->id, 'total_score' => $student->marks->sum('total_score') );
}
// Sort scores from highest to lowest
$sortedScores= array_values(array_reverse(Arr::sort($totalScore, function ($value) {
return $value['total_score'];
})));
// get the current student Id
$id = $this->student->id;
// find a student in the array that matches the current student id and return his score.
//so this is where I would like to return the score and position of the student
$filteredArray = Arr::where($sortedScores, function ($value, $key) use ($id) {
return $value['student_id'] == $id;
});
}
)->disabled(),
if you dd($sortedScores)
You have a two-dimensional array.
$sortedScores = [
['student_id' => 2, 'total_score' => 443],
['student_id' => 4, 'total_score' => 410],
['student_id' => 1, 'total_score' => 371],
['student_id' => 3, 'total_score' => 170],
];
The index of each row is already consecutive from 0-3. I think you want to add a rank to the rows.
foreach($sortedScores as $idx => $row)
$sortedScores[$idx]['rank'] = $idx+1;
//test output
var_export($sortedScores);
Output:
array (
0 =>
array (
'student_id' => 2,
'total_score' => 443,
'rank' => 1,
),
1 =>
array (
'student_id' => 4,
'total_score' => 410,
'rank' => 2,
),
2 =>
array (
'student_id' => 1,
'total_score' => 371,
'rank' => 3,
),
3 =>
array (
'student_id' => 3,
'total_score' => 170,
'rank' => 4,
),
)
If you want rank to start at 0, just assign $idx.
Try on https://3v4l.org/9Dv7D
I hope this helps and that I understood your problem correctly.
To find the key for a specific value in a multidimensional array see 'PHP Multidimensional Array Searching (Find key by specific value)'
You can use the array_search() function. This will either return the key of the value you are looking for or false if it is not in the array.
$letters = ['a', 'b', 'c', 'd'];
$key = array_search('b', $letters);
//In this case $key will equal to 2.
Presuming that $filteredArray contains a single item, then the first key should be the student's position in the sorted array.
$position = array_keys($filteredArray)[0];
I am trying to merge two results of two queries in MYSQL using PHP, but I am puzzled how to do it! I am using PDO. I am programming for a hobby and am trying to make a to do list app just like a Trello board. However, I just can't figure out how to merge two results from different tables in a database.
The idea is as follows:
I have a table called 'task_lists' with the content:
'list_id => 1, list_name = 'listOne'
'list_id => 2, list_name = 'listTwo'
And a table called 'tasks':
task_id => 1, list_id => 1, task_name => 'taskOfListOne', duration => 5, is_done => 1
task_id => 2, list_id => 1, task_name => 'anotherTaskOfListOne', duration => 5, is_done => 1
task_id => 3, list_id => 2, task_name => 'taskOfListTwo', duration => 10, is_done => 0
And I am trying to create an array that is merged between the two results as something like:
(I know this is a rough picture of how the array is supposed to look like)
$result = [array]
[list_id] = 1, [list_name] = 'listOne' =>
[array][list_id] = 1, ['task_name] = taskOfListOne,[duration] = 5, ['is_done'] => 1
[array][list_id] = 1, ['task_name] = anotherTaskOfListOne,[duration] = 5, ['is_done'] => 1
[list_id] = 2, [list_name] = 'listTwo' =>
[array][list_id] = 2, ['task_name] = taskOfListTwo,[duration] = 5, ['is_done'] => 1
Is this even possible? I have tried a Union sql query and methods like nested foreach statements, but none of them worked for me. Am I missing something here?
PS: Sorry for my bad english.
Have you tried a left join?
SELECT TL.`list_id`, TL.`list_name`, T.`task_name`, T.`duration`
FROM task_lists AS TL
LEFT JOIN tasks as T ON TL.`list_id` = T.`list_id`
And then in PHP you build the array in the format you want.
Later edit:
Simple PHP example to parse SQL data as you asked (to remove duplicated info):
<?php
// $mysql_rows -> here is your query result, fetched as associative array
$filtered_array = array();
foreach ($mysql_rows as $row){
// Initiate record if is not already initiated
if (!isset($filtered_array[ $row['list_id'] ])){
$filtered_array[ $row['list_id'] ] = array(
'list_id' => $row['list_id'],
'list_name' => $row['list_name'],
'tasks' => array()
);
}
// Add tasks
$filtered_array[ $row['list_id'] ]['tasks'][] = array(
'task_name' => $row['task_name'],
'duration' => $row['duration'],
'is_done ' => $row['is_done ']
);
}
// Optional: if you want to remove list_id from $filtered_array key names, uncomment the next line
// $filtered_array = array_values($filtered_array);
?>
I am trying to LIMIT a Query which is already been selected. I know I can directly do it in my query select, but due to some logics of the code, if I do that way I have to run the query selection twice which (I believe) will increase the computational time!
So, what I am trying is this:
$query1 = $mysqli->query("SELECT * FROM tableName WHERE someconditions");
then, I need to re-select a sub-selection of the $query1 for my Pagination. What I am trying to do is something like this;
$query2 = LIMIT($query1, $limit_bigin, $limit_end);
where $limit_bigin, $limit_end provide the LIMITING range (start and end respectively).
Could someone please let me know how I could do this?
P.S. I know I can do it directly by:
$query1 = $mysqli->query("SELECT * FROM tableName WHERE someconditions");
$query2 = $mysqli->query("SELECT * FROM tableName WHERE someConditions LIMIT $limit_bigin, $limit_end");
But this is running the query twice and slows down the process (I must run the first query without limits due to some logics of the program)
EDIT 1
As per the answers I tried using array_slice in PHP. BUT, since Query is an object it doesn't give the results that was expected. A NULL is resulted form
array_slice($query1, $start, $length, FALSE)
If you have already carried out the query and the result set has been returned to your PHP, you can not then LIMIT it. As you state, then running a second SQL execution of a subpart of the same query is wasteful and repetative.
Don't
Repeat
Yourself.
DRY.
As I said above, repetition causes issues with maintainability as you can easily forget about repetition, tweaking one SQL and forgetting to tweak the other.
Don't
Repeat
Yourself.
DRY.
Use PHP instead
Once you have executed the query, the result set is then passed back to PHP.
So assuming you have a $var with the contents of you SQL query, then you simply need to select the valid rows from the $var, not from the database [again].
You can do this using PHP numerous Array functions. Particularly array_slice().
So;
$query1 = $mysqli->query("SELECT * FROM tableName WHERE someconditions");
Now, to select the second page, say for example rows 10 to 20:
$query2 = array_slice($query1, (10-1), 10 );
This wil then "slice" the part of the array you want. Remember that the array counts will start at zero so to grab row 10 (of an index starting at 1, Typical of a MySQL Auto Increment Primary Key), then you will need to select X number of rows from row (10-1) .
Please also read the manual for PHP array_slice().
Further
As referenced in comments, there is no guarentee that your SQL will return the same values each time in the same order, so it is highly recommended to order your results:
$query1 = $mysqli->query("SELECT * FROM tableName
WHERE someconditions ORDER BY primary_key_column");
Example Data:
$query1 = $mysqli->query("SELECT * FROM tableName WHERE someconditions ORDER BY id");
/***
$query1 = array {
0 => array { 'id' => 1, 'user' => "Jim", 'colour' => "Blue" },
1 => array { 'id' => 2, 'user' => "Bob", 'colour' => "Green" },
2 => array { 'id' => 3, 'user' => "Tom", 'colour' => "Orange" },
3 => array { 'id' => 4, 'user' => "Tim", 'colour' => "Yellow" },
4 => array { 'id' => 5, 'user' => "Lee", 'colour' => "Red" },
5 => array { 'id' => 6, 'user' => "Amy", 'colour' => "Black" }
}
***/
$page = 2;
$size = 3; // number per page.
$start = ($page - 1) * $size; //page number x number per page.
// Select the second page of 3 results.
$query2 = array_slice($query1, $start, $size , FALSE);
/***
$query2 = array {
0 => array { 'id' => 4, 'user' => "Tim", 'colour' => "Yellow" },
1 => array { 'id' => 5, 'user' => "Lee", 'colour' => "Red" },
2 => array { 'id' => 6, 'user' => "Amy", 'colour' => "Black" }
}
***/
You can then use these in a foreach or other standard array manipulation technique.
I have searched and searched but can't find what I'm after, it's probably super simple.
I have a table that has a priority field to display in order that was chosen. What I want to be able to do I have seen but don't know how to make my code do it.
Take data with priority 1,2,3,4,5,6 and change #5 to 4 and adjust all the other numbers accordingly.
So now it would be 1,2,3,4 (old 5),5 (old 4),6.
I'm using PHP, Mysql and for some reason I'm not getting this concept. I can increase/decrease the number but I can't adjust the others around it. So I end up with 1,2,3,4,4,6
Table structure
ID, Cat, Title, Priority
I only want to increase/decrease in the cat. I'm looking at sorting multiple at a time so it could be
1 (old 5), 2 (old 3), 3 (old 6)... etc
Thanks for the help.
EDIT
Maybe my first explaination wasn't explained well enough.
Let's say I have 10 pictures, each with a unique ID and the priority they are given right now is the same as the ID. I can display the images ORDER BY priority
ID 1,2,3,4,5,6,7,8,,9,10
Priority 1,2,3,4,5,6,7,8,9,10
But after looking at the pictures I want to change the priority they are shown.
SO... I click an arrow that has a link that tells the database to update picture ID 4 with priority 4 to be ID 4 priority 2 because it's more important.
Now my database looks like this.
ID 1,2,3,4,5,6,7,8,9,10
Priority 1,2,2,3,5,6,7,8,9,10 because the rest of the priority numbers didn't get changed because I don't know how to make that work.
What I want to do is, after I change one priority to the new one, rearrange the numbers so they are in numerical order after that number because they are less important. How do I do this?
I hope this following code could give you som help how to solve your problem/challenge.:
<?php
//Say that you've recieved array from db...
$arr = array();
$arr[0] = array('id'=>5, 'sortOrder' => 1, 'picture' => 'picture1');
$arr[1] = array('id'=>6, 'sortOrder' => 2, 'picture' => 'picture2');
$arr[2] = array('id'=>7, 'sortOrder' => 3, 'picture' => 'picture3');
$arr[3] = array('id'=>9, 'sortOrder' => 4, 'picture' => 'picture4');
$arr[4] = array('id'=>10, 'sortOrder' => 5, 'picture' => 'picture5');
//Do some sorting...
$executeSQL = down($arr,2); //Tell sortOrder to decrease for picture3 and to increase for picture2
//Array will change to:
//$arr[1] = array('id'>=6, 'sortOrder' => 3, 'picture' => 'picture2');
//$arr[2] = array('id'>=7, 'sortOrder' => 2, 'picture' => 'picture3');
//$executeSQL returns an array of two sql-statements that you should execute
$executeSQL = up($arr, 1); //Decrease sortorder for picture2 and increase for picture3
//Array will change to:
//$arr[1] = array('id'=>6, 'sortOrder' => 2, 'picture' => 'picture2');
//$arr[2] = array('id'=>7, 'sortOrder' => 3, 'picture' => 'picture3');
//$executeSQL returns an array of two sql-statements that you should execute
echo print_r($arr,true); //Will output the original array
function down(Array &$arr, $index) {
$origPrev = $arr[$index-1]['sortOrder'];
$arr[$index-1]['sortOrder'] = $arr[$index]['sortOrder'];
$arr[$index]['sortOrder'] = $origPrev;
$sql = "UPDATE table SET sortOrder=" . $origPrev . " WHERE id=" . $arr[$index]['id'];
$sql2 = "UPDATE table SET sortOrder=" . $arr[$index]['sortOrder'] . " WHERE id=" . $arr[$index-1]['id'];
return array($sql, $sql2);
}
function up(Array &$arr, $index) {
$origPrev = $arr[$index+1]['sortOrder'];
$arr[$index+1]['sortOrder'] = $arr[$index]['sortOrder'];
$arr[$index]['sortOrder'] = $origPrev;
$sql = "UPDATE table SET sortOrder=" . $origPrev . " WHERE id=" . $arr[$index]['id'];
$sql2 = "UPDATE table SET sortOrder=" . $arr[$index]['sortOrder'] . " WHERE id=" . $arr[$index+1]['id'];
return array($sql, $sql2);
}
?>
I am creating a questionnaire for a client that requires the questions to be organized by 3 layers of levels. I've successfully created the U.I. however I've been trying for the last 3 hours to pull data from a database in such a way that everything loads in the right place. The database is organized like so by the client so i have no control over it:
id description parentId about
1 Level 1 0 This is the top level or in my case tab1
2 Level 2 0 This is the next tab in the top level
3 Level 1a 1 This is the first category under tab1
4 Level 1b 1 This is the next category under tab1
5 Level 1a1 3 This is the content under the first category of tab1
So anything with a parentId of 0 is the top level and will contain anything of the second level with the parentId of 1 and so on. Confusing yes, I can barely make sense of this but this is how I've been told to do it.
What approach would be the best way to execute something like this? An example from another question I'm using as a reference is attached below (although not working)
foreach (mysql_query("SELECT * FROM pB_test ORDER BY id ASC") as $row) {
$menuitem = array_merge(array(), $row);
$menuLookup[$menuitem['id']] = $menuitem;
if ($menuitem['parent'] == null) {
$menuitem['path'] = "/" . $menuitem['name'];
$menu[] = $menuitem[];
} else {
$parent = $menuLookup[$menuitem['parent']];
$menuitem['path'] = $parent['path'] . "/" . $menuitem['name'];
$parent['menu'][] = $menuitem;
}
}
Any help would be greatly appreciated. Cheers
If you have exactly 3 levels, then you can try this:
http://sqlfiddle.com/#!2/70e96/16
(
SELECT 1 AS lvl,
top_level.description AS o1, top_level.id AS id1,
NULL AS o2, NULL AS id2,
NULL AS o3, NULL AS id3,
top_level.*
FROM node AS top_level
WHERE top_level.parentId = 0
)UNION ALL(
SELECT 2 AS lvl,
top_level.description AS o1, top_level.id AS id1,
category_level.description AS o2, category_level.id AS id2,
NULL AS o3, NULL AS id3,
category_level.*
FROM node AS top_level
INNER JOIN node AS category_level ON category_level.parentId = top_level.id
WHERE top_level.parentId = 0
)UNION ALL(
SELECT 3 AS lvl,
top_level.description AS o1, top_level.id AS id1,
category_level.description AS o2, category_level.id AS id2,
last_level.description AS o3, last_level.id AS id3,
last_level.*
FROM node AS top_level
INNER JOIN node AS category_level ON category_level.parentId = top_level.id
INNER JOIN node AS last_level ON last_level.parentId = category_level.id
WHERE top_level.parentId = 0
)
ORDER BY o1,o2,o3;
I added a lvl field to the selects, different value for each level. Also added o1,o2,o3 for ordering nested levels nicely, of course you may have another needs. You could process all rows in PHP, for example split them into 3 arrays (one for each level), or maybe create a lookup table by id, etc.
It might be worth doing this in PHP, as opposed to SQL if you're working with an external database. I haven't benchmarked the following, so try with your data and see if performance is problematic or not.
You can choose yourself what to do with orphaned records (which reference parentIDs that don't exist anymore).
Ordering in PHP like this requires that you have all of your data beforehand, so use something like PDO's fetchAll(PDO::FETCH_ASSOC) method, which should result in something like this:
$data_from_database = array(
array("id" => 1, "parentId" => 0, "description" => "Level 1"),
array("id" => 2, "parentId" => 1, "description" => "Level 1a"),
array("id" => 3, "parentId" => 1, "description" => "Level 1b"),
array("id" => 4, "parentId" => 0, "description" => "Level 2"),
array("id" => 5, "parentId" => 2, "description" => "Level 1a1"),
array("id" => 6, "parentId" => 5, "description" => "Level 1a11a"),
array("id" => 7, "parentId" => 5, "description" => "Level 1a11b"),
array("id" => 8, "parentId" => 9, "description" => "Level 3"),
);
First off, you'll want to have the primary key (ID) as the array's keys. The following also adds the keys "children" and "is_orphan" to every record.
$data_by_id = array();
foreach($data_from_database as $row)
$data_by_id[$row["id"]] = $row + array(
"children" => array(),
"is_orphan" => false
);
This will look something like this:
$data_from_database = array(
1 => array("id" => 1, "parentId" => 0, "description" => "Level 1",
"children" => array(), "is_orphan" => false),
...
);
Now, it gets tricky: we'll loop through the array and add references.
foreach($data_by_id as &$row)
{
if($row["parentId"] > 0)
{
if(isset($data_by_id[$row["parentId"]]))
$data_by_id[$row["parentId"]]["children"][] = &$row;
else
$row["is_orphan"] = true;
}
}
unset($row); // Clear reference (important).
The last step is to clean up the 'root' of the array. It'll contain references to duplicate rows.
foreach($data_by_id as $id => $row)
{
// If you use this option, you'll remove
// orphaned records.
#if($row["parentId"] > 0)
# unset($data_by_id[$id]);
// Use this to keep orphans:
if($row["parentId"] > 0 AND !$row["is_orphan"])
unset($data_by_id[$id]);
}
Use print_r($data_by_id) after every step to see what happens.
If this proves to be a time consuming operation, try to build up the tree by only doing SELECT id, parentId FROM ... and then later fetching the metadata such as description. You could also store the result in Memcache or serialized into a database.
i also had the same kind of problem but after lot of googling and stackoverflowing :-)
i found my answer....
Here is my way of coding.
function showComments($parent = 0)
{
$commentQuery = "SELECT * FROM comment WHERE parent = ".mysql_real_escape_string($parentId);
$commentResult = mysql_query($commentQuery)
while ($row = mysql_fetch_array($commentResult))
{
echo '[Table containing comment data]';
showComments($row['commentID']);
}
}
showComments();