select all row values as a list - sql

I have a table tasks that looks like this:
userId caption status id
1 Paul done 1
2 Ali notDone 18
3 Kevin notDone 12
3 Elisa notDone 13
I join it with another table users to find the number of taskswhere status = notDone. I do it like this:
SELECT u.id,
t.number_of_tasks,
FROM users u
INNER JOIN (
SELECT userId, COUNT(*) number_of_tasks
FROM tasks
WHERE status = "notDone"
GROUP BY userId
) t ON u.id = t.userId
"""
Now, I want create another column captions that somehow includes a list of all captions that were included in the countand fulfil the join + where conditions.
For example, I would expect this as one of the rows. How can I achieve this?
userId number_of_tasks captions
3 2 ["Kevin", "Elisa"]

You can use json_group_array() aggregate function inside the subquery to create the list of captions for each user:
SELECT u.id, t.number_of_tasks, t.captions
FROM users u
INNER JOIN (
SELECT userId,
COUNT(*) number_of_tasks,
json_group_array(caption) captions
FROM tasks
WHERE status = 'notDone'
GROUP BY userId
) t ON u.id = t.userId;

Related

How to sum up max values from another table with some filtering

I have 3 tables
User Table
id
Name
1
Mike
2
Sam
Score Table
id
UserId
CourseId
Score
1
1
1
5
2
1
1
10
3
1
2
5
Course Table
id
Name
1
Course 1
2
Course 2
What I'm trying to return is rows for each user to display user id and user name along with the sum of the maximum score per course for that user
In the example tables the output I'd like to see is
Result
User_Id
User_Name
Total_Score
1
Mike
15
2
Sam
0
The SQL I've tried so far is:
select TOP(3) u.Id as User_Id, u.UserName as User_Name, SUM(maxScores) as Total_Score
from Users as u,
(select MAX(s.Score) as maxScores
from Scores as s
inner join Courses as c
on s.CourseId = c.Id
group by s.UserId, c.Id
) x
group by u.Id, u.UserName
I want to use a having clause to link the Users to Scores after the group by in the sub query but I get a exception saying:
The multi-part identifier "u.Id" could not be bound
It works if I hard code a user id in the having clause I want to add but it needs to be dynamic and I'm stuck on how to do this
What would be the correct way to structure the query?
You were close, you just needed to return s.UserId from the sub-query and correctly join the sub-query to your Users table (I've joined in reverse order to you because to me its more logical to start with the base data and then join on more details as required). Taking note of the scope of aliases i.e. aliases inside your sub-query are not available in your outer query.
select u.Id as [User_Id], u.UserName as [User_Name]
, sum(maxScore) as Total_Score
from (
select s.UserId, max(s.Score) as maxScore
from Scores as s
inner join Courses as c on s.CourseId = c.Id
group by s.UserId, c.Id
) as x
inner join Users as u on u.Id = x.UserId
group by u.Id, u.UserName;

How to extract only rows that contain fields in a given list?

Consider I have 2 tables One User
userId
userName
123
user1
124
user2
and the other Items
userId
itemName
123
"item1"
123
"item2"
124
"item1"
Is there a way to get a list of all users who have all the items in a given list (say ["item1","item2"]) i.e in this case only user - 123 is a valid response. the below would be the expected response in this case
userId
userName
123
user1
Right now I am thinking of pulling in data using in ("item1","item2") and then processing it in memory.
I am looking for a way to accomplish this using a query.
Try:
select u.userId,u.userName
from Users u
inner join ( select userId
from Items
where itemName in ('item1','item2')
group by userId
having count(itemName)=2
) as i on u.userId=i.userId;
Demo: https://www.db-fiddle.com/f/7yUJcuMJPncBBnrExKbzYz/168
Subquery returns only the userId from Items table which have both itemName.
select u.* from User u
join (select userId from Items where itemName='item1'
intersect
select userId from Items where itemName='item2') sub
on u.userId=sub.userId;
Using an EXISTS with a HAVING will get you that.
SELECT *
FROM Users AS usr
WHERE EXISTS (
SELECT 1
FROM Items AS itm
WHERE itm.userId = usr.userId
AND itm.itemName IN ('item1', 'item2')
HAVING COUNT(DISTINCT itm.itemName) = 2
);
userid
username
123
user1
db<>fiddle here
select *.u from
user u
inner join (select * from item where item ='item1') a on a.id=u.id --they have item1
inner join (select * from item where item ='item2') b on b.id=a.id --they have item2
;
It takes only user that have both, sql fiddle
After your explanation
select count(*), u.id from
user u
inner join (select * from item where item in ('item1','item2')) a on a.id=u.id
having count(*)>1
a with clause and array comparison:(postgres)
with base_data as (
select
userId,
array_agg(itemName) over (partition by userId) as liste
from Items
)
select
* from base_data
where liste #> array['item1','item2']::varchar[]

Select by frequency

I have two tables, like that:
users(id, name)
phones(user_id, number)
I'd like to select all user's names that are in more than three rows in the table phones. How can I do that?
Join the tables and add a having clause that limits the results returned by the count of the user_ids
select name,
count(user_id)
from users u
join phones p
on u.id = p.user_id
group by name
having count(user_id) > 3
SQL Fiddle: http://sqlfiddle.com/#!2/c5516/2
select name from user
join phones on id = user_id
Group By user_id
Having Count(number) > 3

Getting random profiles that have a match with current profile

I'm trying to get 3 random unique profiles with the same sex ID as the current user ID (orig.id_user = 6 in this example), and their respective reviews.
SELECT DISTINCT u.id_user, s.review
FROM user AS u
CROSS JOIN user AS orig ON orig.id_sex = u.id_sex
INNER JOIN user_review AS s ON s.id_user_to = u.id_user
WHERE orig.id_user = 6
ORDER BY RAND()
LIMIT 3
Somehow, the id_user column displays repeated values. Why?
UPDATE (assuming i have the id_sex value)
SELECT DISTINCT s.id_user_to, s.id_user_from, s.review
FROM user_review AS s
LEFT JOIN user AS u ON u.id_user = s.id_user_to
WHERE u.id_sex = 2
ORDER BY RAND()
LIMIT 20
But this is still returning repeated rows in the id_user_to column, they should be unique values because of the DISTINCT.
SOLUTION using GROUP BY
SELECT us.id_user_to, us.review
FROM user_review AS us
LEFT JOIN user AS u ON u.id_user = us.id_user_to
WHERE u.id_sex = 2
GROUP BY us.id_user_to
ORDER BY RAND()
LIMIT 3

List of questions comparison

I have a profile that looks like this:
profile_id | answer_id
----------------------
1 1
1 4
1 10
I have a table which contains a list of responses by poll respondents with structure like this:
user_id | answer_id
-------------------
1 1
1 9
2 1
2 4
2 10
3 14
3 29
How do I select a list of users that gave all of the answers in the profile? In this case only user 2.
You can use the following:
select user_id
from response r
where answer_id in (select distinct answer_id -- get the list of distinct answer_id
from profile
where profile_id = 1) -- add filter if needed
group by user_id -- group by each user
having count(distinct answer_id) = (select count(distinct answer_id) -- verify the user has the distinct count
from profile
where profile_id = 1) -- add filter if needed
See SQL Fiddle with Demo
Or another way to write this is:
select user_id
from response r
where answer_id in (1, 4, 10)
group by user_id
having count(distinct answer_id) = 3
See SQL Fiddle with Demo
This is an example of a join query with an aggregation:
select a.user_id
from profile p full outer join
answers a
on p.answer_id = p.answer_id and
p.profile_id = 1
group by a.user_id
having count(p.profileid) = count(*) and
count(a.user_id) = count(*)
The full outer join matches all the profiles to all the answers. If the two sets completely match, then there are no "null"s in the ids of the other set. The having clause checks for jsut this condition.
SELECT user_id
FROM user_answer
WHERE user_id in (SELECT user_id FROM profile WHERE answer_id = 1) AND
user_id in (SELECT user_id FROM profile WHERE answer_id = 4) AND
user_id in (SELECT user_id FROM profile WHERE answer_id = 10)
SELECT *
FROM table1
INNER JOIN table2
ON table1.answer_id = table2.answer_id
WHERE table2.user_id = 2
i think this might be what you're looking for.