Maintaining/updating sets of associations - php

Suppose we have these tables:
CREATE TABLE meetings (
id INT AUTO_INCREMENT,
# other fields
PRIMARY KEY(id)
);
CREATE TABLE participants (
id INT AUTO_INCREMENT,
# other fields
PRIMARY KEY(id)
);
CREATE TABLE meetings_participations (
meeting INT, # for joining on meetings.id
participant INT, # for joining on participants.id
PRIMARY KEY(meeting,participant)
);
Given a particular $meeting_id and an array $participant_ids of participants in that meeting, what is a good way to update the table meetings_participations?
I've considered
DELETE FROM meetings_participations
WHERE meeting = $meeting_id;
INSERT INTO meetings_participations
(meeting, participant)
VALUES
$participations;
where, in PHP
$participations = join(',', array_map(function($p){
return "($meeting_id,$p)";
}, $participant_ids));
but I wonder whether it wouldn't be better to try to conserve existing records (rather than deleting unconditionally and re-inserting). Is there a better approach to the general problem?

Related

SQL: Getting values which all other values related to each must be in another table

I'm working in a web page with PHP and MySQL where I have this DB:
CREATE TABLE action (
idaction INT NOT NULL AUTO_INCREMENT,
--Other columns
PRIMARY KEY (`idaction`));
CREATE event (
idevent INT NOT NULL AUTO_INCREMENT,
--Other columns
PRIMARY KEY (`idevent`));
CREATE TABLE currentGame (
user_email VARCHAR(45) NOT NULL,
--Other columns
PRIMARY KEY (`user_email`));
CREATE TABLE IF NOT EXISTS currentEvents (
currentGame_user_email VARCHAR(45) NOT NULL,
event_idevent INT NOT NULL,
PRIMARY KEY (currentGame_user_email, event_idevent),
FOREIGN KEY (currentGame_user_email) REFERENCES currentGame (user_email),
FOREIGN KEY (event_idevent) REFERENCES event (idevent));
CREATE TABLE IF NOT EXISTS spawnConditions (
action_idaction INT NOT NULL,
event_idevent INT NOT NULL,
PRIMARY KEY (action_idaction, event_idevent),
FOREIGN KEY (action_idaction) REFERENCES action (idaction),
FOREIGN KEY (event_idevent) REFERENCES event (idevent));
So I need to do a query which has the actions which fulfill any of these conditions:
It is not in spawnConditions.
If it is in spawnConditions, all the events which it is related to in this table, must be in the subgroup of currentEvent with a certain known user_email.
In other words, for action A1, being in spawnConditions with events E1 and E2, to be able to be selected from table action, both E1 and E2 must be in currentEvents with the known currentGame_user_email.
Can it be written using only SQL or do I need to involve PHP?
I think that not exists can get the work done here. Basically, you want to filter out actions for which a corresponding record exists in currentEvents with a currentGame_user_email other than a fixed value:
select a.*
from action a
where not exists (
select 1
from spawnConditions sc
inner join currentEvents ce
on ce.event_idevent = sc.event_idevent
where
sc.action_idaction = a.idaction
and ce.currentGame_user_email <> ?
)
The question mark represents the "certain known user_email" you mentioned in the question.

Retrieving last inserted ID for InnoDB composite key table

I have the following table:
CREATE TABLE `my_table` (
composite_pk1 INT NOT NULL ,
composite_pk2 INT NOT NULL ,
data VARCHAR(255) NOT NULL ,
primary key (composite_pk1, composite_pk2)
) ENGINE=InnoDB;
For a given composite_pk1, I wish composite_pk2 to act as an autoincrement primary key. I don't wish to lock the table, and as such plan on using a trigger such as the following:
DELIMITER $$
CREATE TRIGGER my_trigger BEFORE INSERT ON my_table
FOR EACH ROW BEGIN
SET NEW.composite_pk2 = (
SELECT IFNULL(MAX(composite_pk2), 0) + 1
FROM issue_log
WHERE composite_pk1 = NEW.composite_pk1
);
END $$
I can now insert a record:
$stmt=$myDB->prepare('INSERT INTO my_table(composite_pk1, data) VALUES (?,?)');
$stmt->execute([123,'hello']);
How do I get the last inserted composite_pk2? PDO::lastInsertId only works with native autoincrement tables (i.e. not the trigger approach). I "could" later do a SELECT query to get the max value, however, there is no guarantee that another record has snuck in.
You can make composite_pk2 an unique key with auto_increment:
CREATE TABLE `my_table` (
composite_pk1 INT NOT NULL ,
composite_pk2 INT NOT NULL unique auto_increment,
data VARCHAR(255) NOT NULL ,
primary key (composite_pk1, composite_pk2)
) ENGINE=InnoDB;
Now last_insert_id() will return the recently created id for composite_pk2.

Adding lists of data to MySQL

I have some data to add to my database, I'm not sure what my table schema should be. I have an id number for each specific user, 4 categories of games, and a possible number of items (0+) each user has for each game. I want to set a table for each game with the categories ->id and ->items, so I can save the list of user id's in the table, with the items they have for that game.
I can't seem to get it to work, I think because of the dynamic number of items for each user. Is it possible for me to achieve my above mentioned table schema? Why not/how?
I have been trying:
foreach ($json->rgDescriptions as $mydata)
{
$sql = $dbh->prepare('INSERT INTO user_items_tf2 (items) VALUES (:item) WHERE steam_id = :steamid');
$sql->bindParam(':item', $mydata->name);
$sql->bindParam(':steamid', $steamprofile['steamid']);
$sql->execute();
}
There are numbers of ways to do this but one which is very flexible and seems to answer your questions would be this.
-- Players
CREATE TABLE player
(`id` int primary key auto_increment, `name` varchar(255))
;
-- Games
CREATE TABLE game
(`id` int primary key auto_increment, `name` varchar(255))
;
-- Items and what game they belong to
CREATE TABLE item
(`id` int primary key auto_increment, `game_id` int, `name` varchar(255))
;
-- What games players are playing
CREATE TABLE player_game
(`player_id` int, `game_id` int)
;
-- What items players have
CREATE TABLE player_item
(`player_id` int, `item_id` int, index(`player_id`))
;
If you never needed to ask the question which users had a given item you could skip the player_item table and stuff the data (as JSON for instance) of their items into a column of the player table with a blob type.
$sql = $dbh->prepare('INSERT INTO user_items_tf2 (items, steam_id) VALUES (:item, :steamid)');

Assign a unique ID to each name in a table where names are repeated

I have a table that contains millions of sales records and looks like this:
CREATE TABLE `sales` (
`dollar_amount` INT NULL,
`transaction_date` DATE NULL,
`company_name` VARCHAR(45) NULL,
`company_id` INT NULL);
The first three columns are populated with data. I would like to insert data into the company_id column that will identify each company with an auto_incremented integer. I plan to use the company_id field as a foreign key referencing another table that will contain each company's details. Many companies have multiple transactions, so the code needs to assign the same company_id to each row in the sales table with a matching company_name.
Is there a way to do this using only MySQL?
First, I'd recommend creating the company table:
CREATE TABLE company (
company_id INT NOT NULL AUTO_INCREMENT,
company_name VARCHAR(45),
PRIMARY KEY(company_id));
Then insert the companies from your sales data:
INSERT INTO company (company_name)
SELECT distinct company_name
FROM sales;
Finally, update your sales table with a join to get the company_id:
UPDATE sales s
JOIN company c ON s.company_name = c.company_name
SET s.company_id = c.company_id;
SQL Fiddle Demo
You should also remove the company_name field from the sales table since this is now stored in the company table.
To define an auto incremented integer, you just use the AUTO_INCREMENT keyword. However, if you define any columns as auto_increment, you must also make that column your primary key. Which, in this case, would make sense in order for it to be a foreign key elsewhere.
Try this:
CREATE TABLE `sales` (
`dollar_amount` INT NULL,
`transaction_date` DATE NULL,
`company_name` VARCHAR(45) NULL,
`company_id` INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(company_id);
SQL Fiddle

Relative MySQL Database

I have a MySQL database table which is built as follows.
The numbers below the classes denote a "level" for the class, such as high, medium, or low, with 0 being does not attend that class.
I need to build another table to save homework assignments for each class, but I'm a bit lost as to how I'd build that table, specifically denoting what the exact columns of the rows have to be, since each class does not have something specific that denotes it.
A properly normalized structure would have a separate table for courses, giving each an id, and another table placing students into courses by including a student id, course id, and level.
CREATE TABLE students (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(64) NOT NULL,
password VARCHAR(64),
email VARCHAR(...)
UNIQUE KEY (username)
);
CREATE TABLE courses (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(64) NOT NULL
);
/* If a record doesn't exist for a course and student,
the student isn't enrolled in that course. Otherwise,
the level is defined here */
CREATE TABLE enrollments (
id INT NOT NULL PRIMARY KEY,
student_id INT NOT NULL,
course_id INT NOT NULL,
level INT NOT NULL,
/* Each student may be enrolled only once per course */
UNIQUE KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES students (id),
FOREIGN KEY (course_id) REFERENCES courses (id)
);
Finally then, you can create a table for assignments assigned in each course:
CREATE TABLE assignments (
id INT NOT NULL PRIMARY KEY,
course_id INT NOT NULL,
description TEXT
/*... other columns related to the assignment as necessary*/
);
And for students to complete assignments if necessary:
CREATE TABLE student_assignments (
student_id INT NOT NULL,
assignment_id INT NOT NULL,
assignment_body TEXT, /* or whatever... */
/* Or to track when completed */
submitted_timestamp TIMESTAMP,
PRIMARY KEY (student_id, assignment_id),
FOREIGN KEY (assignment_id) REFERENCES assignments (id),
FOREIGN KEY (student_id) REFERENCES students (id)
);

Categories