Simple SQL Select Query between 2 tables - sql

I have 2 table
First table - userTable
usedID userName
1 someName
2 someOthername
Second Table - ratingTable
userID ratingValue
1 5
1 3
1 5
1 3
2 5
2 5
2 3
2 5
I need to write a SQL query that will get all userID in ascending order for number of times rated (5 star)

SELECT u.userID, u.userName, COUNT(*) AS ratingCount
FROM userTable u
INNER JOIN ratingTable r
ON u.userID = r.userID
AND r.ratingValue = 5
GROUP BY u.userID, u.userName
ORDER BY ratingCount

Here's one example:
select u.UserId
, count(r.ratingValue)
from userTable u
left join
ratingTable r
on u.userID = r.userID
and r.ratingValue = 5
group by
u.UserID
order by
count(r.ratingValue)
If the result does not require users without any five star ratings, you can even omit the userTable altogether.

I assume you mentioned 5 stars as the rating system you are using and not that you only wish to retrieve users with ratings of 5 stars.
SELECT u.userName, avg( r.ratingValue ) as averageRating
FROM userTable u
LEFT JOIN ratingTable r ON u.userID = r.userID
GROUP BY u.UserID
ORDER BY avg( r.ratingValue ) desc
This will get the average rating of each user and display their names.
userName averageRating
test1 4.5000
test2 1.7500

Related

SQL LIMIT by distinct column value without subqueries

user
id
name
age
1
anna
6
2
john
10
3
lord
50
cats
id
name
userID
1
miez
1
2
caty
1
3
random
2
4
idk
3
When using
SELECT U.id, C.name FROM user U
INNER JOIN cats C ON U.id = C.id
LIMIT 2
I get as a
result
UserID
CatName
1
miez
1
caty
What I want is to limit my rows by the distinct values of UserID, like this
SELECT U.id, C.name FROM user U
INNER JOIN cats C ON U.id = C.id
LIMIT 2 <distinct U.id rows>
UserID
CatName
1
miez
1
caty
2
random
People suggested using limit in subqueries and check if UserID is in the return
like
... WHERE UserID IN (SELECT id FROM User LIMIT 2)
but this only works well for small tables and is not an elegant solution for good performance.
My idea was using DENSE_RANK(), like:
SELECT U.id, C.name FROM user U
DENSE_RANK() OVER (ORDER BY U.id) as rows,
INNER JOIN cats C ON U.id = C.id
WHERE rows < 50
but it is not working either.
You can't use a column alias on the same level where you define it. You will have to wrap the query in a derived table. However if you want a specific number of row per user you need to use partition by, not order by
select id, name
from (
SELECT u.id,
c.name,
DENSE_RANK() OVER (PARTITION BY U.id ORDER BY c.name) as rnk
FROM user U
JOIN cats C ON U.id = C.userid
) t
WHERE t.rnk <= 2

Selecting users who are in the same group with "name"

Users
id username
1 ryan
2 mike
3 annie
4 lisa
Groups
id name
1 football
2 hockey
Permissions
user_id group_id
1 1
1 2
2 1
4 2
So I'm looking for every username, who belong to at least one same group with username ryan. I'd also like to know everyone who is not in the same group.
SELECT Users.username
FROM Users
LEFT JOIN Permissions ON Users.id = Permissions.user_id
LEFT JOIN Groups.id = Permissions.group_id
WHERE Users.id;
So this is how I got it started, but have no idea how to continue.
Here is one method:
with ug as (
SELECT u.username, g.name
FROM Users u JOIN
Permissions p
ON u.id = p.user_id
)
SELECT DISTINCT ug.username
FROM ug
WHERE EXISTS (SELECT 1
FROM ug ug2
WHERE ug2.group_id = ug.group_id AND
ug2.username = 'Ryan'
);
The above assigns the group name to each user and then treats this as a single table. EXISTS is used to determine if the groups overlap.
Note that groups is not needed because you are not asking for the name of the group. The id is sufficient to answer the question.
I am not a big fan of using select distinct. It usually means that the database engine is doing more work than necessary -- creating duplicates and then removing them.
Here is an alternative solution that uses exists:
select u.*
from users u
where exists (select 1
from permissions p join
permissions pr
on pr.group_id = p.group_id join
users ur
on pr.user_id = ur.user_id and
ur.username = 'Ryan'
where p.user_id = u.id
);
You can do it with multiple joins of Users and Permissions:
select uu.username
from Users u
inner join Permissions p on p.user_id = u.id
inner join Permissions pp on pp.group_id = p.group_id
inner join Users uu on pp.user_id = uu.id and pp.user_id <> u.id
where u.username = 'ryan'
See the demo.
Results:
| username |
| -------- |
| mike |
| lisa |

Left join without multiple rows from right table

I have two tables (User and Salary). I want to do a left join from User to Salary. For each user I want their name and salary. In case they have no salary that field can be left empty. So far a left join is all we need. But I only want one row per user. Due to some defects there can be several salaries for one user (see table salary). I only want one row per user which can be selected randomly (or top 1). How do I do that? The expected output is presented in the bottom.
User Table:
User Name
1 Adam
2 Al
3 Fred
Salary Table
User Salary
1 1000
2 2000
2 2000
Expected table:
User Name Salary
1 Adam 1000
2 Al 2000
3 Fred null
Changed User to Userid as User is a reserved word in SQL
SELECT u.Userid, u.Name, MAX(S.Salary)
FROM Usertable u
LEFT JOIN Salarytable s ON u.Userid = s.userid
GROUP BY u.userid, u.name
SQL Fiddle: http://sqlfiddle.com/#!6/ce4a8/1/0
Try this:
select U.User, U.Name, min(S.Salary)
from UserTable U
left join SalaryTable S on S.User = U.User
group by U.User, U.Name
You can utilize a ROW_NUMBER to get the max (or min) salary:
SELECT *
FROM Usertable u
LEFT JOIN
(
select Userid, Salary,
row_number()
over (partition by Userid
order by Salary desc) as rn
from Salarytable
) as s
ON u.Userid = s.userid
AND rn = 1
And in Teradata you could apply the rn = 1filter using QUALIFY within the Derived Table:
SELECT *
FROM Usertable u
LEFT JOIN
(
select Userid, Salary,
row_number()
over (partition by Userid
order by Salary desc) as rn
from Salarytable
qualify rn = 1
) as s
ON u.Userid = s.userid
Use a derived table to get distinct rows from salaries table.
select u.userid, u.username, s.salary
from users u left join (select distinct userid, salary from salaries) s
on u.userid = s.userid
Also, renamed tables and columns. Table names should normally end with s (since pluralis.) Columns should not.
Or, do a GROUP BY:
select u.userid, u.username, max(s.salary)
from users u left join salaries s
on u.userid = s.userid
group by u.userid, u.username
Or skip the left join, instead do a correlated sub-query:
select u.userid, u.username, (select max(s.salary) from salaries s
where u.userid = s.userid)
from users
Try this
select distinct U.User, U.Name, S.Salary
from UserTable U
left join SalaryTable S on S.User = U.User

Inner Join of 3 Tables

In Table Approver:
No Userid
1 3
2 7
In Table Users:
No UserID RoleID
1 3 1
2 4 1
3 5 2
4 7 3
Table Roles
RoleID Name
1 ABC
2 BCD
3 CDE
I want to select rolename of users in table approver like:
Userid Name
3 ABC
7 CDE
I'm not 100% sure why approver.no is on the user and approver table.... I'm going to assume userId is unique in both situation..... if thats the case this should work:
select
u.userid,
r.name
from
Approver as a
inner join [Users] as u on a.userId = u.UserId
inner join [Roles] as r on u.roleId = r.roleId
if that is NOT the case and you need the approver.no user.UserId combo to be unique than the following should work:
select
u.userid,
r.name
from
Approver as a
inner join [Users] as u on a.userId = u.UserId
and a.No = u.No
inner join [Roles] as r on u.roleId = r.roleId
the differences between these two as far as the result set concerns can be found here: http://sqlfiddle.com/#!3/0daa9/4
Notice that the second query returns a single result against the provided data
select
a.Userid,
r.Name
from
Approver a
join Users u on a.no = u.no
join Roles r on u.RoleID = r.RoleID
SELECT U.UserID,
Name
FROM Approver A
JOIN Users U
ON A.UserId = U.UserId
JOIN Roles R
ON R.RoleId = U.RoleId
SQL FIDDLE DEMO
SELECT Approver.UserId, Name from Approver INNER JOIN Users
on Approver.No=Users.No
INNER JOIN Roles
on Users.RoleID=Roles.RoleID

Writing a Mathematical Formula in SQL?

I have these tables: users, comments, ratings, and items
I would like to know if it is possible to write SQL query that basically does this:
user_id is in each table. I'd like a SQL query to count each occurrence in each table (except users of course). BUT, I want some tables to carry more weight than the others. Then I want to tally up a "score".
Here is an example:
user_id 5 occurs...
2 times in items;
5 times in comments;
11 times in ratings.
I want a formula/point system that totals something like this:
items 2 x 5 = 10;
comments 5 x 1 = 5;
ratings 11 x .5 = 5.5
TOTAL 21.5
This is what I have so far.....
SELECT u.users
COUNT(*) r.user_id
COUNT(*) c.user_id
COUNT(*) i.user_id
FROM users as u
JOIN COMMENTS as c
ON u.user_id = c_user_id
JOIN RATINGS as r
ON r.user_id = u.user_id
JOIN ITEMS as i
i.user_id = u.user_id
WHERE
????
GROUP BY u.user_id
ORDER by total DESC
I am not sure how to do the mathematical formula portion (if possible). Or how to tally up a total.
Final Code based on John Woo's Answer!
$sql = mysql_query("
SELECT u.username,
(a.totalCount * 5) +
(b.totalCount) +
(c.totalCount * .2) totalScore
FROM users u
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM items
GROUP BY user_id
) a ON a.user_id= u.user_id
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM comments
GROUP BY user_id
) b ON b.user_id= u.user_id
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM ratings
GROUP BY user_id
) c ON c.user_id = u.user_id
ORDER BY totalScore DESC LIMIT 10;");
Maybe this can help you,
SELECT u.user_ID,
(a.totalCount * 5) +
(b.totalCount) +
(c.totalCount * .2) totalScore
FROM users u LEFT JOIN
(
SELECT user_ID, COUNT(user_ID) totalCount
FROM items
GROUP BY user_ID
) a ON a.user_ID = u.user_ID
LEFT JOIN
(
SELECT user_ID, COUNT(user_ID) totalCount
FROM comments
GROUP BY user_ID
) b ON b.user_ID = u.user_ID
LEFT JOIN
(
SELECT user_ID, COUNT(user_ID) totalCount
FROM ratings
GROUP BY user_ID
) c ON c.user_ID = u.user_ID
ORDER BY totalScore DESC
but based on yur query above,thismay also work
SELECT u.users
(COUNT(*) * .5) +
COUNT(*) +
(COUNT(*) * 2) totalcore
FROM users as u
LEFT JOIN COMMENTS as c
ON u.user_id = c_user_id
LEFT JOIN RATINGS as r
ON r.user_id = u.user_id
LEFT JOIN ITEMS as i
ON i.user_id = u.user_id
GROUP BY u.user_id
ORDER by totalcore DESC
The only difference is by using LEFT JOIN. You will not use INNER JOIN in this situation because there are chances that user_id is not guaranteed to exists on every table.
Hope this makes sense
Here's an alternative approach:
SELECT
u.user_id,
SUM(s.weight) AS totalScore
FROM users u
LEFT JOIN (
SELECT user_id, 5.0 AS weight
FROM items
UNION ALL
SELECT user_id, 1.0
FROM comments
UNION ALL
SELECT user_id, 0.5
FROM ratings
) s
ON u.user_id = s.user_id
GROUP BY
u.user_id
I.e. for every occurrence of every user in every table, a row with a specific weight is produced. The UNIONed set of weights is then joined to the users table for subsequent grouping and aggregating.