Adding multi dimensional arrays to different tables

I need help getting this data into the database and have each records for each row in a different table, My approach was to add second id for each students called unique and then insert it into the database using seal as the primary key instead using the id. My problem is how to get each users records and associate them with their ids or the unique id i created and insert them in a different table called students_meta_table,
Here is the data in array:

Array
(
    [0] => Array
        (
            [first_name] => James
            [last_name] => Mike
            [age] => 20
            [gender] => male
            [student_id] => 1
            [records] => Array
                (
                    [0] => Array
                        (
                            [term] => First term
                            [scores] => Array
                                (
                                    [0] => Array
                                        (
                                            [maths] => 207
                                            [english] => 104
                                            [science] => 80
                                        )

                                )

                        )

                )

        )

    [1] => Array
        (
            [first_name] => Luke
            [last_name] => Garry
            [age] => 21
            [gender] => male
            [student_id] => 2
            [records] => Array
                (
                    [0] => Array
                        (
                            [term] => First term
                            [scores] => Array
                                (
                                    [0] => Array
                                        (
                                            [maths] => 200
                                            [english] => 109
                                            [science] => 96
                                        )

                                )

                        )

                )

        )

    [2] => Array
        (
            [first_name] => Zara
            [last_name] => Eben
            [age] => 22
            [gender] => female
            [student_id] => 3
            [records] => Array
                (
                    [0] => Array
                        (
                            [term] => First term
                            [scores] => Array
                                (
                                    [0] => Array
                                        (
                                            [maths] => 205
                                            [english] => 108
                                            [science] => 76
                                        )

                                )

                        )

                )

        )

    [3] => Array
        (
            [first_name] => Festus
            [last_name] => Amrem
            [age] => 19
            [gender] => male
            [student_id] => 4
            [records] => Array
                (
                    [0] => Array
                        (
                            [term] => First term
                            [scores] => Array
                                (
                                    [0] => Array
                                        (
                                            [maths] => 202
                                            [english] => 101
                                            [science] => 94
                                        )

                                )

                        )

                )

        )

    [4] => Array
        (
            [first_name] => Sean
            [last_name] => Thomas
            [age] => 23
            [gender] => male
            [student_id] => 5
            [records] => Array
                (
                    [0] => Array
                        (
                            [term] => First term
                            [scores] => Array
                                (
                                    [0] => Array
                                        (
                                            [maths] => 198
                                            [english] => 121
                                            [science] => 105
                                        )

                                )

                        )

                )

        )

)

And this is the code i wrote to add only the other details to the database but the records i dont know how to go about them

foreach($data as $dat){
			
		// Create unique code and must be inside a loop as to create unique numbers for each row
		$uniq1 = date(strtotime('NOW'));
		$uniq2 = rand(100000, 999999);
		$unique = $uniq1 . $uniq2;
		
		
		$firstname = $dat['first_name'];
		$lastname = $dat['last_name'];
		$age = $dat['age'];
		$gender = $dat['gender'];
		$studentId = $dat['student_id'];
		$records = $dat['records'];
		
		
		// REBUILD THE DATA FOR EASY INSERTION 
		
		$values[] = "('$unique', '$firstname', '$lastname', '$age', '$gender', '$studentId')";
		
		
	}
	
	
	$sql = "INSERT INTO student_table (unique, first_name, last_name, age, gender, student_id) VALUES ".implode(", ", $values);

if($conn->query($sql) === TRUE){
			echo 'Data was loaded into the database';
		}else{
			echo $sql .'<br>'. $conn->error;
		}
		
		$conn->close();

Where are you getting these arrays? What is the real problem you are trying to solve by doing this?

The data is coming from an external school api database and needed to be populated in a sub site once every week as to interact with it from the sub database with other added columns that is not found in the external site

Any help will be highly appreciated, my major problem is that am not looping it correctly and thats why i keep getting index not exits or wrong matching of data to the appropriate student

Can’t you just call their API real time and add some caching in front of it? Copying all that data over the time feels rather pointless, especially given that most caching systems (like Redis and Memcached) are out-of-the-box capable of caching complete arrays, which is a lot easier that converting your array to MySQL and then back from MySQL to an array again.

1 Like

thanks alot, i would used the api directly if am to just display it on the site but that will not solve my problems because those datas in the database where i copied it to will serve a whole lot, which caching may not solve. for a simple display then your suggestions are very perfect but for custom twisting and working with the data then i need them in my database

There’s no Year or Dates associated with the “term” or “scores”?
I.E “First Term” what year?
Would there be multiple Scores of the same type, e.g. “maths” during the same term for each student?
If there are more than ONE set of scores for each “term” what are the dates these scores represent?
Does the array ever change?
Seems there is missing data to do proper data keeping with.
Just wondering if you’ll be seeing things like this and what you expect to do with this information.

    [4] => Array
        (
            [first_name] => Sean
            [last_name] => Thomas
            [age] => 23
            [gender] => male
            [student_id] => 5
            [records] => Array
                (
                    [0] => Array
                        (
                            [term] => First term
                            [scores] => Array
                                (
                                    [0] => Array
                                        (
                                            [maths] => 198
                                            [english] => 121
                                            [science] => 105
                                        )

                                    [1] => Array
                                        (
                                            [maths] => 300
                                            [english] => 242
                                            [science] => 115
                                        )

                                )

                        )

                )

        )

)

thanks alot, but the record currently showing is for first term which is an academic session for three months, and in a term they did maths, English and science and of which each of the 5 students scored various marks for each subject

nop, each term has only three subject which you can either score once for each

the array is static only number of students per term changes and their various scores for each three subjects, meaning that next term the number of students may exceed 5 that will take same three subjects and get different scores according to what they wrote

So why do you need a $unique column?

He doesn’t… Just track with student id and term.

So if we are not worried about years or anything you just need to check if data has been entered into the tables for student id and term. I would query both tables and build a little array that has the student id as the primary KEY and terms as array values. example:

//Get student ids and terms already in system			 
	$student_ids = array();
	$sql_student_ids = "SELECT 
	  st.student_id 
	, mt.term
	FROM `student_table` AS st 
		LEFT JOIN `students_meta_table` AS mt
			ON mt.student_id = st.student_id";	
	$result_student_ids = $conn->query($sql_student_ids); 
	while ($row = mysqli_fetch_array($result_student_ids)) { 
		$student_ids[$row['student_id']][] = $row['term'];
	}

By doing this you can then check while looping through the data if the student id is a KEY in the $student_ids array allowing you to either ADD a record to the student_table or UPDATE the student_table record for this student, e.g. AGE or NAME CHANGE etc. A simple IF/ELSE statement can determine this.

if(!array_key_exists($dat['student_id'],$student_ids)){			
	$query_AddStudent->execute();
}else{		
	$query_UpdateStudent->execute();
}

Then it’s just a matter of digging our way into this array to get to the level of subject and score with some foreach loops and then checking if the Term has been records for this student by again referencing the $student_ids array but this time looking at values in the array for this student.
I can see 2 conditions that need to be checked.

  1. There are no records for this student
  2. There are records but the TERM is not yet entered, e.g. Second Term

The first is the same as before, the second checks that the array KEY does exist but it doesn’t find the $term in the $student_ids array under this students KEY. This 2 part condition looks like this.

if(!array_key_exists($dat['student_id'],$student_ids) || (array_key_exists($dat['student_id'],$student_ids) && !in_array($term,$student_ids[$dat['student_id']]))):	
	$query_AddSubjectScore->execute(); 
endif;

Putting it all together looks like this.

if(!empty($data)): 

	//Get student ids and terms already in system			 
	$student_ids = array();
	$sql_student_ids = "SELECT 
	  st.student_id 
	, mt.term
	FROM `student_table` AS st 
		LEFT JOIN `students_meta_table` AS mt
			ON mt.student_id = st.student_id";	
	$result_student_ids = $conn->query($sql_student_ids); 
	while ($row = mysqli_fetch_array($result_student_ids)) { 
		$student_ids[$row['student_id']][] = $row['term'];
	}
	
	$sql_AddStudent = "INSERT INTO `student_table`(
	  `student_id`
	, `first_name`
	, `last_name`
	, `age`
	, `gender`) 
	VALUES (?,?,?,?,?)";
	$query_AddStudent = $conn->prepare($sql_AddStudent);		
	$query_AddStudent->bind_param("sssss", $studentId, $firstname, $lastname, $age, $gender);
		
	$sql_UpdateStudent = "UPDATE `student_table` SET
	  `first_name` = ?
	, `last_name` = ?
	, `age` = ?
	, `gender` = ?
	WHERE `student_id` = ?";		
	$query_UpdateStudent = $conn->prepare($sql_UpdateStudent);		
	$query_UpdateStudent->bind_param("sssss", $firstname, $lastname, $age, $gender, $studentId); 
									 
	foreach($data as $dat):	
		
		$firstname = $dat['first_name'];
		$lastname = $dat['last_name'];
		$age = $dat['age'];
		$gender = $dat['gender'];
		$studentId = $dat['student_id']; 
			
		if(!empty($dat['student_id'])): 
		
			if(!array_key_exists($dat['student_id'],$student_ids)){			
				$query_AddStudent->execute();
			}else{		
				$query_UpdateStudent->execute();
			}
			
			if(!empty($dat['records'])): 
				
				$sql_AddSubjectScore = "INSERT INTO `students_meta_table`(
				  `student_id`
				, `term`
				, `subject`
				, `score`) 
				VALUES (?,?,?,?)";
				$query_AddSubjectScore = $conn->prepare($sql_AddSubjectScore);		
				$query_AddSubjectScore->bind_param("ssss", $studentId, $term, $subject, $score);
			
			
				foreach($dat['records'] as $k => $v):
					$term = $dat['records'][$k]['term'];														
					foreach($dat['records'][$k]['scores'] as $kk => $subjects):
						foreach($subjects as $subject => $score):
							if(!array_key_exists($dat['student_id'],$student_ids) || (array_key_exists($dat['student_id'],$student_ids) && !in_array($term,$student_ids[$dat['student_id']]))):	
								$query_AddSubjectScore->execute(); 
							endif;
						endforeach; 
					endforeach; 
				endforeach;
			endif;	
		
		endif;	
	endforeach;

endif;

i need a unique column bcs the students id is not a unique spcific one for the students, id just a way they numbered how many rows or students is in the data, and if i don’t add a unique id for first call, the second call will overight the ids when student_id 1 may have different surname and firstname, so i used datetime plus rand to distinguish each students store in the data base.

Thanks but i can’t run a select statement in an Api call, thats why i need it stored in my database once there i know exactly what to do with it

This is NOT a query of Api but “your database” to see what student ids have been added.

That’s not going to work as this “unique id” applies to the database you are adding records to. An upload today and one the next will not be able to tell if this person is already in the database. You are just throwing an array of data that can’t tell one John Doe from the next.
Why is student id just an increment number and not a true value in Api array?

There are many new projects that get populated with some type of file,be it an inventory file or list of employees or something but in most cases this is a one time thing and from that point on records are tracked with the new system.

I am sure you can create a student id (though I would use an auto increment DB field create this number) for each student added to the system. But you can’t rely on just a name for a student to be unique and identified by during input.

We have not had a good basic database question for awhile so here is one possible solution. Going to limit the solution to just inserting data into a clean database. I don’t understand if there are plans to update the data with subsequent imports but that is okay, just getting the data in is a good first step.

Start with the database schema just to get on the same page:

USE schools;

DROP TABLE IF EXISTS students;

CREATE TABLE students (
    id         INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(20),
    last_name  VARCHAR(20),
    age        INT,
    gender     CHAR(8)
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;

DROP TABLE IF EXISTS terms;

CREATE TABLE terms (
    id         INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    student_id INT NOT NULL,
    name       VARCHAR(20)
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;

DROP TABLE IF EXISTS scores;

CREATE TABLE scores (
    id      INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    term_id INT NOT NULL,
    name    VARCHAR(20),
    score   INT
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;

So three tables for students, terms and scores. Each child table is inked to it’s parent table via an id. Again this is all just to get going.

I am going to add one twist here and use the Doctrine DBAL library. As you will soon see, the library makes it trivial to insert and update records using simple arrays. Under the hood, sql prepare statements are used to it’s all safe from injection attacks. There is simply no reason to spend time and effort making sql INSERT strings when libraries such as DBAL are readily available and widely used.

I also added the Symfony var-dumper library which gives you a function called dump. Basically the same as var_dump but with much prettier output.

# create project
mkdir schools
cd schools
composer require doctrine/dbal
composer require symfony/var-dumper

And now for the actual working code:

# import.php

use Doctrine\DBAL\DriverManager;

require_once 'vendor/autoload.php';

$db = DriverManager::getConnection([
    'url' => 'mysql://user:password@localhost/schools'
]);

// This is just the array of data from the original post
$students = require 'students.php'; // dump($students);

foreach($students as $student) {
    $db->insert('students',[
        'first_name' => $student['first_name'],
        'last_name'  => $student['last_name'],
        'age'        => $student['age'],
        'gender'     => $student['gender'],
    ]);
    $studentId = $db->lastInsertId();

    foreach($student['records'] as $term) {
        $db->insert('terms',[
           'student_id' => $studentId,
           'name' => $term['term'],
        ]);
        $termId = $db->lastInsertId();

        foreach($term['scores'] as $name => $score)
        {
            $db->insert('scores',[
                'term_id' => $termId,
                'name' => $name,
                'score' => $score,
            ]);
        }
    }
}

All very straight forward. So get this working and then modify to suit.

1 Like

thanks, but i that aspect is fix already, from our new database we query by unique id which serves as the serial number per each record inserted into the database.
instead of searching where id is 4 we do where unique_id = 35286328813

thanks a million times, thats just the idea