Can I do it with one query? - mysql

SELECT `invites`.`id`, `invites`.`from`, `invites`.`to`, `invites`.`group_id`
FROM `invites`
WHERE `invites`.`to` = '33'
It gets ID of user that sent invite and ID of user that is invited. And, of course, ID of group they invited each other. I need to display usernames of them, but they are stored in another table. The same goes for groups. Info about them is stored in another table.
I could make new queries to get that info from IDs, but is it worth it? Can I do it with only one query?
Edit:
Structure:
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`surname` varchar(50) NOT NULL,
PRIMARY KEY (`id`),
);
CREATE TABLE `invites` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`group_id` int(10) unsigned NOT NULL,
`from` int(10) unsigned NOT NULL,
`to` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `groups` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);

A join is always worst it:
SELECT `invites`.`id`,
`invites`.`from`,
`invites`.`to`,
`invites`.`group_id`,
`toUsers`.`name` as toUserName,
`fromUsers`.`name` as fromUserName,
`groups`.`name` as groupName
FROM `invites`
INNER JOIN `users` AS toUsers
ON `invites`.`to` = toUsers.id
INNER JOIN `users` AS fromUsers
ON `invites`.`from` = fromUsers.id
INNER JOIN `groups`
ON `invites`.`group_id` = `groups`.`id`
WHERE `invites`.`to` = '33'
The only thing you have to keep in mind is to have foreign keys on from, to and group_id, and have primary keys on Id in tables Users and Groups. Then your query will be as fast as you need.
So I advise to run this :
ALTER TABLE `invites`
ADD CONSTRAINT FK_invites_to FOREIGN KEY (`to`) REFERENCES users(id),
ADD CONSTRAINT FK_invites_from FOREIGN KEY (`from`) REFERENCES users(id),
ADD CONSTRAINT FK_invites_group FOREIGN KEY (`group_id`) REFERENCES groups(id)
See some doc : http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html

You can use a JOIN:
SELECT invites.id, invites.from, invites.to, invites.group_id, group.name, u1.name, u2.name
FROM invites, user as u1, user as u2, group
WHERE invites.from = u1.id
AND invites.to = u2.id
AND group.id = invites.group_id
AND invites.to = '33'
Assuming your tables are called user and group and your fields name. You may have to put the backticks back on the field names because FROM is a reserved keyword.

I'd use a join too, but I'd create a view first that joins invites to users, then join two of those together - it means not having to repeat the join between invite and user.

Related

MySql SELECT with Joins and conditions statement

I have three tables
TABLE `courses` (
id int NOT NULL UNIQUE AUTO_INCREMENT,
title varchar(50) NOT NULL UNIQUE,
duration int NOT NULL,
theme varchar(50) NOT NULL,
students_quantity int NOT NULL,
PRIMARY KEY (id)
);
TABLE `users` (
id int NOT NULL UNIQUE AUTO_INCREMENT,
name varchar(50) NOT NULL,
email varchar(50) NOT NULL UNIQUE,
password varchar(50) NOT NULL,
status varchar(20) NOT NULL,
role_id int NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (role_id) REFERENCES `roles` (id)
);
TABLE `teachers_courses` (
teacher_id int NOT NULL,
course_id int NOT NULL,
PRIMARY KEY (teacher_id, course_id),
FOREIGN KEY (teacher_id) REFERENCES `users` (id),
FOREIGN KEY (course_id) REFERENCES `courses` (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
How can I get get courses.* and users.name AS teacher for this course, and if I have not course_id and teacher_id for this course in teachers_courses I'll get 'none' in teacher?
Use JOIN to combine your data following primary key - foreign key path.
Function coalesce() would return the second argument, if the first one evaluates to NULL.
select c.*, coalesce(u.name, 'none') as teacher
from courses c
left join teachers_courses tc on c.id = tc.course_id
left join users u on tc.teacher_id = u.id
order by c.id
Since there can be multiple teachers for each course, the only case in which you would get 'none' as teacher value would be if there is no teacher assigned for a course (not even one). If there is more than one teacher, there will be as many rows in the output as there are teachers for each course, thus I included ORDER BY to sort the result properly.
If you need to view the data for only one course, include a WHERE condition like this:
-- ... Above SQL here ...
WHERE c.id = ?

mysql view users vs admin users syntax inquiry

Setting up JDBC security realm with Glassfish and I came across this link that provides user control access (admin vs view users). here is the link for this tutorial.
http://jugojava.blogspot.com.eg/2011/02/jdbc-security-realm-with-glassfish-and.html?showComment=1459000091065#c232448315667617784
Few things I didn't understand in the guide and I hope if you can help. What do the lines below mean? (full sql code at the end of of the question)
KEY `fk_users_has_groups_groups1` (`group_id`),
KEY `fk_users_has_groups_users` (`user_id`),
I don't see any table named u but the code is using u.*. The code at the very bottom doesn't have "u" table in it. However, I see the reference to "u" in the inner Join. Can I do select u. based on this INNER JOIN?
CREATE VIEW `v_user_role` AS
SELECT u.username, u.password, g.group_name
FROM `user_groups` ug
INNER JOIN `users` u ON u.user_id = ug.user_id
INNER JOIN `groups` g ON g.group_id = ug.group_id;
Here is the full Code
CREATE TABLE `groups` (
`group_id` int(10) NOT NULL,
`group_name` varchar(20) NOT NULL,
`group_desc` varchar(200) DEFAULT NULL,
PRIMARY KEY (`group_id`)
);
CREATE TABLE `users` (
`user_id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(10) NOT NULL,
`first_name` varchar(20) DEFAULT NULL,
`middle_name` varchar(20) DEFAULT NULL,
`last_name` varchar(20) DEFAULT NULL,
`password` char(32) NOT NULL,
PRIMARY KEY (`user_id`)
);
CREATE TABLE `user_groups` (
`user_id` int(10) NOT NULL,
`group_id` int(10) NOT NULL,
PRIMARY KEY (`user_id`,`group_id`),
KEY `fk_users_has_groups_groups1` (`group_id`),
KEY `fk_users_has_groups_users` (`user_id`),
CONSTRAINT `fk_groups` FOREIGN KEY (`group_id`) REFERENCES `groups` (`group_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
);
CREATE VIEW `v_user_role` AS
SELECT u.username, u.password, g.group_name
FROM `user_groups` ug
INNER JOIN `users` u ON u.user_id = ug.user_id
INNER JOIN `groups` g ON g.group_id = ug.group_id;
INSERT INTO `groups`(`group_id`,`group_name`,`group_desc`) VALUES
(1,'USER','Regular users'),
(2,'ADMIN','Administration users');
INSERT INTO `users`(`user_id`,`username`,`first_name`,`middle_name`,`last_name`,`password`) VALUES
(1,'john','John',NULL,'Doe','6e0b7076126a29d5dfcbd54835387b7b'), /*john123*/
(2,'admin',NULL,NULL,NULL,'21232f297a57a5a743894a0e4a801fc3'); /*admin*/
INSERT INTO `user_groups`(`user_id`,`group_id`) VALUES (1,1),(2,1),(2,2);
1) KEY indicates that those fields are indexed. The index is not unique. See create index documentation.
2) The "u" is an alias, specifically it is a table alias. You can use aliases to refer to a table or field name under a different name. An alias is specific to a query. The alias is defined in the from clause:
`users` u

MySQL JOIN to get multiple entries from same table

I have a table of users that has a user id and multiple vehicles assigned to that user. Each vehicle is an integer value that is equal to a vehicle id in a separate table. I would like to retrieve the user id and the make and model of each of the vehicles associated with that user. I can get the user id and the make and model of a single vehicle with the following query:
SELECT users.user_id, vehicles.make, vehicles.model
FROM users
JOIN vehicles ON users.vehicle1 = vehicles.vehicle_id
I have tried multiple joins, etc.. but I have can not get it to work.
Any help would be greatly appreciated.
Edited to include table structures:
I tried to include these as images but I don't have enough posts yet.
Users Table
https://www.dropbox.com/s/tbn6e0omn96m41k/users.JPG?dl=0
Vehicles Table
https://www.dropbox.com/s/8dpr22c9llbzrf2/vehicles.JPG?dl=0
I need to get the make and model of ALL vehicles associated with the user.
#vthokie11 -- Drew has a good point. Your table structure is not good.
You can refer here
I would have had a structure in the following way:
1. user table:
DROP TABLE IF EXISTS `stacktest`.`user`;
CREATE TABLE `stacktest`.`user` (
`user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
2.vehicle table:
DROP TABLE IF EXISTS `stacktest`.`vehicle`;
CREATE TABLE `stacktest`.`vehicle` (
`vehicle_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`make` varchar(45) DEFAULT NULL,
`model` varchar(45) DEFAULT NULL,
PRIMARY KEY (`vehicle_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
3.user_vehicle table: (which maintains the relationship between user and vehicles)
DROP TABLE IF EXISTS `stacktest`.`user_vehicle`;
CREATE TABLE `stacktest`.`user_vehicle` (
`user_id` int(10) unsigned NOT NULL,
`vehicle_id` int(10) unsigned NOT NULL,
KEY `FK_user_vehicle_1` (`user_id`),
KEY `FK_user_vehicle_2` (`vehicle_id`),
CONSTRAINT `FK_user_vehicle_2` FOREIGN KEY (`vehicle_id`) REFERENCES `vehicle` (`vehicle_id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK_user_vehicle_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
And now you need to execute this simple query:
SELECT user.user_id, vehicle.make, vehicle.model
FROM user LEFT JOIN user_vehicle on user_vehicle.user_id=user.user_id
LEFT JOIN vehicle on vehicle.vehicle_id=user_vehicle.vehicle_id where user.user_id=1;
SELECT users.user_id, vehicles.make, vehicles.model
FROM users, vehicles
WHERE users.vehicle1 = vehicles.vehicle_id
Note: It would be better practice to name the column in the user table vehicle_id so it is clear that it is an id in another table and make it a foreign key.

What is the best way to query data between two tables which is not directly related

What is the best way to query data between two tables which is not directly related, but through third or even fourth table, that's related using foreign keys?
Something like 1 -> 2 -> 3 -> 4.
I saw many very different suggestions, but would like to find out what could be the best way in such situation, where I try to maintain relational database model but also avoid super complicated SQL queries.
Here is example that matches my case:
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`city_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_city_id` FOREIGN KEY (`city_id`)
REFERENCES `city` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
CREATE TABLE `city` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`country_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_country_id` FOREIGN KEY (`country_id`)
REFERENCES `country` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
CREATE TABLE `country` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`continent_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_continent_id` FOREIGN KEY (`continent_id`)
REFERENCES `continent` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
CREATE TABLE `continent` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
Lets say I want to get all users from specific Continent
SELECT name FROM user WHERE city_id IN (
SELECT id FROM city WHERE country_id IN (
SELECT id FROM country WHERE continent_id = 1 ) ) );
It looks fine, I guess, but what if I want to get all users from specific City
SELECT name FROM user WHERE city_id IN (
SELECT id FROM city WHERE country_id IN (
SELECT id FROM country WHERE continent_id = (
SELECT continent_id FROM country WHERE id = (
SELECT country_id FROM city WHERE id = 1 ) ) ) );
Is this efficient?
Maybe it's possible to achieve the same with JOIN?
I tried to avoid adding all values to user table, as to maintain that relation city->country->continent and involve relations to do the job, but maybe in this case it's just not worth doing so? ..maybe not efficient, and it's better to redesign database?
Should be something like this and you may want to analyse (with explain why an JOIN is better vs subquery) EXPLAIN manual page http://dev.mysql.com/doc/refman/5.7/en/explain.html
Please note that you can only trust INNER JOIN with filters like below with LEFT JOIN or RIGHT JOIN you can get wrong results..
For all users within an continent with continent_id = 1
SELECT
user.name
FROM
user
INNER JOIN
city
ON
user.city_id = city.id
INNER JOIN
country
ON
city.country_id = country.id
AND
country.continent_id = 1
;
For all users within an city with city_id = 1
SELECT
user.name
FROM
user
INNER JOIN
city
ON
user.city_id = city.id
AND
city.id = 1
see http://sqlfiddle.com/#!2/1c40e/23
Inner joins
-- get users to cities
from
City c
inner join Users u
on c.Id = u.City_Id
-- users to continents
from
Users u
inner join City ci
on u.City_id = ci.Id
inner join Country co
on ci.Country_Id = co.Id
inner join Continent con
on co.Continent_Id = con.Id

How can I select the current holder for each championship?

I want to select the current holders for each championship in a championships table, and return NULL for championships that have not had any winners yet.
Here are the create statements for the two tables:
CREATE TABLE `championships` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`friendly_name` varchar(255) NOT NULL,
`rank` int(2) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `friendly_name` (`friendly_name`)
) ENGINE=InnoDB;
CREATE TABLE `title_history` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`championship` int(10) unsigned NOT NULL,
`winner` varchar(255) NOT NULL,
`date_from` date NOT NULL,
`location` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `championship` (`championship`)
) ENGINE=InnoDB;
ALTER TABLE `title_history` ADD CONSTRAINT `title_history_ibfk_1` FOREIGN KEY (`championship`) REFERENCES `championships` (`id`) ON UPDATE CASCADE;
What MySQL statement would return the data set I wanted?
Assuming you're storing the winner of a championship as the primary key/id of the holder, something like this should work. You might want to add in another join to get the actual name of the team from another table though.
Because LEFT join will only select rows from the 'right' table when there is a match, everything that doesn't have one should come back as NULL.
SELECT name, [holder]
FROM championships AS c
LEFT JOIN title_history AS h ON c.winner = h.id
EDITED VERSION:
With further insight into your tables and from your comment, maybe try this subselect:
SELECT friendly_name,
(SELECT winner FROM title_history WHERE championship = c.id ORDER BY date_from DESC LIMIT 1)
FROM championships AS c
ORDER BY name
If I understand your structure correctly, that ought to get the last winner of each championship?