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

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.

Related

Speed-up/Optimise MySQL statement - finding a new row that hasn't been selected before

First a bit of background about the tables & DB.
I have a MySQL db with a few tables in:
films:
Contains all film/series info with netflixid as a unique primary key.
users:
Contains user info "ratingid" is a unique primary key
rating:
Contains ALL user rating info, netflixid and a unique primary key of a compound "netflixid-userid"
This statement works:
SELECT *
FROM films
WHERE
INSTR(countrylist, 'GB')
AND films.netflixid NOT IN (SELECT netflixid FROM rating WHERE rating.userid = 1)
LIMIT 1
but it takes longer and longer to retrieve a new film record that you haven't rated. (currently at 6.8 seconds for around 2400 user ratings on an 8000 row film table)
First I thought it was the INSTR(countrylist, 'GB'), so I split them out into their own tinyint columns - made no difference.
I have tried NOT EXISTS as well, but the times are similar.
Any thoughts/ideas on how to select a new "unrated" row from films quickly?
Thanks!
Try just joining?
SELECT *
FROM films
LEFT JOIN rating on rating.ratingid=CONCAT(films.netflixid,'-',1)
WHERE
INSTR(countrylist, 'GB')
AND rating.pk IS NULL
LIMIT 1
Or doing the equivalent NOT EXISTS.
I would recommend not exists:
select *
from films f
where
instr(countrylist, 'GB')
and not exists (
select 1 from rating r where r.userid = 1 and f.netflixid = r.netflixid
)
This should take advantage of the primary key index of the rating table, so the subquery executes quickly.
That said, the instr() function in the outer query also represents a bottleneck. The database cannot take advantage of an index here, because of the function call: basically it needs to apply the computation to the whole table before it is able to filter. To avoid this, you would probably need to review your design: that is, have a separate table to represent the relationship between movies and countries, which each tuple on a separate row; then, you could use another exists subquery to filter on the country.
The INSTR(countrylist, 'GB') could be changed on countrylist = 'GB' or countrylist LIKE '%GB%' if the countrylist contains more than the country.
Then don't select all '*' if you need only some columns details. Depends on the number of columns, the query could be really slow

(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}

What is the best way to store multiple selections from drop down list to database?

I have a multi select drop down list. I:
I'm just wondering which is the best way to store it into MySQL database:
1) serialise() method of PHP or 2) in specific format with different symbols like comma (,) vertical bar (|) and etc.
For example, I have cities table. It contains city_id and city_name.
One person may travel to more than one city. So he will select multiple cities. I want to store this selection somehow so that I can use them whenever I need by mysql joins.
I use Laravel 5.
You'll have 3 tables :
Cities (id, name)
Users (id, name)
City_user (id, id_city, id_user)
For example your user (1, Sakuto) has traveled to (1, Belgium) and (2, France), your table city_user will contain:
(1, 1, 1) (User 1 has traveled to city 1)
(2, 2, 1) (User 1 has traveled to city 2)
The proper way that will store your data in a normalized way is to have a separate table for the selected cities.
I don't know what entitity your selections are connected to, but say it's users.
users table
id int
name string
etc.
userCities table:
id int
userId int
cityId int
cities
id int
name string
So if a user selects three cities, he or she will have one row in the users table and three rows in the userCities. Doing this will enable you to create much better and faster queries than if you store the selections as a concatenated value in one column in the users table. To retrieve a user and its cities you do an outer join with userCities and cities (so that users without cities are also returned).

PHP + Mysql: dynamic table name from first query for execution of second query

I have three tables, the first is a table storing applications, the second is a table storing different online forms (different types of applications), the third is a table that stores actual form data:
TABLE applications=========
-applicationID (PK)
-formID (FK)
-formRecordID
====================
TABLE forms=========
-formID (PK)
-formName
-tableName (could be 'form_businessLicense','eventLicense',etc)
====================
TABLE form_businessLicense=====
-recordID (PK)
-dateSubmitted
-(a whole bunch of other data)
===============================
"formRecordID" points to "recordID" in "form_businessLicense" or "eventLicense". Since it could reference any table, it can't be a foreign key. So instead I grab the tableName from the "forms" table, then build a query to get all the application data from, say "form_businessLicense".
So I need to get data from, say, all applications plus a bit of data from the application form filled out (ex:form_businessLicense). I'm just going to paste my code (I'm actually querying all applications in a given set of IDs):
$applications = $this->selectAll(
"SELECT applicationID, formName, tableName, fieldIdentifier, formRecordID, dateSubmitted, DATE_FORMAT(dateSubmitted,'%c/%e/%Y') AS dateSubmittedFormat
FROM applications AS a
JOIN forms AS f
ON a.formID = f.formID
WHERE a.applicationID IN (".$applicationIDs.")
ORDER BY dateSubmitted ASC"
);
for($a=0;$a<count($applications);$a++){
$form = $this->select("SELECT ".$applications[$a]['fieldIdentifier']." AS identifierName
FROM ".$applications[$a]['tableName']."
WHERE recordID = ".$applications[$a]['formRecordID']
);
$applications[$a]['identifierName'] = $form['identifierName'];
}
Is there any way to merge these two queries into one so I don't have to loop over all results and run a separate query for each result? I feel like I could maybe do this with a JOIN but I'm not sure how to reference the "tableName" and "formRecordID" for use in the same SQL statement.
You need to apply join to three tables, and select count(PK) of third table while adding a group by clause for the PK of third table.
Note: PK used for Primary Key

Items sql table

I am building a textbased game, and I have problem of how to build/structur my SQL table for items.
Item can be anything from weapon, a fruit, armor, etc. But I'm not sure how to properly design this.
For example
Iron Sword Str +4 Health +3
(Or something like that)
But if its a fruit item
Fruit of Health (lol)
+5 health when eated
Any tips or ideas? The question is How do I structure this SQL table?
Store different types of object in different tables.
Give each table the proper columns for the respective kind of object it stores.
CREATE TABLE Weapons (WeaponId INT PRIMARY KEY, Name VARCHAR(20), Strength INT);
CREATE TABLE Foods (FoodId INT PRIMARY KEY, Name VARCHAR(20), HealthBonus INT);
If you want all types of objects to have some common attributes, like weight or purchase price, then you could create a general Items table that has those common attributes.
CREATE TABLE Items (ItemId INT AUTO_INCREMENT PRIMARY KEY,
Weight NUMERIC(9,2), Cost NUMERIC(9,2));
You'd make the WeaponId and FoodId primary keys from the other tables would each match one of the ItemId values in Items. When you want to create a new weapon, you'd first add a row to Items which would generate a new ItemId value, then use that value explicitly as you insert into Weapons.
See the Class Table Inheritance pattern.
Re your question below.
If you are querying for a specific weapon, you can join:
SELECT * FROM Items i JOIN Weapons w ON w.WeaponId = i.ItemId
WHERE w.Name = 'Iron Sword';
If you are query for all items in the character's backpack, you'd have to do multiple joins:
SELECT i.*, COALESCE(w.Name, f.Name, t.Name) AS Name,
CONCAT_WS('/',
IF (w.WeaponId, 'Weapon', NULL),
IF(f.FoodId, 'Food', NULL),
IF(t.TreasureId, 'Treasure', NULL)
) AS ItemType
FROM Items i
LEFT OUTER JOIN Weapons w ON w.WeaponId = i.ItemId
LEFT OUTER JOIN Foods f ON f.FoodId = i.ItemId
LEFT OUTER JOIN Treasure t ON t.TreasureId = i.ItemId
etc.;
If a given Item matches a Weapon but not a Food, then the columns in f.* will be null. Hopefully a given ItemId matches an Id used in only one of the specific subtype tables. On the other hand, it allows a given item to be both a weapon and a food (for instance, vegan cupcakes, which can be effective projectiles ;-).
Sounds like you need a table of attributes (strength, health, etc.). Then a table of the items (name, description, etc) and an association linking the two together (obviously linking by related id's rather than text for normalization, but this is just to demonstrate).
Item Attr Value
Iron Sword Str +4
Iron Sword Hlth +3
Fruit Hlth +5
Right answears given above.. They are different approaches... The second one requires good knwledge of OOP.
I want to mention an other thing, I suggest you read some tutorial on Entity Relational diagram-design. I gues for a game you will probably need to study a few basic things only so it will take you only some hours I guess.
There are many things to consier while designing... for example:
Entity = A thing that can logically stand on its own with its own attributes: Customer, Supplier, Student, Departement are some strong entities etc. Works for, belongs to etc are not entities but relations that associatin entities together.
An entity becomes a table. Strong entities (that have no dependencies) become tables with a simple primary key and usually without accepting any foreign keys. Phone number is not a strong-indepndent entity every time a customer is deleted the phone has no menaing, every time a phone is deleted hte customer has still meaning. Phone is an attribute but because of multiple values it becomes finally a table.
All these are not to tutor er design just to mention that db design in not something to take lightly, it can save you or give you big pain... depends on you.

Categories