MySQL DB structure, need clarification - php

I'm designing a website in which you can upload games with a description, etc, and store their rating. I am the only one who can provide a rating, so I won't need a second DB for that.
However, there will be indefinite rounds of competitions, so I want to store the Round # in a column in the DataBase. The other requirement is that each round has it's own information, so I'll be making a second table with a Round_Number, then other fields like Description, Due Date, etc.
So this is my current DB schema idea:
Entries ( Comp #, Entry # AutoInc, Password,
Entry Date, Author Contact, Description, Screenshot, Website )
Competitions ( Comp #, Theme, Entries, Start Date, Finish Date, Prize )
So Entries can left join Competitions on Comp # to get extra information about its competition. Does these seem most efficient? There is no account system (or users table), each entry just gets its own password for editing.

I would think this through further. You could potentially have different screenshots for different Entries and competitions. You might as well define a relational schema and add user accounts for authentication purposes. Define three tables:
CREATE TABLE USERS_T
(
account_id number,
username varchar,
password data_type,
author varchar,
....
)
CREATE TABLE COMPETITIONS_T
(
comp_id number,
description varchar,
theme varchar,
#entries number- not needed, just count by id in ENTRIES_T table
start_date date,
finish_date date,
prize varchar,
...
)
CREATE TABLE ENTRIES_T
(
account_id number,
comp_id number,
entry_date date,
descr varchar,
screenshot blob,
website varchar,
...
)
Each competition can have multiple entries, but each entry can only be for 1 competition.
Add a primary key on the ENTRIES_T table: PRIMARY KEY (account_id, comp_id)
You could even take this further, define a themes table, comments table, etc. I think you'll get the idea. Hope this helps. Goodluck!

Related

(sql) input using select statement

i wanna make a project to find a rented house / room
user can post his room/house to rent
and others can see it on the map
first user should login then he register his location in table location.
table location consist id_location, lat, lng, id_user, etc.
id_location is auto increment,its primary key.
id_user is from session that user logged on, it reads that username, Its foreign key.
rest are input by user,
i've succeed to make till this point
and the next thing is user gonna pick house or room to rent
example; they wanna rent a room.
this room table has id_room, id_user,id_location, rate, etc.
id_room is auto increment, primary key.
id_user is from session that user logged on, so it reads the username.
and the problem is for the id_location.
how can the database had that id_location from the last location input / location tbl?
Because that a location can have more than 1 room and the type of the room can be different, like it rate, facility, or etc.
so i make it several input for each room,
then user can creates its own room data for each room but has a same id_location
i mean something like
insert into tbl_room
values (null, '$_SESSION[username]', '$rate','$bla' ,'...','...' ,'...' , )
where id_location from location.id_location
but i know 'where' cant be in input scenario ..
i've read something about select statement
but i dont know how that select works in my case, i mean the exact query will be
i hope u r understand about my problem
thanks be4
On this case you have a one to many relationship, where one location can have many rooms. The simple solution is to have a column on the rooms table that references the location id from the location the room belongs to.
You can make something like this on MySQL
Create table locations (
Id int auto_increment,
Location varchar(100)
)
Create table rooms (
Id int auto_increment,
Location_id int
)
ALTER TABLE rooms ADD CONSTRAINT fk_location
FOREIGN KEY ( location_id` ) REFERENCES locations (id)
Insertions would be similar to this:
Insert into room (location_id) values ({$locationId});
And then you can get the room location by something like
Select l.location
From location l
Inner join rooms r on r .id_location = l.id
Where r.id = {$roomId}

MySQL Database: How far to Normalize / Queries VS Join / Unique Index

Lately i found myself designing a database. The database is consisted of several tables (InnoDB) :
Table 1: Country (id , country_name)
Table 2: City (id, city_name , countryid)
Table 3: Users (id , cityid , A , B, C, D, E)
On the Users table, A , B ,C , D and E are some characteristics of the user, where characteristic A if you combine it with cityid must be unique, that is why i created a unique index for these 2 columns:
CREATE UNIQUE INDEX idx_user ON Users(cityid , A);
The rest columns B,C,D and E are other user characteristics (for example hair color, height, weight, etc.), that as you understand, will be repeated on the table ( hair color = black, or weight = 75 kg).
At the same time countryid and cityid are configured as foreign keys on UPDATE and DELETE CASCADE.
Search will be based on cityid and A columns. A drop down menu to select the city (hence cityid) and a text box to insert the characteristic A and then hit SEARCH button.
My questions are:
On Users table, i have repeating data in the same column (columns B, C ,D and E). This is against 2NF. Do i have to create a separate table for each of these columns and then assign a foreign key of each of these tables to Users table in order to achieve 2NF?
Table B (id, Bchar)
Table C (id, Cchar)
Table D (id, Dchar)
Table E (id, Echar)
Users (id, cityid, A, Bid, Cid, Did, Eid)
For the time i will not use columns B,C,D and E as search data, only display them after searching using cityid and A search. If (in the future) i decide that i need to display all results of Users that live in cityid and have black hair, what do i have to keep in mind now while designing the database?
In one hand we have DML(INSERT, UPDATE, DELETE) and on the other hand quering (SELECT). DML will work faster on normalized DBs and quering on denormalized DBs. Is there a middle solution?
Will UNIQUE INDEX created above , be enough to ensure uniqueness for the combination of the data in columns cityid and A? Do i need to further restrict it using JavaScript or better PHP?
Multiple Queries VS Joins:
Normalizing the database will require multiple queries or a single query with joins. In the case where "The user searches for a user from Madrid with characteristic A":
a) Multiple queries:
i) Go to City table and find the id of Madrid (for example, id = 2 )
ii) Given the Madrid id and the input for characteristic A, go to Users table and SELECT * FROM Users WHERE cityid="2" AND A="characteristic";
b) INNER JOIN:
i) SELECT City.city_name, Users.B, Users.C FROM City INNER JOIN Users ON Users.cityid = City.id;
Which one should i prefer?
Thanks in advance.
Your tables are already in 2NF.The condition for 2NF is there should be no partial dependency.For example lets take your users table and user-id is the primary key and another primary key more appropriate to call candidate key is (cityid,A) with which you can uniquely represent a row in the table.Your table is not in 2NF if cityid or A alone is enough to uniquely retrieve B,C,D or E but in your case one needs both (cityid,A) to retrieve a unique record and hence it's already normalized.
Note:
Your tables are not in 3NF.The condition for 3NF is no transitive dependency.Let's take the users table here userid is the primary key and you can get a unique (cityid,A) pair with that and in turn you can get a unique (B,C,D,E) record with (cityid,A) obtained from userid.In short if A->B and B->C indirectly A->C which is called transitive dependency and it's present in your user table and hence it's not a suitable candidate for 3NF.

Using MySQL IGNORE on one column under certain conditions

I want to import records from Gmail into a table, and I do not need duplicates for each account.
Description:
I have a table named list with definition below:
id int(11),
account_id int(11),
email varchar(255),
phone varchar(30),
primary key(id),
FOREIGN KEY (account_id) REFERENCES accounts (id)
This table holds records for different accounts and an email can be considered valid for two or more accounts. This means that an email can repeat in a table but can only appear once for each account_id.
I imported my contacts from Gmail (which is above 700 contacts and other users may have more than that).
The challenge:
I have an option of running two queries (one to check if email or phone exists, the second to insert record) for each record which in my case is 1,400 SQL queries to enable me insert all imported records, ensuring there are no duplicates for each account_id in the list table.
I have looked at MySQL IGNORE and similar keywords like ON DUPLICATE KEY UPDATE but they do not seem to work in this scenario as I cannot make the email and phone columns unique as they can contain duplicate content.
What is the best way of inserting these 700 records ensuring that the email and phone are not repeated for each account_id without having to run 1,400 queries?
QUESTION UPDATE:
I do not think INSERT IGNORE CAN WORK HERE FOR THE FOLLOWING REASONS:
I cannot make email and phone unique columns
The phone number may be empty but with an email entry, this may break the unique pattern
QUESTION ILLUSTRATION
I have two offices using the table to store their customer records. Someone can be a customer to both offices. This means his record can appear twice in the table but can only appear once for each account_id in the table.
The challenge now is to insert several records into the table ensuring that a record does not repeat for each account_id.
What you are trying to achieve is not very clear to me, but it looks very much like you just need to add some two-columns unique constraints.
an email must be unique for one given account_id:
ALTER TABLE your_table ADD UNIQUE (account_id, email);
a phone number must be unique for one given account_id:
ALTER TABLE your_table ADD UNIQUE (account_id, phone);
Both indexes may exist at the same time on your table. Either could raise a "duplicate-key violation" error, and would trigger the IGNORE or the ON DUPLICATE clauses of your insertions.
That being said, there is an issue in your structure. You are about to duplicate your customers' details for each account_id they are in business with.
You should have a customers table that contains all your customer's contact details (and only that), another accounts table -- your "offices", if I understand it right -- and finally one relation table to model the n-n relationship between customers and accounts:
CREATE TABLE customers_accounts (
customer_id INT NOT NULL,
account_id INT NOT NULL,
PRIMARY KEY (customer_id, account_id),
FOREIGN KEY (customer_id) REFERENCES customers(id)
FOREIGN KEY (account_id) REFERENCES accounts(id)
);
You had the answer: use "INSERT IGNORE" but what you probably didn't do is add a composite unique index (mentioned by RamdomSeed above), and/or set blank fields to NULL.
1) Create composite index, using the account id. This means that the email must be unique for that user.
ADD UNIQUE(account_id, email)
2) Regarding the phone "may be blank" set this to NULL when blank. Unique indexes ignore NULLS. (A small gotcha, but probably plays in your favour here, and why it's like that. You can then also add
ADD UNIQUE(account_id, phone)
(Aside: general advice is that you don't usually have multiple uniques on a table as it can get confusing and messy, but it might be what you need and it's fine - so long as you can handle the logic)
Seems like you could use INSERT IGNORE assuming AccountId is your unique identifier:
INSERT IGNORE INTO table
SET field = someValue,
anotherfield = someothervalue
If however you can have the same accounts with multiple emails, then this may not be what you're looking for.
So it sounds like you're using a scripting language (php seems to be popular with mysql) to store an array of contacts from gmail?
If so, this insert statement will insert the record if the account id doesn't exist in the table already -- this uses an Outer Join with a Null check, but you can also use Not In or Not Exists as well:
Insert Into YourTable (Id, AccountId, Email, Phone)
Select t.Id, t.AccountId, t.Email, t.Phone
From (Select 1 Id, 1 AccountId, 'someemail' Email, 'somephone' Phone) t
Left Join YourTable t2 On t.AccountId = t2.AccountId
Where t2.AccountId Is Null
EDIT:
Assuming I'm understanding the comments, then just add to the Outer Join:
Insert Into YourTable (Id, AccountId, Email, Phone)
Select t.Id, t.AccountId, t.Email, t.Phone
From (Select 1 Id, 1 AccountId, 'someemail' Email, 'somephone' Phone) t
Left Join YourTable t2 On t.AccountId = t2.AccountId
And (t.email = t2.email Or t.phone = t2.phone)
Where t2.AccountId Is Null
This should ensure no accounts get reinserted if they have a matching phone or email.
Insert Into YourTable (Id, Account_Id, Email, Phone)
Select a.id, a.Account_Id, a.Email, a.Phone
From (Select t.id, t.Account_Id, t.Email, t.Phone from t
group by account_id,email,phone )a;
Suggest to import the records into a temp table (t). Then only filter the records into another table (yourtable) ie remove the duplicate as you like.

PHP / MYSQL - how to structure a log database for 25,000 users

I'm dealing with about 25,000 users (employees) spread over 5 divisions in a company.
For all these users, there's an MS Excel spreadsheet in use at the moment. The spreadsheet consists of about 35 columns, logging the employees's daily activities.
Each row is 1 activity and there are on average about 3 activities per day (never ending, meaning the log just grows and grows).
MY QUESTION:
I would like to build a database (PHP/MYSQL) that holds the activity log for these users as opposed to the MS Excel files.
Should I have a table per user with the 35 columns... leading to a database with 25,000 tables?
Or should I store the activities to a 35-sized array, convert it to binary and store it in a blob and build such a table per year... leading to 1 table per year with 25,000 rows?
Employee
------------
employeeID
employee_name
Day
------------
dayID
day
Activity
-------------
activityID
activity_name
dayID
employeeID
This way you can see an activity for a day
You can see activities for an employee
Can see activity for an employee on a specific day
I would use a 35-column table, if you actually use many/most of those fields per activity.
CREATE TABLE users (
uid INT,
name VARCHAR(255),
...
);
CREATE TABLE activities (
uid INT, // references users.uid
type VARCHAR(32),
date DATE,
... // The 35 activity-related columns
);
And then I would partition on time. Perhaps per-year as you suggested (that would mean up to about 27.4 million rows per table), or per month (about 2.2 million rows per table) if search performance is important, and a per-year table is too big for good performance.
As a rule of thumb, you don't want to create a table for each user. You want to have 1 table, users, for that matter, where you'd store IDs, names etc that pertain only to a user model. Then have a separate table would document users' daily activities with a user_id column as a reference to the user's row in the users table.

Multi-class, interlinked school timetable as a MySQL database

I've looked around for a bit now at other suggestions relating to this, but nothing I've seen has quite suited my needs, so here goes!
What I have is a multi-class (Australian secondary school; Years 7-12), multi-day (Mon-Fri) school timetable. What I now want to build is a MySQL database with the following information to be deployed on an account driven website.
Subjects:
Running time (as "Period 1 on Wednesday", "Period 2 on Friday", etc. -- multiple values in this column) Instructor (linked to separate database of all teachers) -- This would additionally need to change (temporarily) if a teacher was sick and replaced; perhaps a "replacementinstructor" column to be ignorned when NULL. Location (different, but specifically allocated, rooms on different days) -- As above, change temporarily when room altered.Other obviousnesses: Course name ("Year 7 Health"), Unique ID (Something like "7.HEALTH", rather than just auto-incrementing INT.), etc.
Teachers:
First name, last name Courses they take Contact infoOther obviousnesses: Unique ID (Auto-incrementing INT), Username (fname.lname), Password for their account, etc.
Students:
First name, last name Courses they attend (stored as an individual list for each student) Year level / Form (Year 7, Year 11, etc.)Basic personal info (Home suburb, email, etc.)More obviousnesses: Unique ID (same setup as teachers), Username (same as teachers), password, etc.
Any insight as to how I might design such a data structure would be greatly appreciated, I'm more of a UI fanatic than a MySQL thinker ;-D
Thanks in advance.
I can think of the following tables to use in MySQL:
students
Student information
id (auto_increment)
firstname
lastname
username
password
student_id (I had a student ID but I can't remember if I was given this in yr 7 or yr 10)
year
email
contact_phone
street
suburb
state (ENUM - ACT,NSW,WA,SA,TAS,VIC,NT,QLD)
teachers
Teacher information
id (auto_increment)
firstname
lastname
title (Dr, Mrs, etc)
username
password
email
contact_phone
street
suburb
state (ENUM - ACT,NSW,WA,SA,TAS,VIC,NT,QLD)
subjects
All the different subjects
id (auto_increment)
subject
subject_code (eg 7.HEALTH)
year
locations
Various locations around the school
id (auto_increment)
location (eg BLOCK A, ROOM 2, MUSIC ROOM)
subject_teachers
What subjects each teacher teaches
id (auto_increment)
subject_id
teacher_id
subject_students
Which subjects each student takes
id (auto_increment)
subject_id
student_id
subject_timetable
Main Timetable
id (auto_increment)
subject_id
location_id
teacher_id
alt_teacher_id (eg substitute teacher)
duration
period (number 1-however many periods in a day. 6 if I remember correctly)
week (number 1-2 or even perhaps 1-52)
weekday (number 1-5)
notes (as #Oswald suggested you could add additional notes for when things change)
The notes could be collated and then displayed as footnotes on the timetable.
You will obviously need a table for Subjects, a table for Students and a table for Teachers.
Read up on database normalization. This will tell you (amongst other things):
Do not put the running time into the Subject table as a comma separated list. Instead use a table for the running time and use a foreign key to map entries from that table to Subjects table.
Same goes for mapping teacher to courses.
Same goes for mapping students to courses.
That Running Time table would also be suitable for holding the location of a course during a specific running time.
Additionally, if you record temporary changes in the database, you are dependent on people changing the relevant information at a specific time. To get around this, you might want to consider a table Lessons, where you record
Running time
Week number
Course
Instructor
Location
Other fields that might be specific to that particular lesson.
This will allow you to schedule changes in advance (Mr. McCachney is sick for the next two weeks, Room 101 is closed for redecoration for a month, etc.)

Categories