MySQL for Test Website - php

I am making a website similar to Moodle. It basically holds tests for students. The confusion comes in when allowing multiple types of questions on the tests and how to create the tables to store the data.
The test_info table will contain the test ID and description.
The test table will contain Test ID and Question ID pairs.
To get the questions for a test this will require a "SELECT * FROM test WHERE testID=$tID" which will take a substantial amount of time to execute when the database gets large.
Originally the questions table contained the question, 4 possibilities and the answer.
How should I store the data for multiple questions and multiple types of questions in a database?
For Example:
Test 51 - "Intro Mathmatics"
Question 1: What is 2+2?
A: 4 B: 12
C: 7 D: 8
Question 2: (0,103) is the range for the equation y=2x/(100x)?
True False
This results in data with 2 different sizes
Questions:
| Question | A | B | C | D | Correct |
| Question | Correct |
If I store these in 1 table it will waste 4 50Char Var-Chars per question and if I store these in 2 separate tables, how will the SELECT know which table to select from?
Questions:
| TestID | TableID | QuestionID |
Questions1:
| Ques | A | B | C | D | Correct |
Questions2:
| Ques | A | B | C | D | Correct |
What a nightmare.

Put the answer in their own table:
answers (answer_id, question_id, description, sequence)
and put the correct answer_id in the question table.

An empty VARCHAR(50) only needs six bits, which means that it will (most likely) take up one byte only on the disk. The VAR in VARCHAR means that the space usage will adapt to the actual data. This means that using one table will "waste" only four bytes per question.
Since you have two types of questions that you handle in two different ways in the code, your two-table approach is viable as well.
The recommended approach is:
Note that four-answer questions and yes-no questions are both special cases of a multiple-choice question. Store these in the database. One possible schema:
question: question_id (PK), test_id (FK), text
answer: answer_id (PK), question_id (FK), text, is_correct
PK = primary key, FK = foreign key

I think you data structure will be like this:
Table1 :Test
id:
TestName:
Table2: Questions:
id:
test_id:
Question:
Answer: //say a,b,c,d
Table3:Answers
id:
question_id:
Anwser Option: //say a,b,c,d
Answer:
Sql For tables with data:
The data will help you in understanding the structure.
CREATE TABLE IF NOT EXISTS `Tests` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Test` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
--
-- Dumping data for table `test2`
--
INSERT INTO `test2` (`id`, `Test`) VALUES
(1, 'Test1');
//////////////////////////////////////
CREATE TABLE IF NOT EXISTS `questions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`test_id` int(11) NOT NULL,
`Question` varchar(255) NOT NULL,
`Answer` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `questions`
--
INSERT INTO `questions` (`id`, `test_id`, `Question`, `Answer`) VALUES
(1, 1, 'question1', 'a'),
(2, 2, 'Question2', 'd');
////////////////////////////////////////////////////
CREATE TABLE IF NOT EXISTS `answers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`question_id` int(11) NOT NULL,
`answer_option` varchar(10) NOT NULL,
`Answer` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
--
-- Dumping data for table `answers`
--
INSERT INTO `answers` (`id`, `question_id`, `answer_option`, `Answer`) VALUES
(1, 1, 'a', 'True'),
(2, 1, 'b', 'False'),
(3, 2, 'a', 'Asnwer1'),
(4, 2, 'b', 'Answer2'),
(5, 2, 'c', 'Answer3'),
(6, 2, 'd', 'Answer4'),
(7, 2, 'e', 'Answer5');

Related

MySQL: "Patch" existing data on a table from values in another table

I have two tables, identical in structure. First one is populated with records obtained from another system that potentially needs corrections (could be one or many columns). Second table is corrections that I want to apply to the first table.
For example, I could have the following rows on table1:
order_number | name | email | tracking_no
101 null foo#bar.com 3456789
102 An Example ex#ample.com 1010101
...and the corrections I want to apply to these rows on table2:
order_number | name | email | tracking_no
101 Name Surname null null
102 null null 45778901
Essentially: Add missing name to order_number 101 and correct the wrong tracking_no for order_number 102.
The logic of what I am trying to do is: "Patch" values in table1 with the corrections contained for the same order_number in table2, giving precedence over to values in table2 and not overwriting existing values in table1 if the corresponding value in table2 is a null.
For the case where a value is null in table1 and we have a non-null correction in table2, COALESCE seems to be the right way to go but I can't figure out how to overwrite an already existing value with the corresponding "fix" from table2.
Is there a mechanism in MySQL/MariaDB that would allow me to do this as the alternative is a very messy "pull two records from two tables, compare values and build up the new correct record and insert it back into table1".
As pointed out in comments, here is a reproducible set of test data along with table structures:
USE so_demo;
DROP TABLE IF EXISTS so_demo.table1;
DROP TABLE IF EXISTS so_demo.table2;
CREATE TABLE so_demo.table1 (
order_number int(11) NOT NULL AUTO_INCREMENT,
name varchar(50) DEFAULT NULL,
email varchar(250) DEFAULT NULL,
tracking_no varchar(255) DEFAULT NULL,
PRIMARY KEY (order_number),
UNIQUE INDEX UK_table1_order_number (order_number)
)
ENGINE = INNODB,
CHARACTER SET utf8mb4,
COLLATE utf8mb4_general_ci;
CREATE TABLE so_demo.table2 (
order_number int(11) NOT NULL AUTO_INCREMENT,
name varchar(50) DEFAULT NULL,
email varchar(250) DEFAULT NULL,
tracking_no varchar(255) DEFAULT NULL,
PRIMARY KEY (order_number),
UNIQUE INDEX UK_table2_order_number (order_number)
)
ENGINE = INNODB,
CHARACTER SET utf8mb4,
COLLATE utf8mb4_general_ci;
INSERT INTO so_demo.table1 VALUES (101, NULL, "foo#bar.com", 3456789);
INSERT INTO so_demo.table1 VALUES (102, "An Example", "ex#ample.com", 1010101);
INSERT INTO so_demo.table2 VALUES (101, "Name Surname", NULL, NULL);
INSERT INTO so_demo.table2 VALUES (102, NULL, NULL, 45778901);
The term you are looking for is merge and in mysql an insert on duplicate key
drop table if exists t,t1;
create table t(order_number int primary key, name varchar(20) , email varchar(20), tracking_no int);
insert into t values
(101 , null , 'foo#bar.com' , 3456789),
(102 , 'An Example', 'ex#ample.com' , 1010101);
create table t1(order_number int, name varchar(20), email varchar(20), tracking_no int);
insert into t1 values
(101 , 'Name Surname', null , null),
(102 , null , null , '45778901');
insert into t
select * from t1
on duplicate key update
t.name = case when t1.name is not null then t1.name else t.name end,
t.email = case when t1.email is not null then t1.email else t.email end,
t.tracking_no = case when t1.tracking_no is not null then t1.tracking_no else t.tracking_no end
;
select * from t;
+--------------+--------------+--------------+-------------+
| order_number | name | email | tracking_no |
+--------------+--------------+--------------+-------------+
| 101 | Name Surname | foo#bar.com | 3456789 |
| 102 | An Example | ex#ample.com | 45778901 |
+--------------+--------------+--------------+-------------+
2 rows in set (0.001 sec)

MySQL operator IN () only this values

quite a few hours struggling with a problem, it's more about the issue.
Depicting the script when you type in textboxa searches in SQL data records where name = contain. Assuming that I entered 2 names separated by a comma (X, Y) 2:
a) If you choose "all containing typed values" is to look for all the records, in the above "name". It works:
$where. = "AND name IN ('". implode (' ', ' ', $array). "')";
Model: Contains X.
Contains The Y;
Contains the Y and X;
It works as it should.
(b)) If you choose "all containing only the values to be entered to look for records in which only occurs in the" name "is what we have.
Here I do not know how to do it.
Model: contains the X and Y-only, I don't want records that contain only X, but Y and Alternatively, if in MySQL "name" is 3 options (X, Y, Z).
I do not know how to explain more clearly:--------------------example: I have 5 values in the table, where the name is in different combinations:
1) Name1, Name2,
2) name1, Name8,
3) name1, Name9,
4) Name8, Name3,
5) Name4, Name5,
using the selected the first option, where the textbox typed: "Name1, Name" the result will be: 1), 2), 3), 4), this is valid.
Using the second result should only be 2).
The result of a) res1 or res2 or res1 and res2.
Now I need somehow to come to a solution, when I type in textbox "res1, res2" the result will be: only the records that contain res1 and res2, and NOTHING AFTER that.
---- EDIT FOR REPLIES:
+--------------------- my_val_search -----------+
| aid | int(11) | primary key auto_increment|
|name | varchar(255)| |
+-----------------------------------------------+
Records:
1|Ruby
2|CSS
3|HTML
4|PHP
5|Python
6|SQL
7|Javascript
8|C++
9|AJAX
10|Java
+---------------------- topics -----------------+
|id | int(11) | Primary key auto_increment |
|dateline| date | |
|author | varchar(30) | |
|message | text | |
|aid |int(11) |foreign key with my_val_search|
+-----------------------------------------------+
Now if you type in the search "Ruby, Python" I want to have found records only for topics where are "Ruby and Python".
By using the
$myValInput = $_GET['keywords'];
$where = "AND a.name IN('" . implode("','", $myValInput. "')";
Searches for records, where when you type "Python, Ruby" is divided into:
-Python
-Ruby,
- Python and Ruby
and I want to make the results appear only for "Python and Ruby". In addition, if someone types "Python, Ruby, Java," the result should be
-Python, Ruby, Java,
the record should be just what I wrote, nothing more.
Using IN cannot get what you expect!
When used with a subquery, the word IN is an alias for = ANY. Thus, these two statements are the same:
SELECT s1 FROM t1 WHERE s1 = ANY (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 IN (SELECT s1 FROM t2);
https://dev.mysql.com/doc/refman/5.7/en/any-in-some-subqueries.html
Besides, I think your schema is incorrect. The topic should be parent and the topic keywords should be child. If I were you, I will do it like this.
CREATE TABLE IF NOT EXISTS `topic` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateline` date,
`author` varchar(30),
`message` text,
PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `topic_keywords` (
`topic` int(10) unsigned NOT NULL,
`keyword` varchar(255),
FOREIGN KEY (`topic`) REFERENCES `topic` (`id`),
UNIQUE INDEX (`topic`, `keyword`)
) ENGINE=InnoDB;
In your case, I think the full text is suitable.
https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
CREATE TABLE IF NOT EXISTS `topic` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateline` date,
`author` varchar(30),
`message` text,
`keywords` varchar(255),
PRIMARY KEY(`id`),
FULLTEXT (`keywords`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
INSERT INTO `topic` (`keywords`)
VALUES ('Ruby,Python'),
('Ruby'),
('Ruby,SQL'),
('Python'),
('Python,SQL'),
('Ruby,Python,SQL,AJAX');
SELECT `keywords`
FROM `topic`
WHERE MATCH(`keywords`) AGAINST('+Ruby +Python' IN BOOLEAN MODE);
+----------------------+
| keywords |
+----------------------+
| Ruby,Python |
| Ruby,Python,SQL,AJAX |
+----------------------+
2 rows in set (0.00 sec)
If you just find out the records just exact your input. You can add a hash column. Like design a hash table for your Mysql table. So you can use hash value in your query statement. But don't forget to update the hash value, if your keywords value had been changed. Add the before update trigger might be a good choice.
CREATE TABLE IF NOT EXISTS `topic` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateline` date,
`author` varchar(30),
`message` text,
`keywords` varchar(255),
`keywords_hash` varchar(32),
PRIMARY KEY(`id`),
FULLTEXT (`keywords`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
INSERT INTO `topic`
(`keywords` , `keywords_hash`)
VALUES ('Ruby,Python', md5('Ruby,Python'));
SELECT `keywords`
FROM `topic`
WHERE `keywords_hash` = md5('Ruby,Python');
Here's an attempt at a solution, but there's better solutions query wise than using a regular denormalized structure to handle this - so if you're going to query this a lot (and with a large number of terms), you might want to look into caching the lookup values in your original table and then querying by their content. Anyway, to solve it with regular joins:
Given data:
CREATE TABLE foo (id int);
CREATE TABLE foo_bar (foo_id int, bar_id int);
CREATE TABLE bar (id int, name varchar(25));
INSERT INTO foo VALUES (1), (2), (3), (4);
INSERT INTO bar VALUES (1, 'Ruby'), (2, 'Python'), (3, 'PHP');
INSERT INTO foo_bar VALUES (1, 1), (2, 1), (3, 3), (4, 3), (2,2);
You can retrieve id values from foo that has both Ruby and Python by joining each term manually:
SELECT f.id FROM foo f
JOIN foo_bar fb ON fb.foo_id = f.id
JOIN bar b ON fb.bar_id = b.id AND b.name = 'Ruby'
JOIN foo_bar fb2 ON fb2.foo_id = f.id
JOIN bar b2 ON fb2.bar_id = b2.id AND b2.name = 'Python'
This gives 2 as the only id that haves all languages present.

How to update a row with multiple images which has only single image inserted initially in php

I have a table with columns id,image and organisation name.I have inserted single image for each organisation initially but i need to update it with multiple images for each organisation.I am using php ver 5.4.16 and mysql ver 5.6.12.
Try to use a separator, for example use comma and store it like this in your table :
id image organization
1 path_to/img1.png org_name1
2 path_to/img1.png,path_to/img2.png org_name2
and later, after you extract the record, use explode function to extract it to an array like this :
$images = explode(",", $data->image);
PS : please give enough length for the image field, for example, give it varchar(4000), this is to make sure there will be no string truncation
Split the table in two and use the id of your table as foreign key in the new image table. ("normalization" and "relations" should be your search tags) https://en.wikipedia.org/wiki/Database_normalization
Or if you can't you should use json to insert multiple content. http://php.net/manual/en/book.json.php
You need to add a new table named "image" with columns :
- id_img
- image
- ref_organisation_id (foreign_key)
The best solution, in my opinion, to your problem would be to slightly redesign your database schema - the existing table will not be able to store multiple images for the same company judging by the overview of the tables you gave in the question.
There ought to be a table for orgainisations and another table for images associated with those organisations. The images table would have a key that links to the organisations table.
A very quickly put together example database structure
+----+------------------+
| id | name |
+----+------------------+
| 1 | ACME Web Company |
| 2 | ACME ISP |
+----+------------------+
+----+--------+------------+
| id | org_id | image |
+----+--------+------------+
| 1 | 1 | logo.jpg |
| 2 | 1 | banner.jpg |
| 3 | 1 | badge.png |
| 4 | 2 | logo.jpg |
| 5 | 2 | banner.gif |
+----+--------+------------+
create table if not exists `organisations` (
`id` int(10) unsigned not null auto_increment,
`name` varchar(50) not null,
primary key (`id`)
) engine=innodb auto_increment=3 default charset=utf8;
insert into `organisations` (`id`, `name`) values
(1, 'acme web company'),
(2, 'acme isp');
create table if not exists `org_images` (
`id` int(10) unsigned not null auto_increment,
`org_id` int(10) unsigned not null,
`image` varchar(50) not null,
primary key (`id`),
key `org_id` (`org_id`),
constraint `fk_org` foreign key (`org_id`) references `organisations` (`id`) on delete cascade on update cascade
) engine=innodb auto_increment=6 default charset=utf8;
insert into `org_images` (`id`, `org_id`, `image`) values
(1, 1, 'logo.jpg'),
(2, 1, 'banner.jpg'),
(3, 1, 'badge.png'),
(4, 2, 'logo.jpg'),
(5, 2, 'banner.gif');

SQL Upvote Downvote system

I am currently working on a forum where posts need to have an upvote/downvote system.
My current sql(phpmyadmin) structure is like this:
Table 1 (posts)
| post_id | post_title | post_score |
Table 2 (pvotes)
| pvote_id | fk_post_id | fk_user_id | pvote_score |
I want to somehow make post_score (in table 1), find all pvote_score (table 2) columns and add/subtract them together, where fk_post_id (table 2) is = to post_id (table 1)
This way I hope to make a voting system that only allows every user to vote once, and automatically calculate a posts post_score from the pvote_score values.
EXAMPLE:
user_1 upvotes post_1 inserting following into table 2:
| (pvote_id) 1 | (fk_post_id) 1 | (fk_user_id) 1 | (pvote_score) 1 |
I now want post_score (table 1) to find all entries in table 2 where:
fk_post_id is same as post_id, and thereafter add or subtract the values from pvote_score and make the sum the NEW value of post_score.
I am trying to make it work just like stackoverflows own upvote/downvote system.
EDIT 1:
Question:
I want to know how can I make the post_score column automatically add/subtract the values from pvotes_score and show the sum as its value?
I've created your database structure like this:
CREATE TABLE `posts` (
`post_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`post_title` varchar(50) DEFAULT NULL,
`post_score` int(11) DEFAULT NULL,
PRIMARY KEY (`post_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
INSERT INTO `posts` VALUES (NULL, 'test', 0), (NULL, 'test2', 0);
CREATE TABLE `pvotes` (
`pvote_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`fk_post_id` int(11) DEFAULT NULL,
`fk_user_id` int(11) DEFAULT NULL,
`pvote_score` int(11) DEFAULT NULL,
PRIMARY KEY (`pvote_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
INSERT INTO `pvotes` VALUES (NULL, 1, 0, 2), (NULL, 1, 0, 3), (NULL, 1, 0, -1), (NULL, 2, 0, 2);
This is the query that should do the trick:
UPDATE posts SET post_score = (SELECT SUM(pvote_score) FROM pvotes WHERE fk_post_id = post_id);
The result I've got is this:
post_id | post_title | post_score
1 | test | 4
2 | test2 | 2

mysql one translates table vs multiple translate table

I am trying to design a structure translate(en -> fr) for all system.
my database :
translates(id,table,key_for_search,column_to_translate,lang,translate)
id | table | key_for_search | column_to_translate | lang | translate
1 | posts | 2 | title | en | hello
2 | posts | 2 | title | sp | hola
add translate to new table or when exists is possible.
this way is good ?
thanks for reply.
sorry for my bad english.
I think I talked unclear.
my tables for example:
--
-- Table structure for table `posts`
--
CREATE TABLE IF NOT EXISTS `posts` (
`id` int(11) NOT NULL,
`title` varchar(100) NOT NULL,
`content` mediumtext
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
--
-- Dumping data for table `posts`
--
INSERT INTO `posts` (`id`, `title`, `content`) VALUES
(1, 'hello', 'hello how are you ?');
-- --------------------------------------------------------
--
-- Table structure for table `translates`
--
CREATE TABLE IF NOT EXISTS `translates` (
`id` int(11) NOT NULL,
`table` varchar(255) NOT NULL,
`key` int(11) NOT NULL,
`column` varchar(255) NOT NULL,
`lang` varchar(3) NOT NULL,
`translate` text NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
--
-- Dumping data for table `translates`
--
INSERT INTO `translates` (`id`, `table`, `key`, `column`, `lang`, `translate`) VALUES
(1, 'posts', 1, 'title', 'es', 'Hola'),
(2, 'posts', 1, 'content', 'es', 'Hola, cómo estás?');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `posts`
--
ALTER TABLE `posts`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `translates`
--
ALTER TABLE `translates`
ADD PRIMARY KEY (`id`), ADD KEY `table` (`table`,`key`,`column`,`lang`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `posts`
--
ALTER TABLE `posts`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `translates`
--
ALTER TABLE `translates`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3;
and my query for get translate
select `column`,`translate` from `translates` where `table` = 'posts' && (`column` = 'title' || `column` = 'content') && `key` = 1 && `lang` = 'es' #Spanish translate
why me using this way ?
because i can in future add new table and I do not need for new design table or new programming
this is a good idea ?
Perhaps a simpler table:
xid -- code that is in the other table
lang -- language of 'xlation' column
xlation -- the translation in `lang` (use utf8 for this column)
xid could be an INT and you simply ask for another entry. Or it could be a short string. Or it could be the English version.
You would need
PRIMARY KEY(xid, lang)
and you might need this to avoid dups:
UNIQUE(lang, xlation)
Now... Think through the dataflow and what INSERTs and SELECTs you will need. You may have more questions.
(Update):
xid | lang | xlation
Hello | en | Hello
Hello | sp | hola
hello how are you ? | en | Hello. How are you?
hello how are you ? | sp | Hola. ¿Coma estas?

Categories