Lets Say I have Many to many relationship between the two tables students and subjects. So we create an intermediary table for mapping the data. Lets say it is students_subjects. The students_subjects table has 2 columns that is student_id & subject_id.
+------------+--------------+
| student_id | student_name |
+------------+--------------+
| | |
+------------+--------------+
+------------+--------------+
| subject_id | subject_name |
+------------+--------------+
| | |
+------------+--------------+
+------------+------------+
| student_id | subject_id |
+------------+------------+
| | |
+------------+------------+
Until a recent time when there is a update in students_subjects (student selecting subjects) what I did was delete all the subjects that student had an insert the new ones.
+------------+------------+----------+
| student_id | subject_id | selected |
+------------+------------+----------+
| 1 | 2 | 1 |
+------------+------------+----------+
| 1 | 2 | 0 |
+------------+------------+----------+
But recently I added a new column to this intermediary table as selected. So if a student selected new subjects and deselect some old subjects I add active for new ones and non_active for deselect ones. (I do this in PHP by getting the newly selected subjects and previously had subjects. Then compare both. When a student select subjects I check all the subjects that student has [active and non active]. If the newly selected subjects has previously non active subjects I active them and add the new ones. )
What is the best practice for doing this. I there a better way to achieve this?
I do think your first approach (deleting all associated rows and inserting new entries) is much more simpler.
If still want to have the selected field, a simple way would be to create unique composite index and using INSERT ... ON DUPLICATE KEY UPDATE. This can also prevent accidental insert of the same user_id and subject_id pair. First create the index:
ALTER TABLE `students_subjects` ADD UNIQUE `unique_student_subject`(`student_id`, `subject_id`);
Then, simply update the selected field for the student to 0 and insert the subjects the student selected. Example using PDO:
$studentId = 1;
$newlySelectedSubjects[] = ['subject_id' => 3];
$newlySelectedSubjects[] = ['subject_id' => 4];
$db = new PDO("connection string here", $username, $password);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->beginTransaction();
try{
$qry = 'UPDATE students_subjects SET selected = 0 WHERE student_id = :studentId';
$stmt = $db->prepare($qry);
$stmt->execute(['studentId' => $studentId]);
$insertQry = 'INSERT INTO students_subjects (student_id, subject_id, selected)'
.'VALUES (:studentId, :subjectId, 1) '
.'ON DUPLICATE KEY UPDATE '
.'selected = 1';
$insertStmt = $db->prepare($insertQry);
foreach ($newlySelectedSubjects as $subject) {
$params = ['studentId' => $studentId, 'subjectId' => $subject['subject_id']];
$insertStmt->execute($params);
}
$db->commit();
} catch (PDOException $e) {
//do something with exception
$db->rollBack();
}
What will happen is, if the student with student_id = 1 and subject_id = 3 already exist in the table, the selected field will be set to 1, if not a new record will be inserted.
If you require to track the user subjects selection and the de-selection, I suggest implementing some sort of audit trails and leave the students_subjects to only hold the relationship between students and subjects. It can be easier with the help of mysql TRIGGER.A simplified example of an audit trail table:
+------------+------------+----------+---------------------+
| student_id | subject_id | action | action_datetime |
+------------+------------+----------+---------------------+
| 1 | 2 | remove | 2018-01-01 00:01:23 |
+------------+------------+----------+---------------------+
| 1 | 4 | add | 2018-01-01 01:07:45 |
+------------+------------+----------+---------------------+
Related
I have three tables
purchase_master
+--------+---------+------------+
| autoid | user_id | package_id |
+--------+---------+------------+
user_master
+--------+---------+------+------------+-----------+
| autoid | user_id | name | user_email | user_pass |
+--------+---------+------+------------+-----------+
package_master
+-------------+----------+--------------+-----------+------------+---------------+------------+
| poster_path | overview | release_date | genre_ids | package_id | original_title| **isfree** |
+-------------+----------+--------------+-----------+------------+---------------+------------+
I want that whenever a new row is created into user_master, a trigger should fire that gets all rows in package_master where isfree = true, and populate those records along with new user_master.user_id into purchase_master table.
Example
lets say package_master has 2 rows having package_id = 100 & package_id = 101, in which isfree=true so when a new record is created into user_master having user_id = C601, the purchase_master table should have 2 new entries like.
"autoid" = 10,"user_id"=C601,"package_id"=100
"autoid" = 11,"user_id"=C601,"package_id"=101
Please guide me if it is possible through mysql trigger. Else I have to do it in PHP, on form submission.
EDIT:: I am using phpmyadmin
Try something like this:
CREATE TRIGGER bi_user_master BEFORE INSERT ON user_master
INSERT INTO purchase_master (user_id, package_id)
SELECT NEW.user_id, pm.package_id
FROM package_master AS pm
WHERE isfree;
I have two table name users and users_images. Both table have the value of userId. like
My user table
| userId | userName | user_address |
| 2 | John | CN-2, UK |
| 3 | Amit | India |
| 4 | David | Us |
| 5 | Shan | Canada |
.
.
...... and so on
| 125000 | Naved | Ukran |
**and my images table contain userid and Image name.
Now I want to merge ImageName field to user table without using any loop (I want to do it with single query (I have millions of records and I will have to do it many times to create temorary table) )
update users u
set
u.imageName = (
select imageName
from users_images i
where i.userid = u.userid GROUP BY u.userId )
you could use ON DUPLICATE KEY
for instance:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
I think you can use Update for this like:
UPDATE Users
SET ImageName =
(SELECT ImageName
FROM UserImages
WHERE UserImages.UserID = Uers.UserID)
Please take a backup of your database first
I have a table student, table structure is follow
student_id | name | class|
-------------------------
2-12-2013 | test | 3 |
-------------------------
2-13-2013 |test2 | 5 |
-------------------------
in this table i want add new record, its student_id become 2-14-2013. For that i want to get the last inserted student_id. how can i get it by MySQL query. Am alredy use mysql_insert_id() But its not working, How to solve this issue?
Define Your student table like below:-
student_id | add_date | name | class|
--------------------------------------
1 | 2013-12-2 | test | 3 |
--------------------------------------
2 | 2013-13-2 |test2 | 5 |
--------------------------------------
student_id would be int auto increamented primary key
then execute Query like below to get last inserted id:-
select student_id from student order by student_id desc limit 1;
Hi You has to set primary key to your table as integer auto-increment then you can use this last inserted id
$con = mysql_connect("localhost", "root", "", "chirag2");
$sql = "INSERT INTO test_student ( student_id,name,class)values('3-12-2013','test1','3' )";
$res = mysql_query($con, $sql);
echo mysql_insert_id($con);
die;
Note: you need to truncate table before alter it and set new primary key to it so take backup before this
I have one field in the backend, where I input IDs separated by comma - ID1, ID2, ID3....These are videos in fact. All ids are stored in the field product_videos in the database (as they are typed).
How can I echo these id's on the frontend so they all show for the product?
Storing comma separated data in one data field is a bad idea. It is a real pain to manipulate, so you should really consider revising your db structure.
You haven't shown your data structure, so I'll give a basic example and then explain how it can be improved. My example assumes product_videos is linked to particular users:
table: `users`
| user_id | user_name | product_videos |
|---------|-----------|----------------|
| 1 | User1 | 1,2,3,4,6,7 |
| 2 | User2 | 5 |
You would maybe run a query
SELECT `product_videos` FROM `users` WHERE `user_name` = 'User1'
This would give you one row, with a comma separate value - you would then need to use something like PHP's explode() to convert it into an array and then loop through that array. That is a very bad method (and it will only become harder as you try to do more advanced things).
Instead, it would be easier to use a link table. Imagine:
table: `users`
| user_id | user_name |
|---------|-----------|
| 1 | User1 |
| 2 | User2 |
table: `videos`
| video_id | user_id |
|-----------|---------|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 6 | 1 |
| 7 | 1 |
In this example, each video is a separate row in a db table (and each video is linked to an existing user). Each row is readily able to be handled independently. This makes it really easy to handle extra data for each video, such as a title, runtime length, date of uploading, etc.
You would then need to run a JOIN query. e.g.
SELECT `videos`.`video_id` FROM `videos`
INNER JOIN `users` ON `users`.`user_id` = `videos`.`user_id`
WHERE `users`.`user_name` = 'User1'
In PHP, you would do something like:
$q = mysql_query("SELECT `videos`.`video_id` FROM `videos` INNER JOIN `users` ON `users`.`user_id` = `videos`.`user_id` WHERE `users`.`user_name` = 'User1'");
while ($row = mysql_fetch_assoc($q)) {
echo "VIDEO ID = " . $row["video_id"] . "<br/>";
}
I have a members table in MySQL
CREATE TABLE `members` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(65) collate utf8_unicode_ci NOT NULL,
`order` tinyint(3) unsigned NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
And I would like to let users order the members how they like.
I'm storing the order in order column.
I'm wondering how to insert new user to be added to the bottom of the list.
This is what I have today:
$db->query('insert into members VALUES (0, "new member", 0)');
$lastId = $db->lastInsertId();
$maxOrder = $db->fetchAll('select MAX(`order`) max_order FROM members');
$db->query('update members
SET
`order` = ?
WHERE
id = ?',
array(
$maxOrder[0]['max_order'] + 1,
$lastId
));
But that's not really precise while when there are several users adding new members at the same time, it might happen the MAX(order) will return the same values.
How do you handle such cases?
You can do the SELECT as part of the INSERT, such as:
INSERT INTO members SELECT 0, "new member", max(`order`)+1 FROM members;
Keep in mind that you are going to want to have an index on the order column to make the SELECT part optimized.
In addition, you might want to reconsider the tinyint for order, unless you only expect to only have 255 orders ever.
Also order is a reserved word and you will always need to write it as `order`, so you might consider renaming that column as well.
Since you already automatically increment the id for each new member, you can order by id.
I am not sure I understand. If each user wants a different order how will you store individual user preferences in one single field in the "members" table?
Usually you just let users to order based on the natural order of the fields. What is the purpose of the order field?
Usually I make all my select statements order by "order, name"; Then I always insert the same value for Order (either 0 or 9999999 depending on if I want them first or last). Then the user can reorder however they like.
InnoDB supports transactions. Before the insert do a 'begin' statement and when your finished do a commit. See this article for an explanation of transactions in mySql.
What you could do is create a table with keys (member_id,position) that maps to another member_id. Then you can store the ordering in that table separate from the member list itself. (Each member retains their own list ordering, which is what I assume you want...?)
Supposing that you have a member table like this:
+-----------+--------------+
| member_id | name |
+-----------+--------------+
| 1 | John Smith |
| 2 | John Doe |
| 3 | John Johnson |
| 4 | Sue Someone |
+-----------+--------------+
Then, you could have an ordering table like this:
+---------------+----------+-----------------+
| member_id_key | position | member_id_value |
+---------------+----------+-----------------+
| 1 | 1 | 4 |
| 1 | 2 | 1 |
| 1 | 3 | 3 |
| 1 | 4 | 2 |
| 2 | 2 | 1 |
| 2 | 3 | 2 |
+---------------+----------+-----------------+
You can select the member list given the stored order by using an inner join. For example:
SELECT name
FROM members inner join orderings
ON members.member_id = orderings.member_id_value
WHERE orderings.member_id_key = <ID for member you want to lookup>
ORDER BY position;
As an example, the result of running this query for John Smith's list (ie, WHERE member_id_key = 1) would be:
+--------------+
| name |
+--------------+
| Sue Someone |
| John Smith |
| John Johnson |
| John Doe |
+--------------+
You can calculate position for adding to the bottom of the list by adding one to the max position value for a given id.