New with databases, having problems wiith primary key - mysql

I have a table where events organized by a sports center are stored.
I created it like this:
create table events(
name varchar(20) primary key,
description varchar(150),
type varchar(20) not null,
event_date not null,
event_start time not null,
event_end time not null,
room_name varchar(20) not null,
foreign key(room_name) references rooms(name) on update cascade on delete cascade);
now I have realized that events with the same name can exist as long as they are not on the same day.
For example, a swimming competition can be held several times in a month and still have the same name. How can I modify the table so that the primary key is made up of name and date without having to delete and re-create the table?

You could use an alter table statement to drop the primary key and add a new one:
ALTER TABLE events DROP PRIMARY KEY;
ALTER TABLE events ADD PRIMARY KEY (name, event_date);

It's worth reading up about primary keys.
As #mureinik says, you can drop the original key and create a new key - but your design has a few problems.
Firstly, primary keys should never change. Columns like "Name" suggest they can change - even if the user corrects a typo, or if the event changes its name (e.g. to add a sponsor). This goes for "room" too.
Secondly, your design combines two logical entities into a single table. I think you have an entity called event_type (tennis tournament), and an event instance (1 July 2021). I think there will be more attributes of event type, which you won't want to copy for each instance (e.g. "description").
So, I think your schema is more like
event_type
----------
event_id int pk
event_name varchar
event_type varchar
event_description varchar
event_instance
--------------
event_type_id int fk pk
event_date date pk
event_start time not null,
event_end time not null,
room_id int fk

Related

MySQL table for single column

This is a question about database design. Say I have several tables, some of which each have a common expiry field.
CREATE TABLE item (
id INT PRIMARY KEY
)
CREATE TABLE coupon (
id INT PRIMARY KEY FOREIGN KEY (`item.id`),
expiry DATE NOT NULL
)
CREATE TABLE subscription (
id INT PRIMARY KEY FOREIGN KEY (`item.id`),
expiry DATE NOT NULL
)
CREATE TABLE product(
id INT PRIMARY KEY FOREIGN KEY (`item.id`),
name VARCHAR(32)
)
The expiry column does need to be indexed so I can easily query by expiry.
My question is, should I pull the expiry column into another table like so?
CREATE TABLE item (
id INT PRIMARY KEY
)
CREATE TABLE expiry(
id INT PRIMARY KEY,
expiry DATE NOT NULL
)
CREATE TABLE coupon (
id INT PRIMARY KEY FOREIGN KEY (`item.id`),
expiry_id INT NOT NULL FOREIGN KEY(`expiry.id`)
)
CREATE TABLE subscription (
id INT PRIMARY KEY FOREIGN KEY (`item.id`),
expiry_id INT NOT NULL FOREIGN KEY(`expiry.id`)
)
CREATE TABLE product(
id INT PRIMARY KEY FOREIGN KEY (`item.id`),
name VARCHAR(32)
)
Another possible solution is to pull the expiry into another base "class" table.
CREATE TABLE item (
id INT PRIMARY KEY
)
CREATE TABLE expiring_item (
id INT PRIMARY KEY FOREIGN KEY(`item.id`),
expiry DATE NOT NULL
)
CREATE TABLE coupon (
id INT PRIMARY KEY FOREIGN KEY (`expiring_item .id`),
)
CREATE TABLE subscription (
id INT PRIMARY KEY FOREIGN KEY (`expiring_item .id`),
)
CREATE TABLE product(
id INT PRIMARY KEY FOREIGN KEY (`item.id`),
name VARCHAR(32)
)
Given the nature of databases in that refactoring the table structure is difficult once they are being used, I am having trouble weighing the pros and cons of each approach.
From what I see, the first approach uses the least number of table joins, however, I will have redundant data for each expiring item. The second approach seems good, in that any time I need to add an expiry to an item I simply add a foreign key to that table. But, if I discover expiring items (or a subset of expiring items) actually share another attribute then I need to add another table for that. I like the third approach best, because it brings me closest to an OOP like hierarchy. However, I worry that is my personal bias towards OOP programming, and database tables do not use composition in the same way OOP class inheritance does.
Sorry for the poor SQL syntax ahead of time.
I would stick with the first design as 'redundant' data is still valid data if only as a record of what was valid at a point in time and it also allows for renewal with minimum impact. Also the second option makes no great sense as the expiry is an arbritrary item that has no real context outside of the table referencing, in other words unless it is associated with a coupon or a subscription it is an orphan value. Finally the third option makes no more sense in that at what point does a item become expiring? as soon as it is defined? at a set period before expiry...at the end of the day the expiry is an distinct attribute which happens to have the same name and purpose for both the coupon and the subscription but which isn't related to each other or as such the item.
Do not normalize "continuous" values such as datetime, float, int, etc. It makes it very inefficient to do any kind of range test on expiry.
Anyway, a DATE takes 3 bytes; an INT takes 4, so the change would increase the disk footprint for no good reason.
So, use the first, not the second. But...
As for the third, you say "expirations are independent", yet you propose having a single expiry?? Which is it??
If they are not independent, then another principle comes into play. "Don't have redundant data in a database." So, if the same expiry really applies to multiple connected tables, it should be in only one of the tables. Then the third schema is the best. (Exception: There may be a performance issue, but I doubt it.)
If there are different dates for coupon/subscription/etc, then you must not use the third.

Using databases and tables (MySQL)

On my website, customers have the option of creating an event with various items (that have attributes like seller, color, etc.).
Should I have ONE database and a new table for each event? I don't know of another way to program this and splitting every customer/event into a new database seems like a bad solution, but I'm new to databases and don't know if that's stupid.
I assume that I'd have a TABLE with user IDs, a TABLE for each event, and a TABLE that links the user to the event(s) he/she created. Is this the optimal way to do this? All in one database?
Thanks!
You should have a one-to-many relationship between a user table, and an event table. The event table should have the user ID as a foreign key.
CREATE TABLE user (
id int UNSIGNED AUTO_INCREMENT NOT NULL,
name varchar(50) NOT NULL,
last_modified timestamp NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY (name)
);
CREATE TABLE event (
id int UNSIGNED AUTO_INCREMENT NOT NULL,
user_id int UNSIGNED NOT NULL,
name varchar(50) NOT NULL,
description varchar(500) NOT NULL,
last_modified timestamp NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES user.id ON UPDATE CASCADE ON DELETE CASCADE
);
So then, you have your users, and when you add an event, you just assign the user_id to whatever user the event is for. Hopefully that gives you something to build from.
If you have a small number of event types, and the event types are very different in nature (different properties) then you may create a different table for each event type.
However usually you will create one database with one table for all the events, with a column for event type or code (if needed). You also don't need to create a table that contain all the event types, your code can contain them. You table should contain only the actual events that were fired.

One field with two references in MySQL

I have three tables:
CREATE TABLE Address (
ResidentID CHAR(5) NOT NULL,
Location varchar(255) NOT NULL,
KEY ResidentID(ResidentID)
);
CREATE TABLE Customer (
CustomerID CHAR(5) NOT NULL,
ContactName varchar(40) NOT NULL,
PRIMARY KEY (CustomerID)
);
CREATE TABLE Supplier (
SupplierID CHAR(5) NOT NULL,
SupplierName varchar(40) NOT NULL,
PRIMARY KEY (SupplierID)
);
I want to store CustomerID and SupplierID in the Address.ResidentID field with using of foreign keys:
ALTER TABLE Address ADD CONSTRAINT fk_CustomerID1 FOREIGN KEY(ResidentID) REFERENCES Customer(CustomerID);
ALTER TABLE Address ADD CONSTRAINT fk_SupplierID1 FOREIGN KEY(ResidentID) REFERENCES Supplier(SupplierID);
But second 'ALTER TABLE' raises Error: relation already exists
Any suggestions?
Data example:
CustomerID ContactName
C0001 Den
SupplierID ContactName
S0001 John
So Address table should contains:
ResidentID Location
C0001 Alaska
S0001 Nevada
You need to either reference addresses from the Customer / Supplier (if they only have one) or two different columns.
The reason you see in this SQLFiddle You cannot INSERT the required columns into the Address table if the ResidentID references BOTH tables. You could only insert lines that would match the contents of Customer AND Supplier but you want an OR connection that you can't create that way.
(Note: In my solutions I assume addresses to be optional. As Tom pointed out in the comments that may not be what you wanted, or expected. Make sure to mark the FK Columns in the first solution as NOT NULL if you want addresses to be mandatory, its more complicated for the second one. You have to mind the correct insertion order then.)
Either:
CREATE TABLE Address (
AddressID CHAR(5) NOT NULL,
Location varchar(255) NOT NULL,
PRIMARY KEY (AddressID)
);
CREATE TABLE Customer (
CustomerID CHAR(5) NOT NULL,
AddressID CHAR(5),
ContactName varchar(40) NOT NULL,
PRIMARY KEY (CustomerID)
);
CREATE TABLE Supplier (
SupplierID CHAR(5) NOT NULL,
AddressID CHAR(5),
SupplierName varchar(40) NOT NULL,
PRIMARY KEY (SupplierID)
);
ALTER TABLE Customer ADD CONSTRAINT fk_AddressID_Cust FOREIGN KEY(AddressID) REFERENCES Address(AddressID);
ALTER TABLE Supplier ADD CONSTRAINT fk_AddressID_Supp FOREIGN KEY(AddressID) REFERENCES Address(AddressID);
or
CREATE TABLE Address (
CustomerID CHAR(5),
SupplierID CHAR(5),
Location varchar(255) NOT NULL,
PRIMARY KEY (CustomerID, SupplierID)
);
CREATE TABLE Customer (
CustomerID CHAR(5) NOT NULL,
ContactName varchar(40) NOT NULL,
PRIMARY KEY (CustomerID)
);
CREATE TABLE Supplier (
SupplierID CHAR(5) NOT NULL,
SupplierName varchar(40) NOT NULL,
PRIMARY KEY (SupplierID)
);
ALTER TABLE Address ADD CONSTRAINT fk_CustomerID1 FOREIGN KEY(CustomerID) REFERENCES Customer(CustomerID);
ALTER TABLE Address ADD CONSTRAINT fk_SupplierID1 FOREIGN KEY(SupplierId) REFERENCES Supplier(SupplierID);
The approach you're trying is (a) not possible and (b) undesirable even if it was possible.
The best approach is to have a CustomerAddress table and a SupplierAddress table, each with a single FK to the matching base table; or if you must, a cross-reference table with appropriate constraints.
If your motivation for having a single Address table was code reuse, you can still do that ... think in terms of a template xxxAddress table design that can refer to any base xxx table. You can write non-database code that treats the base table name as a parameter and then could handle any number of xxxAddress tables as you add more base tables over time.
Or if your motivation for having a single Address table was to simplify reporting, you can always create a view or stored proc that returns a union of all such tables + an added field to indicate the base table for each address row.
Angelo I am revising this a bit based on your comment ---
Angelo, I ran your sample code in a local MySQL instance (not SQLFiddle) and observed an error.
I was surprised (you learn something every day) that MySQL did allow two foreign key constraints to be defined on the same field; however when you attempt to insert data, when trying to point the FK to the Customer table, I get an error saying a foreign key constraint fails referencing the Supplier table; and vice versa for the insert trying to point the FK to the Supplier table.
So my revised statement is (a) it is possible to create the hydra-headed FK in at least some DBMSs -- verified in MySQL, MS SQL Server and Oracle -- although (b) this only makes sense to use when the foreign key can refer to the same logical entity by ID across multiple tables (e.g. to ensure there is a corresponding record in all required tables, for example); and (c) if used to refer to multiple tables where the primary key is NOT the same logical entity, only works if by chance the same primary key value just happens to exist in all referenced tables, which is likely to lead to subtle, hard-to-find errors.
In other words, your example would work when attempting to insert a record referring to Customer ID=3 only if there was also a Supplier ID=3, which are really logically unrelated.
So my slightly revised answer to the OP is, what you're trying to do is not possible (or logical) when the foreign key is referring to different ENTITIES, as in the OP example of Customers and Suppliers.

DBMS design for multivalued attributes

I have to design a database where some information about usage of printer resource is to be recorded in a mysql database. What is the best way to design the database?
I do not want to create a table for each student as there would be around 6000 tables and which would keep growing each year if archives are to be maintained. Also it is difficult to create tables based on registration number of student. Is there a better way than storing multivalued attribute for details of printing. Please suggest some solutions. Also querying should be effective.
There is no need to create different tables for each student.
Just create a Table STUDENT which will contain the personal details of the student identified by their Registration number (lets say Regno-PrimaryKey).
And then another Table RESOURCE, which will have the following schema:
-RecNo Integer PK
-StudentID Foriegn key referenced to Regno in the Student Table
-Usage
or Data,Time(if you require)
This will work for and you need not have to create 6000 or more tables.
You have given very few information, but here is a shot:
create table student
(
registration_no varchar(50) not null primary key,
first_name varchar(50),
last_name varchar(50),
registration_year integer not null
);
create table printer
(
printer_id integer not null primary key,
printer_name varchar(50) not null,
ip_address varchar(50) not null,
queue_name varchar(50) not null
);
create unique index idx_printer_name on printer (printer_name);
create table printer_usage
(
usage_id integer not null primary key,
student_reg_no integer not null,
printer_id integer not null,
usage_date datetime not null,
pages integer not null
);
alter table printer_usage
add constraint fk_usage_student
foreign key (student_reg_no) references student (registration_no);
alter table printer_usage
add constraint fk_usage_printer
foreign key (printer_id) references printer (printer_id);
You will probably need to add more columns to the tables to store all the things you need. I was just guess stuff that you might want to store.

How do you create a constraint on parent tables that also constrains the child tables?

I am not sure how to phrase the question so I'll illustrate the tables and the explain what I want to achieve.
-- static table of the entity classes supported by the application
create table entity_type (
id integer not null auto_increment,
name varchar(30) not null,
primary key(id)
);
-- static table of statuses supported by the application
create table entity_status (
id integer not null auto_increment,
name varchar(30) not null,
primary key(id)
);
-- table of valid combinations
create table entity_type_entity_status_link (
entity_type_id integer not null,
entity_status_id integer not null,
unique key(entity_type_id, entity_status_id),
foreign key(entity_type_id) references entity_type(id),
foreign key(entity_status_id) references entity_status(id),
);
-- The tables where user types and statuses are defined
create table user_type (
id integer not null auto_increment,
name varchar(30) not null,
entity_type_id integer not null,
primary key(id),
foreign key(entity_type_id) references entity_type(id)
);
create table user_status (
id integer not null auto_increment,
name varchar(30) not null,
entity_status_id integer not null,
primary key(id),
foreign key(entity_status_id) references entity_status(id)
);
-- table of valid pairs
create table user_type_user_status_link (
user_type_id integer not null,
user_status_id integer not null,
unique key(user_type_id, user_status_id),
foreign key(user_type_id) references user_type(id),
foreign key(user_status_id) references user_status(id),
);
The basic premise behind these tables is that the system supports core types and statuses and the user is able to create their own user types and statues that derive from these.
The question I have is that I cannot see a way of creating any database constraints on the user_type_user_status_link table to ensure that the you cannot insert a file_type - file_status pair where the parent entity_type - entity_status is itself not valid. Or is this something that would have to be done with triggers.
The basic premise behind these tables is that the system supports core
types and statuses and the user is able to create their own user types
and statues that derive from these.
Although that sounds like a laudable goal on the surface, the effect is to delegate database design to your users. Database design, because the effect of your desire to set foreign key references to a subset of the rows in entity_type_entity_status_link means each of those subsets is a defacto, unnamed table.
This approach never ends well.
What you've developed is the "One True Lookup Table". Google that for a host of reasons why OTLT is an anti-pattern.
The best solution is to model real things in your tables. (Entity isn't a real thing. It's an abstraction of a real thing.) Something along the lines of either
create table file_status (
file_status varchar(30) primary key
);
or
create table file_status (
file_status_id integer primary key,
file_status varchar(30) not null unique
);
would work well for file statuses.
In the case of the second one, you can set a foreign key reference to either the id number (saves space, requires an additional join) or to the status text (takes more space, eliminates a join). Note that you need the unique constraint on the status text; your original design allows the user to enter the same text multiple times. (You could end up with 30 rows where entity_type.name is 'File'.
You should use triggers for that.
MySQL does not support constraints of the form that will prevent what you want.