I have 3 tables: Post, User and Favorites
Post
----------
PostId
Nombre
UserId
Banned
User
----------
UserId
UserName
Info
Favorites
-----------
PostId
UserId
I'm trying to get all posts and telling if the posts are favourites or not, so if a post isn't among favourites, it will appear on the query but favourite flag will be false.
this is my query:
SELECT
p.Nombre as title,
CASE ISNULL(f.PostId,0) WHEN 0 THEN 'false' ELSE 'true' END as favorito
FROM Post p
inner join User u on p.UserId = u.UserId
left outer join Favorite f on p.PostId = f.PostId
where
p.Banned = 0
and it returns everything, and tells if there is a record in favorites, but when I add the filter on where section
and f.UsuarioId = 4
only returns the posts the user likes. Shouldn't be when using left outer join or full outer join that if a record from secondary table doesn't exist should return the records from main table anyways?
¿How can I return all post using filter on secondary table? thanks!
Since you have filtered on f.UsuarioId = 4, that effectively converts the LEFT OUTER JOIN into an INNER JOIN by requiring that the Favorite have a record of a specific value. To still return all those with no related Favorite, you must test for a NULL in that table:
SELECT
p.Nombre as title,
CASE ISNULL(f.PostId,0) WHEN 0 THEN 'false' ELSE 'true' END as favorito
FROM
Post p
INNER JOINUser u ON p.UserId = u.UserId
LEFT OUTER JOIN Favorite f ON p.PostId = f.PostId
WHERE
p.Banned = 0
-- Return either those related Favorite records for UsuarioId = 4 or the NULLs
-- meaning a related record does not exist
AND (f.UsuarioId = 4 OR f.UsuarioId IS NULL)
Related
I have two tables. One with user info, one with payment info. I would like to find out users that are either the sender or the receiver of a payment.
Eample data:
user
id
other columns
1
2
3
payments:
sender
receiver
other columns
1
4
1
3
5
3
4
5
ideal output
id
1
3
what I tried:
SELECT id
FROM user u
where exists
(
SELECT 1
FROM payments p
where u.id = p.sender or u.id = p.receiver
)
BigQuery gave the error:
LEFT SEMI JOIN cannot be used without a condition that is an equality of fields from both sides of the join
which is quite confusing to me
It's because WHERE u.id = p.sender or u.id = p.receiver makes LEFT SEMI JOIN to be non-equi join.
You can separate WHERE condition into 2 EXITS clauses.
SELECT id
FROM user u
WHERE EXISTS (SELECT 1 FROM payments p WHERE u.id = p.sender)
OR EXISTS (SELECT 1 FROM payments p WHERE u.id = p.receiver)
;
output:
But this approach sometimes shows very poor performance in real circumstances.
So, below query would be another option you can use in that case.
SELECT id FROM user u WHERE EXISTS (SELECT 1 FROM payments p WHERE u.id = p.sender)
UNION ALL
SELECT id FROM user u WHERE EXISTS (SELECT 1 FROM payments p WHERE u.id = p.receiver)
;
I think below is the most optimal solution
select distinct id
from payments, unnest([sender, receiver]) id
join user
using(id)
if applied to sample data in your question - output is
There are 2 tables:
- User(id,pseudo)
- Link(id1,id2), those 2 columns are FK on id User
I want to select all Users that have no link with the id = 10. That means I want all id of User but only if a Link(10, id2) doesn't exists.
I have try with a left join but the result is not OK
select distinct *
from USER
left join LINK on USER.id = LINK.id1
where LINK.id1 != 10
An exists query seems to be the most straightforward here:
SELECT u.id, u.pseudo
FROM User u
WHERE NOT EXISTS (SELECT 1 FROM Link l
WHERE (l.id1 = u.id AND l.id2 = 10) OR (l.id2 = u.id AND l.id1 = 10));
In plain English, this says to retain every record from the User table such that no relationship exists between this user and id = 10. Note that I check both sides of a relationship in the Link table, assuming we don't know on which side any user might fall.
Add the condition LINK.id1 = 10 in the ON clause and in the WHERE clause return only the unmatched rows:
select distinct USER.*
from USER left join LINK
on USER.id = LINK.id1 and LINK.id1 = 10
where LINK.id1 is null
It is not clear if you want only id1 not to be 10 or id2 also.
I am trying to select a User where that User DOES NOT have an associated record with a certain value.
I have a User model, a User has_one Feed, a Feed has_many FeedTracks, a FeedTrack belongs_to a Track. I want to select Users only where they DO NOT have a FeedTrack with a certain TrackId.
I'm using rails but I would be open to strict SQL for this.
The best I've got is:
SELECT TOP 1000 *
FROM Users u
LEFT JOIN Feeds f ON f.user_id = u.id
LEFT JOIN FeedTracks ft ON ft.feed_id = f.id
ONLY IF ALL OF THOSE feedTracks' track_id !== <<track_id>>
Obviously that last part statement is not real SQL and that's what my question is. How would I say, hey, get me the Users where this related record doesn't exist. But if that record does exist, don't return that User.
In other words, if that user has a feed, with a feed track with that track id, don't return that User. But if it doesn't, do return that User.
You can use GROUP BY and HAVING:
SELECT u.id
FROM Users u
LEFT JOIN Feeds f
ON f.id = u.feed_id
LEFT JOIN FeedTracks ft
ON ft.feed_id = f.id
GROUP BY u.id
HAVING
SUM(CASE WHEN ft.track_id = #track_id THEN 1 ELSE 0 END) = 0
Modify the columns in the SELECT and GROUP BY as needed.
Try
User.joins(feed: :feed_tracks)
.where('feed_tracks.track_id <> ?', unwanted_track_id)
.order('id desc')
.limit(1000)
.to_sql
You should use not existst operation like this
SELECT TOP 1000 *
FROM Users u
LEFT JOIN Feeds f ON f.id = u.feed_id
LEFT JOIN FeedTracks ft ON ft.feed_id = f.id
where not exists
(select 1
from FeedTracks ft1
where ft1.feed_id = f.id
and ft1.track_id = <<track_id>>)
I've got 84,000 rows in my Users table. Users are created automatically. So, I thought it would be nice to see how many users actually did anything after being created. I wrote this query:
SELECT COUNT(*) FROM Users u
JOIN Folders f ON UserId = u.Id
JOIN Playlists p ON FolderId = f.Id
WHERE 0 = (SELECT COUNT(*) FROM PlaylistItems WHERE PlaylistId = p.Id)
My intent is to only count users which have no playlist items in any of their playlists. This query returned 74,000 results which seems high.
I'm wondering if this query is selecting all users which have at least one playlist with no items in it. That is, if a user has two playlists -- one empty and one populated -- are they still counted in my query? And, if so, how can I modify it to select only users which have only empty playlists.
If that's vastly more difficult then I might try my hand at counting only users with 1 playlist which is empty.
The database structure is:
Many users. 1:1 user:folder, 1:many folder:playlists, 1:many playlists:playlistItems
A better pattern than counting every single playlist and comparing is simply finding all the users who don't have anything in any playlist. I like NOT EXISTS for this:
SELECT COUNT(u.Id)
FROM dbo.Users AS u
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.PlayLists AS pl
INNER JOIN dbo.PlayListItems AS pli
ON pl.id = pli.PlayListID
INNER JOIN dbo.Folders AS f
ON p.FolderID = f.ID
WHERE f.UserID = u.Id
);
As an aside, calling a column Id in its primary table and something else everywhere else might seem like a good idea, but I find it quite confusing. Why isn't a FolderID called a FolderID everywhere in the data model?
Break down your query:
SELECT u.id, COUNT(*) FROM Users u
JOIN Folders f ON UserId = u.Id
JOIN Playlists p ON FolderId = f.Id
join PlaylistItems on PlaylistId = p.Id
group by u.id
This should provide you with a list of all users and the count of the number of rows in playlists by userID. a couple ways to go...
Take a count of all users not in that list:
select count(*) from users where id not in (SELECT u.id FROM Users u
JOIN Folders f ON UserId = u.Id
JOIN Playlists p ON FolderId = f.Id
join PlaylistItems on PlaylistId = p.Id
group by u.id)
MySQL performs poorly on that...same thing using left join:
select count(*)
from users u left join (SELECT u.id, COUNT(*) FROM Users u
JOIN Folders f ON UserId = u.Id
JOIN Playlists p ON FolderId = f.Id
join PlaylistItems on PlaylistId = p.Id
group by u.id)a
on a.id = u.id
where a.id is null
I want to get data if orgid = 2 or if there is no row at all for the uid. orgid is an integer. The closest thing I could think of is to do IS NULL but I'm not getting data for the uid that doesn't have an orgid row. Any idea?
select u.uid,u.fname,u.lname from u
inner join u_org on u.uid = u_org.uid
inner join login on u.uid = login.uid
where u_org.orgid=2 or u_org.orgid is NULL
and login.access != 4;
Basically the OR is if u_org.orgid row doesn't exist.
If there is "no row at all for the uid", and you JOIN like you do, you get no row as result. Use LEFT [OUTER] JOIN instead:
SELECT u.uid, u.fname, u.lname
FROM u
LEFT JOIN u_org o ON u.uid = o.uid
LEFT JOIN login l ON u.uid = l.uid
WHERE (o.orgid = 2 OR o.orgid IS NULL)
AND l.access IS DISTINCT FROM 4;
Also, you need the parenthesis I added because of operator precedence. (AND binds before OR).
I use IS DISTINCT FROM instead of != in the last WHERE condition because, again, login.access might be NULL, which would not qualify.
However, since you only seem to be interested in columns from table u to begin with, this alternative query would be more elegant:
SELECT u.uid, u.fname, u.lname
FROM u
WHERE (u.uid IS NULL OR EXISTS (
SELECT 1
FROM u_org o
WHERE o.uid = u.uid
AND o.orgid = 2
))
AND NOT EXISTS (
SELECT 1
FROM login l
WHERE l.uid = u.uid
AND l.access = 4
);
This alternative has the additional advantage, that you always get one row from u, even if there are multiple rows in u_org or login.