correct way to place data in table OneToONe - mysql

I am confused about the correct/most efficient way to place data in my dababase table when there is a OneToOne relationship.
For example, I have a users table.
I now wish for each user to be able to state his current country location.
i then want to be able to search the datatable for users by current location.
The way that I have done this is to create 3 separate tables. i.e
table one - users : just contains the user information:
CREATE TABLE users(
id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
firstName VARCHAR(30) NOT NULL,
lastName VARCHAR(40) NOT NULL,
);
Table two country list: a list of countries and respective Ids for each country
PHP Code:
CREATE TABLE countrylist(
country_id MEDIUMINT UNSIGNED NOT NULL,
country VARCHAR(60) NOT NULL,
INDEX country_id ( country_id, country ),
INDEX countrylist (country, country_id ),
UNIQUE KEY (country)
);
Table 3; contains the userId and the countryId he lives in:
PHP Code:
CREATE TABLE user_countrylocation(
country_id VARCHAR(60) NOT NULL,
id MEDIUMINT UNSIGNED NOT NULL,
INDEX country_id (country_id, id ),
INDEX user_id (id, country_id )
);
Alternatively, should I place the countryId in the users table and completely get rid of the user_countrylocation. i.e in each user column, I will place a country_id for the country he lives in.
The problem is that I have over 20 similar tables as above that give details on users; i.e languages spoken, age-group, nationality etc.
My concerns is that if I place this unique information in each users column in the user table, then what would be the most efficient way to search the database: that is why I opted for the style above.
So, I really request for some advice on the most efficient/correct way to plan the database.

If you are going to have a huge data then you should keep the same approach and use the following method to keep the one to one constraint satisfied
if you don't have a huge data then you should keep the look up tables like country and use the reference for user in a column. but then you may need to allow them nulls that is make such optional information columns nullable.
The most efficient and exactly correct way is to first delete the data from the third table "user_countrylocation" for the user to be updated. Then insert the new location for the user. don't forget to use transaction.
your table 3 should have
country_id MEDIUMINT UNSIGNED NOT NULL,
instead of
country_id VARCHAR(60) NOT NULL,
and also change tyhe column name from id to user_id in all tables.
if you are using a stored procedure it would be like
create procedure sp_UpdateUserCurrentCountry (
#userID MEDIUMINT UNSIGNED,
#CountryID MEDIUMINT UNSIGNED)
begin
as
delete from user_countrylocation
where user_id = #userID
insert into user_countrylocation
(
country_id,
user_id
)
values
(
#CountryID,
#userID
)
END

One to One relations are usually mapped via Foreign Keys linking the two tables together. A third mapping table is only required for Many to Many relationships. So, you should ideally have a Foreign Key Country_ID in your Users table.
Your SELECT query would then look like
SELECT * FROM Users
WHERE Country_ID = (
SELECT Country_ID FROM Countries
WHERE Country_Name = 'USA'
);

Related

Saving records in MySQL which does not pre-exist

Let say, I have a pre-defined table called cities, with almost all the cities in my country.
When a user register himself (user table), the column cities_id in the table user stores the city id from the table cities (Foreign Key, reference table cities), something like
CREATE TABLE `cities` (
`id` int,
`city_name` varchar(100)
)
CREATE TABLE `user` (
`id` int,
`name` varchar(60)
`****`
`cities_id` FK
)
The user table stores the city id.
But what if I missed a few cities ... How does the user then save his city name in the user table which does not accept any city name but only IDs.
Can I have one more column city_name right after the cities_id in the table user something like
CREATE TABLE `user` (
`id` int,
`name` varchar(60)
`****`
`cities_id` FK
`citiy name` varchar(100)
)
to record the data entered by the user at the time of registration? Can this be done?
You can add a type to city table tag, the user can't find their corresponding to the city allows him to type the name of his city, and then you in the city, and will create a corresponding record in the table type marked as a special status (convenient operating personnel check and correction), at the same time to save the record id to the user record
CREATE TABLE `cities` (
`id` int,
`city_name` varchar(100),
`type` int,
)
CREATE TABLE `user` (
`id` int,
`name` varchar(60)
`****`
`cities_id` FK
)
As #Joakim mentioned in the comment, from a DB perspective, as cities_id is a foreign key referencing to the cities table, inserting a record to the user table will fail if the city in question is not already there in the table.
From a programming perspective, if you want a city which is not there in the table should be first inserted automatically whenever a user is registering, it is possible. Assuming you are using Java and Hibernate and User entity contains City entity, then calling saveOrUpdate() method on the user entity will cause the city record to be inserted if not already there, and a user record will then be inserted into the User table.
That's how I would quickly solve this
Create an additional table to store the missing cities, that will be introduced by users
CREATE TABLE `cities_users` (
`id` int,
`city_name` varchar(100),
`added_by` varchar(100),
`added_TS` DATETIME DEFAULT CURRENT_TIMESTAMP
);
Create a VIEW that UNION the 2 cities tables :
CREATE VIEW all_cities AS
SELECT id, city_name FROM `cities`
UNION ALL
SELECT id, city_name FROM `cities_users`;
Whenever a user register, you query the VIEW to check if the user's city exists. That way you'll kknow if a city exists in your original table OR the cities introduced by users.
If not, you INSERT the new city in the cities_users table (along with the user that created it for logging purposes).
You should generate a unique ID properly, ie one that can't ever exists in the cities table. You can do this in various ways, here's a quick example : Take the last ID in the cities_users table and add 1 million to it. Your cities_users IDs will be like: 1000001, 1000002, 1000003
And finally, you insert the generated cities_users ID in the users table.
Having a separate table for user inputs should help you to keep the database clean :
Your original cities table remains totally unchanged
You will know easily at all times the new cities added by whom and when, and you can create a small interface to review and manage that.
Your users are working for you to complete your database.
If a user suggest a new city you should create a new record into cities table and store city_id into users table. This is the best way to store the table records.
I feel like it should be pointed out, despite answers to the contrary, that your original suggestion of adding a city_name column to the table will work fairly well
If you allow both cities_id and city_name to be nullable then you can validate that one and only one of them is set in the application logic
The benefit of this approach is that it would keep your city table 'pure' and allow you to count duplicates of and analyse the user supplied cities easily
It would however add a very sparse nullable city_name column in your table
I guess it depends on how you want to get the city from the user, (drop-down + text box for others, text-box with suggestions, just a text box) and what you plan to do with the cities you have gathered
You could even change the label to 'city (or nearest city)' with a hard-coded drop-down, or searchable drop-down, and not allow user supplied cities
If you have a buffer table where the raw data is put in, i.e. the relationship between city_name, user_name
CREATE TABLE `buffer_city_user` (
`buffer_id` int,
`city_name` varchar(100),
`user_name` varchar(100),
);
you can first process the buffer table for new city_names - if found, insert into table cities.
Then insert the user info - any new city-names should already be in the cities table and no foreign key issues will occur.

How do I get a row of the same type from one table or another table along with the information about from which table it was

Let's say I have tables:
create table people (
human_id bigint auto_increment primary key,
birthday datetime );
create table students (
id bigint auto_increment primary key,
human_id bigint unique key not null,
group_id bigint not null );
create table teachers (
id bigint auto_increment primary key,
human_id bigint unique key not null,
academic_degree varchar(20) );
create table library_access (
access_id bigint auto_increment primary key,
human_id bigint not null,
accessed_on datetime );
Now I want to display information about a library access, along with the information whether it was a student or a teacher (and then the id corresponding to the table) (let's say I want something like SELECT access_id,id,true_if_student_false_if_teacher FROM library_access), in an idiomatic way.
How do I form the query (in case such database was already deployed) and what are better and more idiomatic ways to solve that problem (in case it wasn't deployed so far).
MariaDB 5.5, database accessed by Go and nothing else.
Thanks in advance.
You said you need to know which table the data comes from. You can use union all for this:
select la.access_id, s.id, 'Students' as source_table
from library_access la
join students s on la.human_id = s.human_id
union all
select la.access_id, t.id, 'Teachers' as source_table
from library_access la
join teachers t on la.human_id = t.human_id
Without looking at your tables or any idea as to what you want returned in the select statement:
SELECT *
FROM people a,
students b,
teachers c,
library_access d
WHERE a.human_id = b.human_id
AND a.human_id = c.human_id
AND a.human_id = d.human_id

MySQL table linking 1:n

So first up I'm not sure if this is a double post or not because I don't know how the exact approach or feature is called and if it even exist.
I know that MySQL has a feature called joins
My plan is to link two MySQL tables in relation 1:n one is t_user the other one t_event.
t_user:
CREATE TABLE t_user (
uId INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(25) NOT NULL,
...
)
t_event:
CREATE TABLE t_event (
eId INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(40) NOT NULL,
date DATETIME NOT NULL,
members ???,
...
)
I want the users to "subscribe" to the events and get stored in the members column as a list (?). This would be no problem if only one user would subscribe to one event. But I have no idea how to setup the t_event table to store more than one user and how to query for all the events a user has "subscribed" for.
This is usually done via third table:
CREATE TABLE t_eventsusers (
eId INT(6),
uId INT(6)
)

Best way with relation tables

I have a question about tables and relations tables ...
Actually, I have these 3 tables
CREATE TABLE USER (
ID int(11) NOT NULL AUTO_INCREMENT,
NAME varchar(14) DEFAULT NULL
);
CREATE TABLE COUNTRY (
ID int(11) NOT NULL AUTO_INCREMENT,
COUNTRY_NAME varchar(14) DEFAULT NULL
);
CREATE TABLE USER_COUNTRY_REL (
ID int(11) NOT NULL AUTO_INCREMENT,
ID_USER int(11) NOT NULL,
ID_COUNTRY int(11) NOT NULL,
);
Ok, so now, 1 user can have one or more country, so, several entries in the table USER_COUNTRY_REL for ONE user.
But, my table USER contains almost 130.000 entries ...
Even for 1 country by user, it's almost 10Mo for the USER_COUNTRY_REL table.
And I have several related tables in this style ...
My question is, is it the fastest, better way to do?
This would not be better to put directly in the USER table, COUNTRY field that contains the different ID (like this: "2, 6, ...")?
Thanks guys ;)
The way you have it is the most optimal as far as time constraints go. Sure, it takes up more space, but that's part of space-time tradeoff - If you want to be faster, you use more space; if you want to use less space, it will run slower (on average).
Also, think of the future. Right now, you're probably selecting the countries for each user, but just wait. Thanks to the magic of scope creep, your application will one day need to select all the users in a given country, at which point scanning each user's "COUNTRY" field to find matches will be incredibly slow, as opposed to just going backwards through the USER_COUNTRY_REL table like you could do now.
In general, for a 1-to-1 or 1-to-many correlation, you can link by foreign key. For a many-to-many correlation, you want to have a relation table in between the two. This scenario is a many-to-many relationship, as each user has multiple countries, and each country has multiple users.
Why not try like this: Create table country first
CREATE TABLE COUNTRY (
CID int(11) NOT NULL AUTO_INCREMENT,
COUNTRY_NAME varchar(14) DEFAULT NULL
);
Then the table user:
CREATE TABLE USER (
ID int(11) NOT NULL AUTO_INCREMENT,
NAME varchar(14) DEFAULT NULL,
CID Foreign Key References CID inCountry
);
just Create a Foreign Key relation between them.
If you try to put this as explicit relation , there will lot of redundancy data.
This is the better approach. You can also make that Foreign Key as index . So that the databse retrieval becomes fast during search operations.
hope this helps..
Note : Not sure about the exact syntax of the foreign key

Database Design for a status update system involving companies and users

I am having an issue that involves a differentiation between whether a status update belongs to a user or a company. Let me explain: Individuals can post statuses as a user or as a company. A user can be a member/owner of a company and switch to a company in the dashboard much like Facebook. The problem is: how will we set in the database whether a status was posted by a company or by a user?
My solution was to have a company column that was a Boolean variable and when we query for each status to display we check if it was from a company. If so then we grab the company_id and look up the name and other relevant information in the database and display it on the site. Does this sound like the right approach? Additionally, does the schema below look correct?
**Company**
id int(11)
name varchar (255)
**Company_members**
company_id int(11) FK_Company
user_id int(11) FK_Users
owner BOOLEAN
**Users**
id int(11)
name varchar (255)
**Status**
id
date DATETIME
user_id FK_Users
company_id FK_Company
company BOOLEAN
Your schema looks good, but the company boolean in the Status table seems redundant. You can just set the company_id to NULL when the status is for an individual's account, and then query SELECT * from Status WHERE company_id IS NULL to get individuals' statuses, and SELECT * FROM Status WHERE company_id IS NOT NULL to get companies' statuses.
Use the Party Model. You need to use table inheritance. Single Table inheritance is simpler and faster but may use nulls.
create table party_type (
id int primary key,
description text not null unique
);
insert into party_type values
(1, 'Individual'),
(2, 'Organization');
create table party (
id serial primary key,
type int not null references party_type(id),
name text not null
);
create table status_update(
id serial primary key,
date datetime,
party_id not null references party(id)
);
(syntax is postgres but you should be able to translate to mysql easily)