A record self referencing in mysql - mysql

I've got a self referencing table in mySql, a table of managers and employees.
CREATE TABLE `employee` (
`employee_id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`firstname` VARCHAR(50) NULL DEFAULT NULL,
`lastname` VARCHAR(50) NULL DEFAULT NULL,
`manager_id` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`employee_id`),
CONSTRAINT `FK_MANAGER` FOREIGN KEY (`manager_id`) REFERENCES `employee` (`employee_id`)
)
I want to know if it's conceptual correct that a record references himself. I explain better: some employees has relationships with some manager, so for example employees 3,5,7 have relationships with manager 1, but I'd like to create a list to show all record related to manager 1 and also manager 1. So I've tried to make manager 1 have relationships with himself, so manager 1 refers to manager 1.
I've got no errors, but wanna know if this is a correct way to implement this relationship or if I will have problems in future.
Thanks

In my opinion it is not best solution because:
It limits You to have only 2 levels in management hierarchy(only manager and employee) You can't have manager -> leader -> employee
You can't easily print a tree of employees
I practice in bigger company the only person with no one on top would be CEO
Myself I would use this query instead
SELECT * FROM employee WHERE employee_id=1
UNION
SELECT * FROM employee WHERE manager_id=1

Related

How to insert data into a table with a foreign key

I'm currently having issues with inserting values into a database table that uses a foreign key from another table to align the is together. The tables are pretty simple. One holds information about a project, and the other hold values for the project images. Here they are in detail.
The projects table
project_id int(50) PRIMARY KEY NOT NULL AUTO_INCREMENT,
project_name varchar(50) NOT NULL,
project_permitted timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT "The date that the project took place.",
project_in varchar(50) NOT NULL COMMENT 'The place where the project took place (ie the city and state).',
project_type varchar(50) NOT NULL COMMENT 'The project type (ie residentual, commercial, etc).',
project_description longtext,
project_published timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
Here is the second table called project_images
image_id int(50) PRIMARY KEY NOT NULL AUTO_INCREMENT,
project_id int(50),
image_url varchar(50) NOT NULL,
CONSTRAINT fk_projects FOREIGN KEY (project_id) REFERENCES projects(project_id)
What I am trying to do is insert values into the second table using the project_id from the projects table using a subquery. That query looks like this:
insert into project_images (project_id, project_url, project_description)
values (
(select project_id from projects where project_name = 'The Venue'),
"images/theVenue.png",
"The Venue: an appartment complex in Austin, Texas."
)
With this query I keep getting an error that says
something to the effect of "You are missing a comma or closing bracket
near project_id.
Can anyone help or point out the best way to handle this situation.
Modify your query to be like
insert into project_images (project_id, project_url, project_description)
select project_id ,
"images/theVenue.png",
"The Venue: an appartment complex in Austin, Texas."
from projects where project_name = 'The Venue';
After looking into this question a bit more, it seems that you cannot use a subquery the way I am using it to get the value of a column, However, the column can be inserted directly so long as the foreign key points to a primary key from another table that has already be inserted. The whole point to using this query was for a PHP project, so I guess I'll just do a select query in a project to get its ID then add that to the sql that queries the project_images table. This seems to be the only way to do that.

Mysql select max with threshold

I have the following 3 tables as part of a car booking system:
CREATE TABLE `b_booking` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`b_car_id` VARCHAR(15) NOT NULL,
`uc_user_id` INT(11) NOT NULL,
`booking_date` DATE NOT NULL,
`delivery_date` DATE NOT NULL,
`delivery_location` INT(10) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `FK_b_booking_b_car` (`b_car_id`),
INDEX `FK_b_booking_uc_users` (`uc_user_id`),
INDEX `FK_b_booking_b_location` (`delivery_location`),
CONSTRAINT `FK_b_booking_b_location` FOREIGN KEY (`delivery_location`) REFERENCES `b_location` (`id`),
CONSTRAINT `FK_b_booking_b_car` FOREIGN KEY (`b_car_id`) REFERENCES `b_car` (`id`),
CONSTRAINT `FK_b_booking_uc_users` FOREIGN KEY (`uc_user_id`) REFERENCES `uc_users` (`id`)
CREATE TABLE `b_car` (
`id` VARCHAR(15) NOT NULL,
`b_carmodel_id` INT(10) NOT NULL,
`day_cost` INT(10) NOT NULL,
`location` INT(10) NOT NULL,
`model_year` SMALLINT(4) NOT NULL,
PRIMARY KEY (`id`),
INDEX `FK_b_car_b_carmodel` (`b_carmodel_id`),
INDEX `FK_b_car_b_location` (`location`),
CONSTRAINT `FK_b_car_b_location` FOREIGN KEY (`location`) REFERENCES `b_location` (`id`),
CONSTRAINT `FK_b_car_b_carmodel` FOREIGN KEY (`b_carmodel_id`) REFERENCES `b_carmodel` (`id`)
CREATE TABLE `b_location` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`adress` VARCHAR(100) NOT NULL,
`b_postal_zip` SMALLINT(4) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
INDEX `FK_b_location_b_postal` (`b_postal_zip`),
CONSTRAINT `FK_b_location_b_postal` FOREIGN KEY (`b_postal_zip`) REFERENCES `b_postal` (`zip`)
A given car (b_car) will have an int representing av location where a car can be picked up or delivered. However, this location will change, due to the fact that a customer can pick up the car on one location, and deliver at another. When a customer registers a booking (b_booking), he/she also register at what location the car will be delivered (delivery_location).
I'm however having a lot of problems with what seems to me shouldn't be that hard to do: Lets say a car is at location 1 at the beginning of the month. Then, customer X register a booking for 15-20. and registers a delivery at location 2. Then customer Y wishes to book the same car. So I need a query which takes into account the date and location.
SELECT c.id, c.location, b.delivery_location, MAX(b.delivery_date) FROM b_car c
LEFT JOIN b_booking b ON b.b_car_id = c.id
WHERE b.delivery_date < '2012-11-28' OR b.delivery_date IS NULL
GROUP BY c.id;
I have tried something similar to this. I will pick all cars which has no bookings with the left join, and it will also pick the last booking (the last booking related to given date). The problem is, of course, that the where condition also excludes any booking with a date higher than the provided date. Also, with this solution I'm forced to get both the location (from b_car) and the delivery location (from b_booking), and perhaps evaluate null on the delivery_location with php or something.. which really doesnt seem optimal.
Any good solutions?
Thanks
I am not sure what your question is. I think your question is to find out which cars are available based on the planning in the booking system on a given day. In that case the only info you need is the ids from the cars and the location and according to the booking system.
BTW I would suggest to add time to your table, because you may want to rent out a car the same day. If customer A returns the car before 12:00 you can easily rent out the same car at 13:00 to customer B.
SELECT c.id, b.delivery_location, b.delivery_date FROM b_car AS c
LEFT JOIN b_booking AS b ON b.id = c.id
WHERE b.delivery_date = '2012-11-28' and b.delivery_location = 1;
I simplified you query, because I think this will give you the information you need. If a customer wants to rent a car you need to know if the car is available in the planning at the specified location.
If you want to know the availability between to dates use WHERE b.delivery_date between [start date] AND [end date].
I suspect that the type of car is important for the customer, so perhaps it is better to look for a specific car type available at the location of choice.
Furthermore, I would suggest checking if the car is actual at the desired location at the delivery time.

Correct database? Does it need altering?

I'm creating a page where I want users to be able to book a seat for an event.
1 user can only book 1 seat
users have no seat selected upon login, first after buying into a spot
Need to able to clear seats table, without loosing anything from user-table (except of course the assigned seats.)
I've created two tables, and since I'm pretty new to mySQL, I wanted to check if this was done correctly:
members-table:
user_id int(8) Not null auto_increment
user_name varchar(30) Not null
user_pass varchar(255) Not null
seat_ID smallint(6) Yes NULL
seats-table
seat_ID smallint(6) No auto_increment
user_id smallint(6) Yes NULL
seat_status tinyint(4) Yes NULL
seat_status tinyint(4) Yes NULL
I've created 2 FK-refs:
ALTER TABLE seats
ADD CONSTRAINT FK_seats
FOREIGN KEY (user_id) REFERENCES members(user_id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE seats
ADD CONSTRAINT FK_seats
FOREIGN KEY (seat_ID) REFERENCES members(seat_ID)
ON UPDATE CASCADE
ON DELETE CASCADE;
Am I on the right track? Will I be able to progress to a decent final product with this setup? suggestions/improvements? I don't want to start all over in a couple of weeks because the database structure is of poor quality.
First of all I don't see why you're using a second table if any user can only hold one seat at any given time, secondly user_id in seats-table should be the same size as user_id in members table namely int(8), otherwise you won't be able to seat users after a while, third issue is the duplication of seat_status, I suppose that was a mistake or you had another name for it.
In my opinion a better idea is to use a single table if it's a 1->1 mapping and define it as
CREATE TABLE `members-table` (
user_id int(8) not null auto_increment,
user_name varchar(30) not null,
user_pass varchar(255) not null,
seat -- your type choice, should be nullable if not seated
);
Clearing the seats with this config would be as simple as
UPDATE `members-table` SET `seat` = NULL;
CREATE TABLE `seats` (
id int(4) unsigned not null auto_increment primary key,
row int(2) unsigned not null,
col int(2) unsigned not null,
UNIQUE(row, col)
) ENGINE InnoDB;
CREATE TABLE `members` (
user_id int(8) not null auto_increment primary key,
user_name varchar(30) not null,
user_pass varchar(255) not null,
seat int(4) unsigned null,
FOREIGN KEY(seat) references seats(id) on delete set null on update restrict,
UNIQUE(seat)
) ENGINE InnoDB;
You will have to populate the seats database with all available rows and columns, use null on id when inserting to use the auto_increment feature!
Check if a seat is taken
SELECT COUNT(*) AS occupied FROM members WHERE seat = (SELECT id FROM seats WHERE row = :ROW AND col = :COL);
Alternatively use SELECT (1 - COUNT(*)) AS vacant in the query above if it's more conveninent for you.
Find first free seat
SELECT MIN(id) FROM seats WHERE NOT EXISTS( SELECT seat FROM members WHERE seat = seats.id);
Unassign all taken seats
UPDATE members SET seat = NULL;

In MySQL, how to "join" three tables

I've seen a good amount of threads on "how to join more than two tables" but none of those threads seem to solve my problem.
I have three tables:
teams, persons and payfiles
teams Table looks like this:
DROP TABLE IF EXISTS `teams`;
CREATE TABLE IF NOT EXISTS `teams` (
`team_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`leader` varchar(32) NOT NULL,
PRIMARY KEY (`team_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=30;`
persons table:
DROP TABLE IF EXISTS `persons`;
CREATE TABLE IF NOT EXISTS `persons` (
`team_id` int(2) DEFAULT '0',
`hash` varchar(32) NOT NULL,
UNIQUE KEY `hash` (`hash`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
payfiles table:
DROP TABLE IF EXISTS `payfiles`;
CREATE TABLE IF NOT EXISTS `payfiles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`hash` varchar(32) NOT NULL,
`deals_passed` int(2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1298 ;
Tables have much more columns, but I trimmed them for the sake of simplicity.
teams table contains records in the following way:
1,Team 1,afaf7878af78a
The latter is the team leader's unique hash.
The table persons contains all the personal information, and a unique hash,
For instance
John Doe whose hash is afaf7878af78a who also is the leader of Team 1.
The table payfile also has a "hash" column, that table contains all the information needed to compute employees' checks.
The management team want a general vision of how much the company is making. They want to see how much money every single team is bringing in.
The problem I'm facing here, is trying to group the earnings by "teams"
My best try so far is this
SELECT hash, SUM(deals_passed) as deals FROM payfiles JOIN persons ON persons.hash = payfiles.hash GROUP BY payfiles.hash
but I can't see an optimized way to query the database in order to generate a team by team general vision of earnings without changing the structure of the database.
For instance:
John Doe, and Jane Doe belong to "Team 1" and they brought in $500 and $600 respectively.
I want to generate something like:
"Team 1 brought in $1100"
My alternative is to change the structure of the database, and add a new column to the payfile table. Such column would be team_id so I can query it easily, but the database currently has about 10,000 records so that implies updating the 10K records that didn't consider a team_id column, and make a lot of changes to the GUI, something that I don't really want to do, although if that's the easiest and best option I'll do it.
Thanks!
SELECT
teams.name,
SUM(payfiles.deals_passed) AS team_deals_passed
FROM payfiles
LEFT JOIN persons USING (hash)
LEFT JOIN teams USING (team_id)
GROUP BY teams.team_id
You can use SUM() to the get the total, and use GROUP BY for the team to get each total by team.

Mysql table architecture suggestion needed

Existing system
- I have existing Users and Tutor_Details tables in my system.
- There are two types of users - tutors and students. Users and Tutor_Details tables are linked by id_user foreign key.
New requirement
- Every tutor can have some of the following credentials:-
Certified
Experienced
Other
A tutor can have a maximum of 3 and minimum 1 credential for now. For every credential specified, the tutor can add some description too.
Right now there are 3 credentials but later there may be more.
What would be the best way to store the credential info. Tutors may be searched by credentials. While viewing a tutor details, all his credentials should be displayed.
I was thinking about the following structure:-
A new Credentials table like-
CREATE TABLE IF NOT EXISTS `Credentials` (
`id_credential` int(10) unsigned NOT NULL auto_increment,
`credential` varchar(255) default NULL,
PRIMARY KEY (`id_credential`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
INSERT INTO `Credentials` (`id_credential`, `credential`) VALUES
(1, 'Certified'),
(2, 'Experienced'),
(3, 'Recent Student'),
(4, 'Other');
If new credentials are added later, they are defined here.
And one new Tutor_credential_map table which will contain one record for every credential of a tutor
CREATE TABLE IF NOT EXISTS `Tutor_Credential_map` (
`id` int(10) NOT NULL auto_increment,
`id_tutor` int(10) NOT NULL,
`id_credential` int(10) NOT NULL,
`description` varchar(255) NOT NULL default '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
That makes things easy for maintenance point of view, but if I want to fetch all the Tutor info as stored in Tutor_Details table along with all his credentials in a single query I get as many result sets for a tutor as there are credentials. This query:-
select td.id_tutor, tcrm.* from Tutor_Details as td inner join Users as u on td.id_user = u.id_user join Tutor_Credential_map as tcrm on td.id_tutor = tcrm.id_tutor join Credentials as cr on tcrm.id_credential = cr.id_credential where td.id_tutor = 23
Any idea to keep the separate tables as well as fetch a single tutor details record for each tutor with all the credentials info? Or is there a better way?
Thanks
It's the nature of the SQL JOIN that multiple rows will be returned for each credential matches.
The easiest way to handle this would be by processing outside of MySQL using whatever lanuguage/system you are using to run the query in the first place.
As far as I can tell, your structure is just fine!
You said "There are two types of users - tutors and students. Users and Tutor_Details tables are linked by id_user foreign key".
Can there be multiple tutors per user, i believe not. Also can a user be a Tutor and a student.
If in both the above cases the answer is "no" i suggest "User_Credential_map" instead of "Tutor_Credential_map". id_tutor can be id_user.
CREATE TABLE IF NOT EXISTS `User_Credential_map` (
`id` int(10) NOT NULL auto_increment,
`id_user` int(10) NOT NULL,
`id_credential` int(10) NOT NULL,
`description` varchar(255) NOT NULL default '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
Now to answer your question based on the above obesrvations.
SELECT
Users.*,
Tutor_Details.* ,
User_Credential_map.* ,
Credentials.*
FROM
Users,
Tutor_Details,
User_Credential_map,
Credentials
WHERE
Tutor_Details.id_tutor = 23
AND Tutor_Details.id_user = Users.id_user
AND Tutor_Details.id_credential = Credentials.id_credential