MySQL logic/numeric constraints? - mysql

I'm new to MySQL and relational databases.
I'm trying to create a little soccer info website and I'm getting started with database design using PhpMyAdmin.
For now I got two tables, 'team' and 'match';
I want the 'goals' field of 'team' to be automatically updated when a new row of match (which obviously includes the goals scored by the team in that match) is added into the database, is it possible?
It would be useful because I'd use it for lots of fields (number of yellow-red cards, points in the competition table...)
Thanks!!!

Firstly, you shouldn't have a table named match since it is a keyword in MySQL. Name it matches instead.
CREATE TRIGGER update_goals
AFTER INSERT ON matches
FOR EACH ROW
UPDATE team SET goals = goals + NEW.goals
WHERE team_id = NEW.team_id;
This is called a trigger. It is called whenever you insert into the matches tables, and it will increment the goal attribute in team table for that particular team by the value of goal that you are adding to matches.
Note that the NEW keyword indicates the row that you are inserting into matches.

Related

I need to link separate table IDs

I have a dashboard where I can update, delete and create. I have 3 separated tables, Developers, Absent, Date.
Developers contains developer names and personal information. Absent is used for a segment of code, basically on certain days someone is absent. Date is mostly the same but this is for holidays instead.
I joined tables, so absent.absent_id=developers.absent_id, date.date_id=developers.date_id
When I create a developer I need to insert values. HOWEVER, I'm having a problem. The IDs of the tables need to be manually inputed through the database. I would like it so that If I create a new developer on the dashboard using Insert, The absent_id and date_id are ID linked between tables.
In short:
If create developer on submit, Add new Auto-incremented ID row to all 3 tables. Anyway this can be done?
You can achieve it with 3 SQL request, first insert new Developer and return its ID, use that to insert a new row in Absent Table and also return its ID, use both IDs to create Date row, i don't think it's related to Front-end(React) but rather a backend.
Cheers.

Database design without a 1 column table

I have been working on a database design and I'm stuck hitting a wall. I'm ending up with what I'm reading is not a normalized database structure but I'm having issues trying to find a "more correct" design and if this design is acceptable how do I execute it in Access?
TLDR: If a table with a single column set as an auto number is an acceptable design, how do you go about inserting a record in it using Access?
The segment of the database of concern is creating a structure for storing companies. Requirements for this is that any changes need to be approved by another user and all historical changes need to be captured so that it can be easily reverted also a company can have multiple aliases but only one legal name.
There is three tables in my solution but one of them is a single column table. From what I've read 95% of people on stack overflow all think its a very bad idea but I've found one post were people are that there are cases for it. I think this is not normal also because I can't find a way to just create a new record in a table with only an auto number column (In Access I have not tried others yet).
Table Structure
Company Names : ID, Company ID, Is Legal Name, Created By, Created On, Approved On, Approved By, Event ID, Is Active
(A company could have a few different names known to the public: TD vs Toronto Dominion. Each name is inserted here with a reference to the company it belongs to)
Companies : ID (Auto Number)
(A company exists and this is its ID)
Companies History : ID, Company ID, Market ID, Holding Company ID, Created By, Created On, Approved On, Approved By, Event ID, Is Active
(These are the historical changes that have been made to the company and who did them and who approved them)
Column Notes:
Event ID : is a FK reference to a table holding each record of actions that have either created, updated or deleted records. (User Research using method [y], Typo Fix, ...)
Is Active : Since deleting records is not possible (historical records need to be kept) this column is used to track if this record is to be included in queries.
Options I see and their issues:
I could get rid of the companies table and make Companies History : ID be the new company id but I find that in that case each time I want to update a company I would need to update each FK reference to the previous company id (I don't think this would be a very normalized approach)
Another Option I see is that I get rid of Companies table and use Company Names : ID as the company id and I would add a column to Company Names called Alias of Company ID. I find that solution adds a log of complexity to my stored data where an alias has company information that differs from the entry that was aliased.
Another Option is that I could add the columns: Created By, Created On, Approved On, Approved By, Event ID and Is Active but this would be duplicating information found in the first record for this company in the Companies History table and this isn't adding any real description to this record.
Anther Option is that I make the Companies table a mirror of Companies History and that when I update or insert a record in Companies I would also insert a record Companies History. With this solution I find that again I duplicate information, that newest record in "Companies History" would hold the same information found in last Inserted or Updated record in in Companies
Another option but is to replace the Companies : ID auto number with a short text and I just get the hash of the current timestamp + a random int. I can now insert new records into this table using access but I feel that this is overkill since I just need the exact same functionality as the auto number.
Another option is move only the legal name into Companies table but now when the legal name of a company changes I have no way of tracking this. Also if I want a list of all names I need to use a union on Companies and Company Names. I find that using unions can reduce performances of queries and I use them only when explicitly needed.
If I don't want to duplicate any information and I don't want to update all FK it seems that I need a table with a single column. If this is acceptable how do I go about inserting a record into a table with a single column set to auto number in Access.
If Companies can be derived from CompanyNames (select distinct CompanyId from CompanyNames), there is no point storing again that information. Just replace that table by a view if you want it (but it as little added value).
On the other hand, if CreatedOn refers to the Company creation (not the row creation) then it is obviously a property of the Company, and I would rather work with
Companies --> Aliases.
But of course I don't know the ins and outs of the reality you're dealing with.

#1109 - Unknown table 'ConcertDetails' in field list

I was attempting to run a simple Insert into my CustomerOrders table and got the error: #1109 - Unknown table 'ConcertDetails' in field list
I did some searches on this and looked at about 7 different stack overflow posts on it but still not sure what is wrong. I also looked up information on triggers and there seems to be different syntax on different sites. The weird thing is this trigger used to work just fine, not sure what has been altered since 4 days ago.
I tried changing some things, for instance I removed my trigger and it let me insert, but when I put the trigger back I couldn't insert any more, so there must be something wrong with the trigger. This is what I have for the trigger:
DROP TRIGGER IF EXISTS `alterPurchasePrice`;
DELIMITER //
CREATE TRIGGER `alterPurchasePrice` BEFORE INSERT ON `CustomerOrders`
FOR EACH ROW BEGIN
IF new.DiscountCode = 'yes' THEN
SET new.PurchasePrice = ConcertDetails.Cost - 10;
END IF;
END
//
DELIMITER ;
The purpose of the trigger is to lower the price by $10 if the user types 'yes' into the DiscountCode field.
This involves the tables:
CustomerOrders: ConcertID, CustomerName, Discount Code, OrderID,
PurchasePrice ConcertDetails: ConcertDate, ConcertID, Cost
I think you need a SELECT to retrieve values from other tables.
To get the value of "cost" from "ConcertDetails" table, for a specific concert, we could write a query like this:
SELECT ConcertDetails.cost
FROM ConcertDetails
WHERE ConcertDetails.concertid = ?
assuming that "concertid" is the primary key (or unique key) of the ConcertDetails table, we would be guaranteed that the query would return at most one row.
To put that to use in the trigger, we should be able to do something like this
SET NEW.PurchasePrice
= ( SELECT d.cost - 10 AS discount_price
FROM ConcertDetails d
WHERE d.concertid = NEW.concertid
);
The value of the concertid column of the row to be inserted from the CustomerOrders table will be supplied for the query through the reference to NEW.concertid. The return from that query will be assigned to the purchaseprice column.
If the query returns more than one row (which could happen if we don't have any kind of guarantee that "concertid" is unique in the "ConcertDetails" table), the trigger will throw a "too many rows" error.
If there are no rows returned, we'd expect a NULL value to be assigned would be assigned to "purchaseprice". We would also get a NULL returned if the "cost" column is set to NULL.
Is there some sort of guarantee that "cost" will never be less than 10? If the returned Cost is 6, then the value assigned to "purchaseprice" would be -4. If we want the value assigned to "purchaseprice" to never be less than zero, we could do something like this:
SET NEW.purchaseprice
= ( SELECT GREATEST(d.cost-10,0) AS discount_price
FROM ConcertDetails d
WHERE d.concertid = NEW.concertid
);
Other notes:
I'd recommend a different naming convention for your triggers. With multiple tables and multiple triggers, it can get kind of confusing, when looking for triggers on CustomerOrders table, to remember that the name of the BEFORE INSERT trigger is "alterPurchasePrice". Especially if you (or someone else) is coming back to work on the system six months or six years from now.
The convention we follow for trigger names is to use the name of the table, followed by an underscore, followed by one of: bi, bu, bd, ai, au, ad (for Before/After Insert/Update/Delete). (Since MySQL doesn't allow more than one trigger for each of those, we don't get naming collisions. And it makes it easier to check whether a BEFORE INSERT trigger exists on a table, before someone writes a BEFORE INSERT trigger that does something else.)
I also mention, in regards to the use of CamelCase table names... the MySQL Reference Manual says this:
To avoid problems ... it is best to adopt a consistent convention, such as always creating and referring to databases and tables using lowercase names. This convention is recommended for maximum portability and ease of use.
https://dev.mysql.com/doc/refman/5.7/en/identifier-case-sensitivity.html
Then again, these are just conventions. We also follow a convention to name tables by naming what a single row represents. If we had a requirement to create this table, we would assign the name customer_order. And the BEFORE INSERT trigger on the table would be named customer_order_bi

Database design: Managing old and new data in database table

I have a table Student with field as followed,
Student table (one record per student)
student_id
Name
Parent_Name
Address_line1, Address_line2, Addess_line
Photo_path
Signature_file_path
Preferred_examcity_choice1,Preferred_examcity_choice1, Preferred_examcity_choice3
Gender
Nationality
.
.
.
I am inserting into this table on Registration form completion through the web interface.
Now there is one more module in a web interface for updating the student data, on every update request I am updating the student table records and inserting the new entry in student_data_change_request. student can change records any number of times.
student_data_change_request
request_id(auto_incr PK)
old_name
new_name
old_photo_path
new_photo_path
old_signature_file_path
new_signature_file_path
Now coming to problem, earlier students were allowed to change very few fields, now client want to allow the candidate to update more number of fields(around 20 fields) and adding old and new columns for the corresponding column isn't elegant and preferred(I guess), I will end up creating 40 columns to keep track of 20 columns. So how should I redesign my table? suggestions are welcomed.
One approach is to have a shadow table named (table)_xx that has the same columns, the time, date, update/insert/delete flag, user or whatever and no referential integrity. Set a trigger to update that table from the source whenever anything happens.
If you've got genuine business requirements that need history then do those properly but this pattern is great as a general audit, debugging and forensic tool.
It's also really easy to automate/script as you just generate it from the DB metadata.
Usually historical table looks like:
request_id
column_name
old_value
new_value
dt
request_id and column_name are primary key. When you update student table you insert new entry in student_data_change_request for each updating column.
Edited:
Another way:
request_id
value_type
name
photo_path
signature_file_path
...
and insert first entry with old values and second entry with new values. Colum value_type is mark old or new.
I would rather have just one table, with an additional column for effective date. Then a view that picks up just the most recent row for each student_id becomes your first "table". If for some reason you must show "current" and "most recently changed" values side-by-side, that is another view.
As usual, it all depends on how you intend to use the data.
My strong preference in these cases is the solution #mathguy suggests - embedding the concept of time in the main table design. This allows you to ask the question "what was this student's address on 1 Jan?", or "who had signature x on 12 Feb?".
If you have to report or execute business logic that reflects the status at any point in time, this design works really well. For instance, if you have to report on how many students lived in a particular address for a given term, you want to know when the records were valid.
But not all applications care about "time" - sometimes, you just want to have an audit table, so you can trace what happened over time in case of anomalies.
In that case, #loztinspace's solution is useful - but in my experience, this rapidly escalates into more work, because those who want to inspect the audit records can or should not get access to a SQL prompt on your production environment.

MySQL Replace old ID with new one based on another table

Maybe my way of thought is wrong, so first off I will try to explain the situation in words.
I have a Database with about 40000 Titles, some of them are related to each other. This Relations are saved in a different database in a simple a b type manner. The problem is that the system was changed so there is a special row in the main database which contains the old id. And the b value is also the old id... However now I want to replace the b value with the new auto increment id given by the database for all the entries in the relations table. So I thought that I need to create a new table, copy the contents but replacing the b value with the new id...
CREATE TABLE list_relations_new LIKE list_listitem_relations;
INSERT list_relations_new
SELECT lr.a, li.id, lr.typeID
FROM list_listitems li
LEFT JOIN list_listitem_relations lr
ON li.oldID = lr.b
Executing this query doesn't give any errors, but also doesn't even create a new table? I hope somebody can help me to sort this out...