I have a database with several users that each can have several books.
I want to get a list of users and their first book based on the (Integer) sortorder field in the table book.
Below is a example of the SQL I use now. Only problem is that it does not return the first book based on the sortorder field for each user.
SELECT
b.id, b.user_id, b.sortorder, b.title
FROM
books AS b
JOIN
books_categories AS bc
ON
(b.id = bc.book_id)
JOIN
categories AS c
ON
(c.id = bc.category_id)
JOIN
users AS u
ON
(u.id = b.user_id)
GROUP BY
b.user_id
You need to join to a subquery that selects the first book for each user.
SELECT b.id, b.user_id, b.sortorder, b.title
FROM books b
JOIN (
SELECT b2.user_id, min(b2.sortorder) sortorder
FROM books b2
GROUP BY b2.user_id
) first_books USING (user_id, sortorder);
Related
I am currently trying to create a SQL statement that allows me to retrieve information from two tables, with a third one which has the post_id from each table and the type. This will allow me to know which table the post_id is from.
ALL_POSTS:
id PRIMARY KEY,
post_id (FOREIGN KEY)
type
MOVIES:
post_id
type
title
MUSIC:
post_id
type
title
band
This is the SQL statement I used:
SELECT a.*, b.*, c.* FROM ALL_POSTS as a, MOVIES as b, MUSIC as c WHERE (a.post_id = b.id AND a.type = b.type) OR (a.post_id = c.id AND a.type = c.type)
The problem is that it retrieves a lot of results, and all of them are from the MUSIC table, and most of them are repeated.
Thanks
You should use a LEFT JOIN on both tables. This way, it would return all the rows on the ALL_POSTS table and the corresponding details on the MOVIES and MUSIC table.
SELECT * FROM ALL_POSTS AS A
LEFT JOIN MOVIES AS B ON A.post_id = B.post_id AND A.type = B.type
LEFT JOIN MUSIC AS C ON A.post_id = B.post_id AND A.type = C.type
At the moment you are generating a cartesian product from the three tables. Use JOINs instead:
SELECT A.*, B.*, C.* FROM ALL_POSTS AS A
LEFT JOIN MOVIES AS B ON A.post_id = B.ID AND A.type = B.type
LEFT JOIN MUSIC AS c ON A.post_id = C.ID AND A.type = C.type
Is it possible to INNER JOIN a MySQL query to achieve this result?
I have a table with Strategies and a table with Members. The Strategy table holds the ID of the author that corresponds to their ID in the Member table and the ID of an author that updated the existing author's work. Is it possible to grab a reference to both of these people at the same time? Something like the following, which returns no errors, but also no results...
SELECT * FROM Strategies
INNER JOIN Members AS a
INNER JOIN Members AS b
WHERE Strategies.ID='2'
AND Strategies.AuthorID = a.ID
AND Strategies.UpdateAuthorID = b.ID
Use a LEFT JOIN:
SELECT
s.*,
a.Name AS MemberName,
b.Name AS UpdatedMemberName
FROM Strategies AS s
LEFT JOIN Members AS a ON s.AuthorID = a.ID AND s.ID = 2
LEFT JOIN Members AS b ON s.UpdateAuthorID = b.ID AND s.ID = 2 ;
If you want them in one column use COALESCE:
SELECT
s.*,
COALESCE(a.Name, b.Name) AS MemberName
FROM Strategies AS s
LEFT JOIN Members AS a ON s.AuthorID = a.ID AND s.ID = 2
LEFT JOIN Members AS b ON s.UpdateAuthorID = b.ID AND s.ID = 2
SELECT toD.dom_url AS ToURL,
fromD.dom_url AS FromUrl,
rvw.*
FROM reviews AS rvw
LEFT JOIN domain AS toD
ON toD.Dom_ID = rvw.rev_dom_for
LEFT JOIN domain AS fromD
ON fromD.Dom_ID = rvw.rev_dom_from
if domain is table name
I have three tables in Mysql that are link together:
Profile (ID, Name, Stuff..)
Contact(ID, ProfileID,desc,Ord)
Address(ID,ProfileID, desc, Ord)
Now I need to select all profile from the profile table, with the “desc” field from Contact and Address where Ord = 1. (this is for a search function where in a table I’ll display the name, main contact info and main Address of a client.
I can currently do this with three separate SQL request:
SELECT Name, ID FROM Profile WHERE name=”bla”
Then in a foreach loop, I’ll run the other two requests:
SELECT ProfileID, desc FROM Contact WHERE ProfileID=MyProfileID AND Ord=1
SELECT ProfileID, desc FROM Address WHERE ProfileID=MyProfileID AND Ord=1
I know you can do multiple SELECT in one query, is there a way I could group all three SELECT into one query?
You should be able to JOIN the tables on the profile.id and the profileid in the other tables.
If you are sure the profileid exists in all three tables, then you can use an INNER JOIN. The INNER JOIN returns matching rows in all of the tables:
select p.id,
p.name,
c.desc ContactDesc,
a.desc AddressDesc
from profile p
inner join contact c
on p.id = c.profileid
inner join address a
on p.id = a.profileid
where p.name = 'bla'
and c.ord = 1
and a.ord = 1
If you are not sure that you will have matching rows, then you can use a LEFT JOIN:
select p.id,
p.name,
c.desc ContactDesc,
a.desc AddressDesc
from profile p
left join contact c
on p.id = c.profileid
and c.ord = 1
left join address a
on p.id = a.profileid
and a.ord = 1
where p.name = 'bla'
If you need help learning JOIN syntax, here is a great visual explanation of joins
This query below only selects column when an ID from Profile table has atleast one match on tables: Contact and Address. If one or both of them are nullable, use LEFT JOIN instead of INNER JOIN because LEFT JOIN displays all records from the Left-hand side table regardless if it has a match on other tables or not.
SELECT a.*,
b.desc as BDESC,
c.desc as CDESC
FROM Profile a
INNER JOIN Contact b
ON a.ID = b.ProfileID
INNER JOIN Address c
ON a.ID = c.ProfileID
WHERE b.ORD = 1 AND
c.ORD = 1 AND
a.Name = 'nameHERE'
The LEFT JOIN version:
SELECT a.*,
b.desc as BDESC,
c.desc as CDESC
FROM Profile a
INNER JOIN Contact b
ON a.ID = b.ProfileID AND b.ORD = 1
INNER JOIN Address c
ON a.ID = c.ProfileID AND c.ORD = 1
WHERE a.Name = 'nameHERE'
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
i have created working demo as your requirement :
The query bellow will retrieve all matching records from the database.its retrieving profile id,name stufff and description of contact tables
select p.id,p.name,p.stauff,c.descr,a.descr from profile as p
inner join contact as c on c.profileid=p.id
inner join address as a on a.profileid=p.id
where p.name="bla" and c.ord=1 and a.ord=1
I have 3 tables :
categories:
ID, category
"1","Cars"
"2","Trucks"
"3","Bikes"
"4","Planes"
"5","Boats"
users:
ID, username
"1","john"
"2","bob"
"3","billy"
users_categories:
ID, userid, categoryid
"1","1","2"
"2","1","5"
"3","2","3"
"4","3","2"
"5","3","4"
"6","3","5"
Q1. What I want is :
john,Trucks,Boats
bob,Bikes
billy,Trucks,Planes,Boats
I've come to this. A Concat of the categories would do.
SELECT U.`username`, (SELECT C.`category` FROM `categories` C LEFT JOIN `users_categories` UC ON C.`ID` = UC.`categoryid` WHERE U.ID = UC.userid) FROM `users` U
But I get #1242 - Subquery returns more than 1 row.
Q2. Is there a better way to structure this ? There won't be more than 50-100 categories.
use GROUP_CONCAT to achieve what you want
SELECT a.username,
GROUP_CONCAT(c.category)
FROM users a
INNER JOIN users_categories b
On a.Id = b.userID
INNER JOIN categories c
ON b.categoryID = c.ID
GROUP BY a.ID
SQLFiddle Demo
if you can live with having the categories as a comma separated string, you can use the GROUP_CONCAT function.
Let's see (I've never tried myself in mysql)
select u.username,
GROUP_CONCAT(DISTINCT c.Category order by c.Category SEPARATOR ',')
from users u
join usersCategories uc
on u.ID = uc.userID
join Categories c
on c.ID = uc.CategoryID
You might have to adjust it to MySQL specific syntax, sorry.
I have the following database schema:
table courses:
id
tutor_id
title
table course_categories:
id
category_id
course_id
table categories:
id
name
table tutors:
id
name
table subscribers:
id
course_id
user_id
I need to make 1 sql to get a course with all it's categories, and the tutor for that course and the number of subscribers for that course. Can this be done in 1 query? Should this be done using stored procedures?
With this query you get what you want:
select co.title as course,
ca.name as category,
t.name as tutor,
count(s.*) as total_subscribers
from courses co
inner join course_categories cc on c.id = cc.course_id
inner join categories ca on cc.category_id = ca.id
inner join tutors t on co.tutor_id = t.tutor_id
left join subscribers s on co.id = s.course_id
where co.title = 'Cat1'
group by co.title, ca.name, t.name
I used left join on subscribers because there might be no one for a given course. I'm assuming that all the other tables have data on it for every course, categorie and tutor. If not, you can user left join as well but then you'll have data with null.
It can be done. You need to look up select and the use of join. See select and join to help complete the assignment
select cou.title, cat.name, tu.name, count(sub.user_id) from courses cou, course_categories cca, categories cat, tutors tu, subscribers sub where cou.id = cca.id and cat.id = tu.id and tu.id = sub.id group by cou.title, tu.name;