Three Tables INNER JOIN and WHERE Clause - mysql

I have four tables
1. tbl_threads
2. tbl_comments
3. tbl_votes
4. tbl_users
suppose the currently logged in user_id =3
thread_id = 10
Now I have to retrieve the following data
All the fields from tbl_comments where tbl_comments.thread_id =10
All the fields from tbl_users based on the common key tbl_users.user_id = tbl_comments.user_id
All the fields from tbl_votes Where user_id =3 And tbl_votes.comment_id =tbl_comments.comment_id
How can I perform all this function with one single query?
I have tried the following query but it gives me the wrong results
SELECT tbl_comments.*
, tbl_users.*
, tbl_votes.*
FROM tbl_comments
INNER JOIN tbl_users
on tbl_comments.user_id = tbl_users.user_id
WHERE thread_id= 10
INNER JOIN tbl_votes
on tbl_votes.comment_id = tbl_comments.comment_id
WHERE tbl_votes.user_id= 3

Assuming the column thread_id is in the tbl_comments table, change the first where to and:
SELECT tbl_comments.*
, tbl_users.*
, tbl_votes.*
FROM tbl_comments
INNER JOIN tbl_users
on tbl_comments.user_id = tbl_users.user_id
and tbl_comments.thread_id= 10
INNER JOIN tbl_votes
on tbl_votes.comment_id = tbl_comments.comment_id
WHERE tbl_votes.user_id= 3
And although your question mentions a table named tbl_threads, you don't show any reference to it in your example.

SELECT O.OrderNumber, CONVERT(date,O.OrderDate) AS Date,
P.ProductName, I.Quantity, I.UnitPrice
FROM [Order] O
JOIN OrderItem I ON O.Id = I.OrderId
JOIN Product P ON P.Id = I.ProductId
ORDER BY O.OrderNumber

Related

MySql crazy join thorugh a grouping table

I have a database structure with the following setup:
po: id, stockNumber, factoryId, other columns
order: id, stockNumber, factoryId, other columns
stock_number: id, stockNumber, groupId
factory: id, name, groupId
The important part here is the stock_number/factory tables. The groupId column is just an integer and if two or more rows in the table have the same value then their stock numbers/factory are considered the same. Typically this is used for different sizes of the same product.
What I'd like to do is write a query that will join "order" to "po" through the group of stock_number and factory so I can find orders with no matching po. Also the factory has to match the same way.
I have this query if I have a specific stock number/factory in mind but I'd like to update it to query the whole orders table for me:
SELECT id
FROM order
WHERE
styleNumber IN (SELECT a.stockNumber FROM stock_number a INNER JOIN stock_number b ON a.groupId = b.groupId or a.id = b.id WHERE b.stockNumber = '123')
AND factoryId IN (SELECT a.submitter_id FROM submitter a INNER JOIN submitter b ON a.groupId = b.groupId OR a.submitter_id = b.submitter_id WHERE b.SUBMITTER_ID = 'alpha');
EDIT: I came up with this query which I think might be on the right track. It only joins in the stock number so it doesn't do factory yet. Can anyone confirm if I'm going in the correct direction:
SELECT *
FROM order o
LEFT JOIN stock_number s_o ON o.stockNumber = s_o.stockNumber
LEFT JOIN stock_number s_p ON s_o.groupId = s_p.groupId
LEFT JOIN po p ON s_p.stockNumber = p.stockNumber
WHERE p.id IS NULL;
Just join all the tables.
select o.id
FROM order AS o
JOIN stock_number AS sn ON sn.stockNumber = o.stockNumber
JOIN submitter AS su ON ON o.factoryId = su.submitter_id
You could use an anti-join pattern. In this example, it looks complicated because of the two relationship tables. But a query something like this:
SELECT o.id
, o.stockNumber
, o.factoryId
FROM `order` o
LEFT
JOIN `stock_number` s
ON s.stockNumber = o.stockNumber
LEFT
JOIN `factory` f
ON f.id = o.factoryId
AND f.groupId = s.groupId
LEFT
JOIN `po` p
ON p.stockNumber = s.stockNumber
AND p.factoryId = f.id
WHERE p.id IS NULL
The anti-join pattern is easier to visualize with a simpler example. Say you had the order table (as in your example), and an order_line table, with rows related to the order table by the order_id column.
order_line: id, order_id, othercolumns
To get order along with matching order_line rows:
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
JOIN `order_line` l
ON l.order_id = o.id
To include rows from order that don't have any matching rows in order_line, we can use an outer join. We add the LEFT keyword:
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
LEFT
JOIN `order_line` l
ON l.order_id = o.id
That gets all rows from order, including rows that don't have a matching row in order_line. The trick now is to exclude all the rows that have a matching row. For any rows that didn't have a match, the columns from order_line will be NULL. So we can add a test in the WHERE clause, to exclude rows that had a match.
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
LEFT
JOIN `order_line` l
ON l.order_id = o.id
WHERE l.order_id IS NULL
That gets us rows from order that don't have a matching row in order_line.
We can use this same pattern in a more complicated query. We use outer join operations, and rows from order that don't have a matching row in po will have NULL values for the columns from po.

Why is this query so slow and what can i do about it

I have the following SELECT UPDATE statement from MySQL
UPDATE table_Learning l
INNER JOIN (select ULN, id from table_users group by ULN having count(ULN) =1) u
ON l.ULN = u.ULN
set l.user_id=u.id
WHERE l.user_id is null
The problem is, it is so slow that it times out, and basically does not work.
I am sure it is to do with the line:
INNER JOIN (select ULN, id from table_users group by ULN having count(ULN) =1) u
and specifically because there is both a GROUP BY and a HAVING clause in this inner select, and from what I have read, because INNER JOINS are very slow with MySQL.
My overall aim is to:
Populate the userID's that are null in table_learning
To do so using the userID's in table_users
To Join on the field named ULN in both tables
To only populate the fields where the ULN is unique in table_users eg if more than one user has this ULN, then do not populate the user_id in table_learning
This is your query:
UPDATE table_Learning l INNER JOIN
(select ULN, id
from table_users
group by ULN
having count(ULN) = 1
) u
ON l.ULN = u.ULN
set l.user_id=u.id
WHERE l.user_id is null;
In MySQL, the subquery is going to be expensive. An index on table_learning(user_id) might help a bit. But filtering inside the subquery could also help:
UPDATE table_Learning l INNER JOIN
(select ULN, id
from table_users
where exists (select 1
from table_learning tl
where tl.ULN = u.uln and tl.user_id is null
)
group by ULN
having count(ULN) = 1
) u
ON l.ULN = u.ULN
set l.user_id=u.id
WHERE l.user_id is null;
For this, you want a composite index on table_learning(ULN, user_id).

mySQL Sub Select needed

I have three tables, libraryitems, copies and loans.
A libraryitem hasMany copies, and a copy hasMany loans.
I'm trying to get the latest loan entry for a copy only; The query below returns all loans for a given copy.
SELECT
libraryitems.title,
copies.id,
copies.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM copies
INNER JOIN libraryitems ON copies.libraryitemid = libraryitems.id AND libraryitems.deletedAt IS NULL
LEFT OUTER JOIN loans ON copies.id = loans.copyid
WHERE copies.libraryitemid = 1
ORDER BY copies.id ASC, loans.createdAt DESC
I know there needs to be a sub select of some description in here, but struggling to get the correct syntax. How do I only return the latest, i.e MAX(loans.createdAt) row for each distinct copy? Just using group by copies.id returns the earliest, rather than latest entry.
Image example below:
in the subquery , getting maximum created time for a loan i.e. latest entry and joining back with loans to get other details.
SELECT
T.title,
T.id,
T.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM
(
SELECT C.id, C.qruuid, L.title, MAX(LN.createdAt) as maxCreatedTime
FROM Copies C
INNER JOIN libraryitems L ON C.libraryitemid = L.id
AND L.deletedAt IS NULL
LEFT OUTER JOIN loans LN ON C.id = LN.copyid
GROUP BY C.id, C.qruuid, L.title) T
JOIN loans ON T.id = loans.copyid
AND T.maxCreatedTime = loans.createdAt
A self left join on loans table will give you latest loan of a copy, you may join the query to the other tables to fetch the desired output.
select * from loans A
left outer join loans B
on A.copyid = B.copyid and A.createdAt < B.createdAt
where B.createdAt is null;
This is your query with one simple modification -- table aliases to make it clearer.
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
With this as a beginning let's think about what you need. You want the load with the latest createdAt date for each c.id. You can get this information with a subquery:
select l.copyid, max(createdAt)
from loans
group by l.copyId
Now, you just need to join this information back in:
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid LEFT JOIN
(SELECT l.copyid, max(l.createdAt) as maxca
FROM loans
GROUP BY l.copyid
) lmax
ON l.copyId = lmax.copyId and l.createdAt = lmax.maxca
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
This should give you the most recent record. And, the use of left join should keep all copies, even those that have never been leant.

Select Query based on values in result set

Is there anyway to select all from 1 table based on the result of one query which contains multiple values without having to do 2 separate queries?
Say long join query will produce a list of id's
SELECT xyz FROM table long join query WHERE id = array of ids found in result table
added example:
SELECT * FROM tweets as t
where t.user_id
in(
SELECT uff.id, uff.username
FROM users as uf
LEFT JOIN followlinks as fl
ON uf.id = fl.user_id
LEFT JOIN users as uff
ON fl.follow_id = uff.id
WHERE uff.id = 1
)
The bit in the parenthesis returns an id and user name of who the user is following (uff.id=1)
How do i then get all 'tweets' by all the id's in the generated resultset
You can use subquery:
SELECT * FROM `table1` WHERE `id` IN (SELECT `table2`.id FROM `table2` )
You might want to check documentation for syntax
SELECT xyz FROM table_A join table_B WHERE table_A.id IN (SELECT ID FROM table_C);
I think you mean something like
edited after the OP edit
SELECT * FROM tweets as t
WHERE t.user_id
in(
SELECT uff.id //remove the second field, you just need the id
FROM users as uf
LEFT JOIN followlinks as fl
ON uf.id = fl.user_id
LEFT JOIN users as uff
ON fl.follow_id = uff.id
WHERE uff.id = 1
)
After trying the in clause I coudnt get the results I was after but after rethinking what I was trying to do I got my results with an additional join clause
SELECT uff.username, t.content
FROM users as uf
JOIN followlinks as fl
ON uf.id = fl.user_id
JOIN users as uff
ON fl.follow_id = uff.id
JOIN tweets as t
ON t.user_id = uff.id
WHERE uf.id = 1

SELECT count(*) on Intermediary Table with Many-Many relationship Only if Exists in Both Related Tables

I have 3 tables: users, courses and courseusers. Courseusers is the intermediary table that joins courses.idCourse with users.idUser. However, the intermediary table has no foreign key constrains and ON DELETE CASCADE or ON UPDATE CASCADE.
Users:
idUser|name
Courses:
idCourse|name
Courseusers:
id|idUser|idCourse
My question is, how do I get the top 3 most subscribed courses (most entries in courseuser), while ignoring manually deleted users from the users and courses tables (they will still exists as entries in courseuser).
What I have right now:
SELECT c.idCourse, c.name, count(*) as count
FROM courseusers as cu
JOIN course as c
ON cu.idCourse=c.idCourse
JOIN users as usr
ON (usr.idUser=u.idUser)
GROUP BY u.idCourse
ORDER BY count DESC
LIMIT 3
Try to use the following query
SELECT c.idCourse, c.name, count(*) as count
FROM courseusers as cu
LEFT JOIN course as c
ON cu.idCourse=c.idCourse
LEFT JOIN users as usr
ON (usr.idUser=u.idUser)
GROUP BY u.idCourse
ORDER BY count DESC
LIMIT 3
http://sqlfiddle.com/#!2/0567a/1
SELECT
c.name,
COUNT(1) AS total
FROM Courceusers cu
JOIN Cources c USING(idCource)
JOIN Users u USING(idUser)
GROUP BY 1
ORDER BY 2 DESC
LIMIT 3;
Join all tables based on table Users not on the intermediary table
SELECT a.idUser, a.Name, COUNT(c.idCourse) totalCount
FROM Users a
INNER JOIN CourseUsers b
ON a.idUser = b.idUser
INNER JOIN Courses c
ON b.idCourse = c.idCourse
GROUP BY a.idUser, a.Name
ORDER BY totalCount DESC
LIMIT 3
select
CourseUsers.idCourse,
Courses.name,
COUNT(distinct CourseUsers.idUser) as Subscribers
from CourseUsers
inner join Courses on CourseUsers.idCourse = Courses.idCourse
inner join Users on CourseUsers.idUser = Users.idUser
group by CourseUsers.idCourse, Courses.name
order by Subscribers desc
limit 3