Storing arrays/lists in one mysql entry - php

I am working on a project that requires a MySQL database.
The project is a quiz/study site and it needs to store a users login data and then a list of the quizes they have saved, and their score for each question on each quiz they have saved. So say we have user 1. I want to go to a table called "user_content" and then be able to see which quizes they have saved and their scores for those quizes.
The quizes will be made in a unique database, and referenced via a foreign key.
The issue is, I want to have a fixed number of columns in the "user_content" section, so I need just one column that has all the foreign keys for the quizes that a user has saved. I don't want a system where you have column "quiz-1" and "quiz-2" is there a way to just have one column that stores a list or array of all the quiz foreign keys? The same goes for storing the user's scores for their favorite quizes, I want a single column that will store an array/list of all their scores. Is this possible in MySQL and what is the best way to achieve this?
(This is using php, not javascript)

So as in the comments this could be accomplished in a well defined RDBMS structure for example: table for the quiz ID and then line item entries for each quiz foreign key back to the quiz ID table.
To answer your question you can store an array as json into a MySQL database by passing your PHP array into json_encode() then inserting that into the MySQL field. In the long run this could impact performance and flexibility when writing queries. You would have to run json_decode() back in PHP to restore the structure of the array. But this would solve the requirements of the question.

Related

how to use the primary key of a table in database as a foreign key in table 2 while inserting the records from a single form simultaneously in php

I am fairly new to web development and currently working on a website using an MVC framework that can capture maintenance work conducted. I have managed to make the forms and it correctly displays any errors in filling the form and if there aren't any errors successfully inserts it into the database. What I would like to achieve is having the main table with the general details of the maintenance such as (date, time, technician, department, location, recommendations) and another table for which records what tasks were done during the maintenance such as sweeping, mopping, wiping the windows, cutting grass, etc. I have a single form that requires all the details required in both the tables to be filled. both tables will have primary keys that will be auto-increment. I would then like to simultaneously insert the data into the relevant tables only while inserting data into the tasks table I would like to have a foreign key to the main table for that particular record so it corresponds accordingly. How can I achieve this without manual input by the user if the primary key of the main table is an auto increment?
This isn't a big problem. It can't be done as a single query, but using transactions you can achieve an all-or-nothing result.
In pseudocode:
Validate data
Start a transaction
Insert data into main record
Get the last inserted ID
Insert one or more records into the child table, using the ID retrieved above
Commit the transaction (or roll back if some error occurred)
The exact mechanics vary between MySQLi and PDO, but the principle is the same.

How to force insert a duplicate key in mysql

It's a daily struggle to work with the previous programmer his code... And now, apparently, also his database.
Problem description
So here we've got a table to store the availability of a user and normally you would assign a unique id to every row of data. Except... he didn't. He made the user_id the first primary key (probably a composite).
So the user changes his availability for each weekday (monday to friday) and every timeslot in that week.
This is made into one row each:
user_id,day,hour_nr,hour_type,location_id
But you might see this one coming, I can't manually insert fake data for developing purposes. I'm trying to add a period and college year (it's for an educational institution) Which worked fine but because the old data didn't require this it's all set to 0.
The new row will consist of:
user_id,day,hour_nr,hour_type,location_id,period_id,collegeyear_id
I've tried uploading data to the table containing the period and college year information but I get an instant error telling me that there is a duplicate entry.
That's correct there is but there already were duplicates as well.
Question
And so the question is: how do I force this without altering the tables keys? I don't feel much for altering the indexed properties of the composite primary key.
Lastly, I know this is wrong and I know that it should have been done differently. Again it's not my work or design and I don't have any time on hand to fix or alter it during this project.
Edit
As requested, hereby a snapshot of the table with data and a snapshot of how it should be
The snapshot shows different headers than mentioned, they're the same but in Dutch.
Current data snapshot (I forgot to put the last 2 columns that are in the Desired data result snapshot on the snapshot but they're already there containing nothing but 0's)
Desired data result
I do need anINSERT, the data has to be added not altered. Or another fix for this issue ofcourse but the data has to be added.
Fix
So in a perfect example of tunnel vision I fixed and therefore answered my own question.
Instead of looking blindly at inserting the data I should have looked more towards the composite key part. I've added the 2 new columns to the key and now all is fine and dandy.
I said that I didn't want to mess with the keys but that was pointed towards the already existing keys not adding to the composite key.
I still dislike the fact that there isn't a single unique id but it is workable.
Q.
And so the question is: how do I force this without altering the tables keys? I don't feel much for altering the indexed properties of the composite primary key.
A.
You cannot force the primary key to have multiple values of the same ID.
The best thing for you to do would be to add an extra column with a new ID and reference that within the software.
A primary key is a special relational database table column (or combination of columns) designated to uniquely identify all table records.
A primary key’s main features are:
It must contain a unique value for each row of data.
It cannot contain null values.
A primary key is either an existing table column or a column that is specifically generated by the database according to a defined sequence.
Resources:- techopedia
So in a perfect example of tunnel vision I fixed and therefore answered my own question.
Instead of looking blindly at inserting the data I should have looked more towards the composite key part. I've added the 2 new columns to the key and now all is fine and dandy.
I said that I didn't want to mess with the keys but that was pointed towards the already existing keys not adding to the composite key.
I still dislike the fact that there isn't a single unique id but it is workable.

Better approach for updating multiple data

I have this MySQL table, where row contact_id is unique for each user_id.
history:
- hist_id: int(11) auto_increment primary key
- user_id: int(11)
- contact_id: int(11)
- name: varchar(50)
- phone: varchar(30)
From time to time, server will receive a new list of contacts for a specific user_id and need to update this table, inserting, deleting or updating data that is different from previous information.
For example, currenty data is:
So, server receive this data:
And the new data is:
As you can see, first row (John) was updated, second row (Mary) was deleted and some other row (Jeniffer) was included.
Today what I am doing is deleting all rows with a specific user_id, and inserting the new data. But the autoincrement field (hist_id) is getting bigger and bigger...
Obs: Table have about 80 thousand records, and this update will occur 30 times a day or more.
I have some (related) questions:
1. In this scenario, do you think deleting all records from a specific user_id and inserting updated data is a good approach?
2. What about removing the autoincrement field? I don't need it, but I think it is not a good idea to have a table without a primary key.
3. Or maybe the better approach is to loop new data, selecting each user_id / contact_id for comparing values to update?
PS. For better approach I mean the most efficient way
Thank you so much for any help!
In this scenario, do you think deleting all records from a specific user_id and inserting updated data is a good approach?
Short Answer
No. You should be taking advantage of 'upsert' which is short for 'insert on duplicate key update'. What this means is that if they key pair you're inserting already exists, update the specified columns with the specified data. You then shorten your logic and reduce increments. Here's an example, using your table structure that should work. This is also assuming that you have set the user_id and contact_id fields to unique.
INSERT INTO history (user_id, contact_id, name, phone)
VALUES
(1, 23, 'James Jr.', '(619)-543-6222')
ON DUPLICATE KEY UPDATE
name=VALUES(name),
phone=VALUES(phone);
This query should retain the contact_id but overwrite the prexisting data with the new data.
What about removing the autoincrement field? I don't need it, but I think it is not a good idea to have a table without a primary key.
Primary keys do not imply auto incremented values. I could have a varchar field as the primary key containing names of fruits and vegetables. Is this optimized for performance? Probably not. There many situations that might call for auto increment and there are definite reasons to avoid it. It all depends on how you wish to access the data and how this can impact future expansion. In your situation, I would start over on the table structure and re-think how you wish to store and access the data. Do you want to write more logic to control the data OR do you want the data to flow naturally by itself? You've made a history table that is functioning more like a hybrid many-to-one crosswalk at first glance. Without looking at the remaining table structure, I can't necessarily say on a whim that it's not a good idea. What I can say is that I would do this a bit differently. I will answer this more specifically in the next question.
Or maybe the better approach is to loop new data, selecting each user_id / contact_id for comparing values to update?
I would avoid looping through the data in order to update it. That is a job for SQL and it does this job well. Sometimes, we might find ourselves in a situation where we must do this to either extract data in a specific format or to repair data in some way however, avoid doing this for inserting or updating the data. It can negatively impact performance and you will likely paint yourself into a corner.
Back to what I said toward the end of your second question which will help you see what I am talking about. I am going to assume that user_id is a primary key that is auto-incremented in your user table. I will do some guestimation here and show you an example of how you can redesign your user, contact and phone number structure. The following is a quick model I threw together that shows the foreign key relationship between the tables.
Note: The column names and overall data arrangement could be done differently but I did this quickly to give you a decent example of a normalized database structure. All of the foreign keys have a structural layout which separates your data in a way that enables you to control the flow of data as it enters and leaves your system. Here's the screenshot of the database model I threw together using MySQL Workbench.
(source: xonos.net)
Here's the SQL so that you can look at it more closely.
You'll notice that the "person" table is extracted from users but shares data with contacts. This enables you to store all "people" in one place, all "users" in another and all "contacts" in another. Now, why would we do this? The number one reason can be explained in two scenarios.
1.) Say we have someone, in this example I'll call him "Jim Bean". "Jim Bean" works for the company, so he is a user of the system. But, "Jim Bean" happens to own a side business and does contact work for the company at the same time. So, he is both a contact and a user of the system. In a more "flat table" environment, we would have two records for Jim Bean that contain the same data which could become outdated or incorrect, quickly.
2.) Let's say that Jim did some bad things and the company wants nothing to do with him anymore. They don't want any record of him - as if he never existed. All that we have to do is delete Jim Bean from the Person table. That's it. Since the foreign relationship has "CASCADE" on update/delete - this automatically propagate and clears out the other tables related to him.
I highly recommend that you do some reading on normalized data structure. It has saved me many hours once I got the hang of it and I will never go back.

What is the correct way to store, increment and reuse an index

I am in the process of building my first bespoke PHP/MySQL app. It is a kind of survey application for vehicle testing.
The survey itself is built via queries to the database where I have stored the questions and using a loop they get outputted to the page each with a unique ID and name.
All is working so far. I can successfully submit results and access the $_POST array and can see answers stored against the correct keys.
I am at the stage now where I wish to save the results.
My intention is to use a table to record the results using one row per answer. The columns I plan on using are survey_id, question_id (the key from the array), question_answer.
My question is how do I correctly store and increment the survey ID itself? If I was recording the survey in such a way that a single row would contain all of the results for a single survey I could auto increment the survey_id field but when I was designing the app I did not think this would be the most robust way to store results. Each survey will be in excess of 100 rows each and any changes to the layout of the survey down the line would be tricky to implement (I suspect).
My initial thought was to create a stand alone table to record and increment survey ID's and then to query this to populate the survey_id in the results table.
I would love to hear the best practise to achieving this.
The best way to do this is to have three tables:
Surveys
Questions
Answers
Surveys would probably have a survey ID, user that created the survey, so on. Questions would have a question ID, the survey ID that the question is for, and what the question is asking. Lastly, Answers would have the answers to the questions, most likely having an answer ID, question ID, user who answered it and their answer.
In essence, your original idea of
create a stand alone table to record and increment survey ID's and then to query this to populate the survey_id in the results table.
is the right one in this circumstance.

Advice: MySQL Database, using concatened data as single row or create several rows

I'm making a table (with MySQL) to store some data, but i'm not sure of the way to do it properly, because of the amount of data. For example if it's adress book database.
so there is a table for users and a table for contacts. Each users can own hundreds of contacts, and there could be thousans of users. Should I add a new row for every single contact (it will make a lot of rows!), or can i just concatenate all of them in one row with the user id.
uuh, this is just an example, but in my case once contacts are INSERTED they will never be UPDATED so, no modifications, they can only be DELETED.
To go by the normal forms, you should have three tables
1) Users -> {User_id} (primary key)
2) Contacts -> {Contact_id} (primary key)
3) Users_Contacts -> {User_id, Contact_id} (Compound key)
The Junction table Users_Contacts will have one record per contact - meaning for each unique value of User_id+Contact_id, there will be one record.
However, In practice, it is not always necessary to stick to the rule book. Depending on the use case, it is often advisable to have a denormalized table. The call is yours.
There is also another option of using NoSQL with MySQL. For example, the contacts can be serialized into JSON and stored. Mysql 5.7 seem to support this data format (with some external help). See this for details.
Say for eg: If you add 3 contacts for a single user and as you mentioned you would be deleting contacts the its better to insert all three contacts, each in a new row with its user id. Because if you want to delete any one of the contact from 3 of them, then it will be easy.
If you concatenate all the contacts for an user and add them in one row could land up many issues. What in future the requirement changes and you need to make a layout all the contacts for an user with edit/delete individual contacts. So you should have one contact in each row.
You can optimize your query by indexing the columns.
Say userid#1234 has 1000 contacts in contact table where the primary key in contact table is idcontact (Indexed by default) and then in contact table another field called "iduser" which is also indexed, then the select performance over an iduser on contact table will be fast.
Ideally its the best approach using mysql database. There are examples of many apps where it maintains millions of data so it should be fine with a contact table and for each contact a new row.
I wouldn't worry about lots of rows. You have to keep in-mind the granularity of control the user would expect (deleting / adding a contact, rearranging the list based on different factors, etc). It's always better to break things out into their owns rows if they are going to be treated independently from a similar item (contacts, users, addresses, etc). Additionally, if you were to concatenate your data, re-ordering for display or removing data becomes extremely resource intensive. Where as MySQL is designed to do exactly that "on the cheap".
MySQL can easily handle millions of rows of data. If you are worried about speed, just make sure your indexes are in-place before your data collection is too big (I would venture a guess, and say you'll need to index the user ID the contact belongs to and the first/last names). Indexes are a double-edged sword, however, as they take up disk space, but allow fast querying of large data sets. So you don't want to go over-board and index everything, only what you'll be sorting/searching by.
(Why on earth will contacts never be updated?...)

Categories