SQL : one to many query on the many data - sql

I'm having a really hard time figuring this out, hopefully someone can shed some light
Consider the following data
Users:
id username password
--------------------------
1 Bob 123
2 Alice abc
...
Services:
id user_id name status
-----------------------------
1 1 product1 canceled
2 1 product2 canceled
3 1 product3 live
4 2 product1 canceled
I need to do a query and find all users who only have canceled services. So the response should be "alice" only as bob has at least 1 live service.
If I try filter where status!='live' then (obviously) I still get Bob & Alice, how can I do a query where all the status is canceled is this possible?

Using exists logic we can try:
SELECT u.username
FROM Users u
WHERE NOT EXISTS (SELECT 1 FROM Services s
WHERE s.user_id = u.id AND s.status <> 'canceled');
This query says, in plain English, to find any users for which we cannot find in the Services table a record for that user whose status is not cancelled.

select distinct u.username from(
select * from service where status = 'canceled') canceled
inner join users u on u.id = canceled.user_id
where canceled.user_id not in(select user_id from service where status = 'live')
It selects all users which is not in the subset with the live status

Here is another way of approaching the problem
select distinct u.user_name from (
select user_id, sum(case when status = 'live' then 1 else 0 end) as live_cnt from services
group by user_id
having live_cnt = 0) s join users u on s.user_id =u.id
;

Related

How to show all users name in SQL

How to show all user name even count is zero.
$users = DB::select('select count(c.status) as countEvent, sum(c.point) as totalPoint,u.name from users u left join campaign_users c on u.id=c.user_id where u.deleted_at IS NULL group by u.name order by countEvent desc');
I want to count every user attend how many events but some of them not attend at all which is zero. I want to display all but the result only displayed users who attend at least one.
users - id, name
campaign_users - status (1 for pending, 2 for attend, 3 for absent), point
Assuming you are interested in countEvent and totalPoint values only for status = 2 (i.e. Attend)
SELECT u.name
,SUM(CASE WHEN c.status = 2 THEN 1 ELSE 0 END) AS countEvent
,SUM(CASE WHEN c.status = 2 THEN c.point ELSE 0 END ) AS totalPoint
FROM users u
LEFT JOIN campaign_users c
ON u.id=c.user_id
WHERE u.deleted_at IS NULL
GROUP BY u.name
It will be great if you can provide the sample data and intended output to understand your requirement clearly.
I guess one of the right way to do it is to query first the names and then count the results of the query .

To display only the active status for one specific id and for same id all status should be disabled

I want to display person list who are Active for only one application starting 'a' and all other application should be 'DISABLED'.
Person id is in one table and application details will be in another table.
Have to select the Person id who is satisfies the following condition. I have attached the model result in which I have joined the three table . I need to select user as below(Highlighted),
person_id is a primary key.
The query used as follows:
SELECT * FROM (select p.person_id from ur_username u join ur_username_person up on u.username_id=up.username_id
join ur_person p on up.person_id=p.person_id
WHERE ( U.USERNAME LIKE 'A-%' OR U.USERNAME LIKE 'a-%')
AND u.status='ACTIVE' AND U.ROLE_ID =123 ) E
WHERE p.person_id IN ( select p.person_id from ur_username u join ur_username_person up on u.username_id=up.username_id
join ur_person p on up.person_id=p.person_id
WHERE ( U.USERNAME NOT LIKE 'A-%' OR U.USERNAME NOT LIKE 'a-%')
AND u.status='DISABLED' AND U.ROLE_ID =123 )
I am getting the person who is active in any other application also.
In that above table i want the username a-pri (starts with a-) also with status 'ACTIVE' all the other status for application id 123 is disabled . no other username is active for that 123 id.
We can try aggregating on the Application_id and then checking two things. First, we can check that a username a-pri who is active appears once, and only once. Second, we can assert that only one record appears as active. These two conditions would seem to meet your requirements.
SELECT
Application_id
FROM yourTable
GROUP BY
Application_id
HAVING
SUM(CASE WHEN username='a-pri' AND status='ACTIVE' THEN 1 ELSE 0 END) = 1 AND
SUM(CASE WHEN status = 'ACTIVE' THEN 1 ELSE 0 END) = 1;
Follow the link below which uses a larger sample data set to cover more edge cases.
Demo
Probably I do not correctly understand your request, but here is my code:
Select username from your_rable where status = 'ACTIVE';
If this is not what you intend to receive, please provide more dataset, maybe for more Application_id and also the needed result.

sql: many to many relationship join

I'm very new to SQL so if there are multiple possibilities I'd like to see them all (and hear which possibilities are better than others). I'm using sqlite3.
I have the following 3 tables: user, channel, subscriptions
user:
user_id name
1 Johnny
2 Stacy
3 Allana
channel:
channel_id channel_name
1 ESPN
2 Disney
subscriptions:
user_id channel_id
1 1
2 2
3 1
3 2
What SQL command do I need to perform to get the following table? I basically want to see who is subscribed to which channels by names (so exactly what's laid out in the subscriptions table but mapping numbers to names based on the other tables).
user_id channel_id
Johnny ESPN
Stacy Disney
Allana ESPN
Allana Disney
I've tried the following but I'm getting nothing in the return statement:
select user.name, channel.channel_name from user, channel, subs where user.user_id == subs.user_id and channel.channel_id == subs.channel_id
Try this out and let me know in case you face any difficulty.
select a.name,c.channel_name
from
user a
left join
subscriptions b
on a.user_id = b.user_id
left join
channel c
on b.channel_id = c.channel_id;
or (in the format u asked in comments)
select u.name,c.channel_name
from
user u
left join
subscriptions s
on u.user_id = s.user_id
left join
channel c
on s.channel_id = c.channel_id;
Haven't tested it but try this:
select
u.name
,c.channel
from
user_id u
inner join subscriptions s
on u.user_id=s.user_id
inner join channel c
on s.channel_id=c.channel_id

SQL join one table against multiple columns

I am having 2 tables in my DB
1) Report table
AppNo AppName AddedBy AssignedTo ModifiedBy
-------------------------------------------
1 App1 1 2 1
2 App2 1 2 2
3 App3 2 2 2
4 App4 1 2 3
2) Users table
UserId UserName Role
----------------------
1 Raj Manager
2 Sid Lead
3 KK Rep
So i want to join two tables so that i can get names in place of Id's
Result Needed:
AppNo AppName AddedBy AssignedTo ModifiedBy
-------------------------------------------
1 App1 Raj Sid Raj
2 App2 Raj Sid Sid
3 App3 Sid Sid Sid
4 App4 Raj Sid KK
My Query:
SELECT
R.AppNo, R.AppName,
u1.UserName as AddedBy,
u2.UserName as AssignedTo,
u3.UserName as ModifiedBy
FROM Report R
LEFT OUTER JOIN Users u1 on u1.UserID = R.AddedBy
LEFT OUTER JOIN Users u2 on u2.UserID = R.AssignedTo
LEFT OUTER JOIN Users u3 on u3.UserID = R.ModifiedBy
But i dont want to join multiple time with User table..
As in my original report table there are nearly 8 UserId columns are there so i cant join 8 times it reduces performance.
Can anyone suggest the best way.
Note: I cant change table schema
Thanks in advance
Joining multiple times is the only way; that is what the data and structure is.
There should be no performance issue, as the database is typically well designed to handle this. Just because a JOIN looks complex to humans doesn't mean it is complex or expensive for the optimizer or the database.
You could use a cartesian join for the columns, although performance will depend heavily on how well indexed the tables are...
select R.AppNo,
R.AppName,
(select UserName from Users where UserID = R.AddedBy) as AddedBy,
(select UserName from Users where UserID = R.AssignedTo) as AssignedTo,
(select UserName from Users where UserID = R.ModifiedBy) as ModifiedBy
from Report R

Count with subquery, group_by and Left join

I am struggeling for along time with this particular query. I am making a 'leaderboard' of users whom have added or received the most places to their guides.
My data model is as follows:
Users
uuid id
string first_name
bool blogger
Guests
uuid id
Tips (join model)
uuid id
uuid guide_id
uuid place_id
integer status
uuid owner_id
string owner_type
Guides
uuid id
uuid user_id
string name
Place
uuid id
string name
A user can add places (trough tips) to his/her guide. This same user can also receive places (tips) from other users to his/her guide. These tips can be accepted tips.status = 1.
What I want is the following:
first_name and count of all places added or accepted tips over all their guides, but not tips they have given to other users grouped by users.blogger = 1.
example:
Guest = true
you 40
user1 30
user2 25
Guest = false
user3 20
user4 15
user5 5
this is what I have so far:
SELECT tips.owner_id, tips.owner_type, count(tips.owner_id) AS places_count
FROM "tips"
LEFT JOIN users on (owner_type ='User' AND users.id = owner_id)
GROUP BY "tips"."owner_id", "tips"."owner_type"
ORDER BY places_count DESC
LIMIT 16
This query does return counts, but does not take received tips in regard and it also counts given tips to other users. I have a hunch that I need to use subqueries, firstly select all the guide id's from a given user and secondly 'simply' select a count of all tips where guide_id = selected_guide_ids AND tips.status = 1. Lastly group the results by users.blogger = 1
But how do I write this?
Edit 1:
I have updated my original question with an additional Guest table (this is why I use owner_type and owner_id instead of table_id. And I've updated the user table with blogger (bool) on which I want to group the results.
Sample data:
Users
id first_name blogger
user1 Daniel true
user2 Quassnoi false
user3 vkp true
Guests
id
guest_1
guest_2
Guides
id user_id name
guide_1 user_1 Bugers
guide_2 user_1 Cool places
guide_3 user_2 Amsterdam
Tips
id guide_id place_id status owner_id owner_type
tip1 guide_1 place_1 1 user_1 User # user_1 added place_1 to his own guide guide_1 (accepted)
tip2 guide_1 place_2 1 guest_1 Guest # guest_1 suggested place_2 to user_1's guide guide_1 (accepted)
tip2 guide_1 place_2 0 guest_1 Guest # guest_1 suggested place_2 to user_1's guide guide_1 (rejected)
tip_3 guide_2 place_1 1 user_2 User # user_2 added place_1 to his own guide guide_3 (accepted)
tip_4 guide_2 place_2 1 user_2 User # user_2 added place_2 to his own guide guide_3 (accepted)
tip_5 guide_2 place_3 1 user_1 User # user_1 added place_3 to user_2's guide guide_2 (accepted)
Places
id name
place1 burgerbar
place2 burgermeester
place_3 bbq shack
What my desired outcome is:
Note that tips given to other users don't count for the tip giver.
first_name tips_count blogger
Quassnoi 3 false (2 added by himself, 1 received from user_1)
Daniel 2 true (1 added by himself, 1 received from guest1. Note that the rejected tip does not count)
vkp 0 false
Edit 2
I've altered Quassnoi's answer a little bit to this:
SELECT *
FROM users u
LEFT JOIN
(
SELECT g.user_id, COUNT(*) tips_count
FROM guides g
JOIN tips t
ON t.guide_id = g.id
AND (t.owner_id = g.user_id AND t.status = 1)
GROUP BY g.user_id
) g
ON g.user_id = u.id
ORDER BY tips_count DESC
This however returns all the records where tips_count is NULL first. I want those to be 0 instead of NULL. How can I cast NULL tips_count to 0?
Edit 3:
I've updated the query so that it only counts the tips where the guide_id is equal to the guide ids from the given user.
SELECT *
FROM users u
LEFT JOIN
(
SELECT g.user_id, COUNT(*) tips_count
FROM guides g
JOIN tips t
ON t.guide_id = g.id
AND (t.guide_id = g.id AND t.status = 1)
GROUP BY g.user_id
) g
ON g.user_id = u.id
ORDER BY tips_count DESC
It seems that it doesn't really matter who added/suggested a place. Once it is accepted (status 1) it belongs to the guide and thus to the guide's user. Hence:
select u.first_name, u.blogger, count(t.id)
from users u
left join guides g on g.user_id = u.id
left join tips t on t.guide_id = g.id and t.status = 1
group by u.id
order by count(t.id) desc;
The way your schema is set up now, it's impossible to tell which guide belongs to which user.
Assuming there's a guide.owner you forgot to mention (or forgot to add), it would be:
SELECT *
FROM user u
LEFT JOIN
(
SELECT g.owner, COUNT(*) cnt
guide g
JOIN tips t
ON t.guide_id = g.id
AND (t.owner_id = g.owner OR t.status = 1)
GROUP BY
g.owner
) g
ON g.owner = u.id