I have 2 tables - users and userbooks:
USERID USERNAME (USERS)
-------- -------
1 User1
2 User2
3 User3
. UserX
ID USERID BOOKID (USERBOOKS)
--- -------- -------
1 1 1
2 2 1
3 3 1
4 2 2
5 2 2
. userx bookx
I would like to get the common records (common read books read by every user) in this example getting the users who read book 1, but of course book id-s always vary
Unless I'm missing something obvious, you are looking for exists:
SELECT UserId, UserName
FROM Users
WHERE EXISTS
(
SELECT 1
FROM UserBooks
WHERE UserBooks.UserId = USERS.UserId
AND BookId = 1
)
This will return all the users that have corresponding records in UserBooks with the bookId 1.
Try This:
DECLARE #BOOK INT = 2;
SELECT UB.BOOKID,U.USERNAME,COUNT(*) AS TIMES
FROM #USERBOOKS UB
INNER JOIN #USERS U ON UB.USERID = U.USERID
WHERE UB.BOOKID = #BOOK
GROUP BY BOOKID,U.USERNAME
ORDER BY 1,2
;
Note that Book ID 2 was used twice by User2
Related
Hello postgres experts,
I have an app where users can vote on a poll. My schema looks like this:
polls table:
id
name
1
Favorite fruit
options table:
id
poll_id
content
1
1
apple
2
1
orange
3
1
grape
4
1
banana
participants table:
id
poll_id
name
1
1
John
2
1
Jane
votes table:
id
poll_id
participant_id
option_id
type
1
1
1
1
yes
2
1
1
3
yes
3
1
2
2
yes
I made the poor choice of deciding to not create rows for "no" votes in the votes table thinking it would "save space". I realize now that it was not such a great idea because in the future I would like to know whether the user explicitly voted "no" or if perhaps the option was added after they voted and thus did not have the option to choose it. So I need to run a query that will fill all the missing "no" votes in the votes table for existing participants. The final result should look like this:
votes table:
id
poll_id
participant_id
option_id
type
1
1
1
1
yes
2
1
1
3
yes
3
1
2
2
yes
4
1
1
2
no
5
1
1
4
no
6
1
2
1
no
7
1
2
3
no
8
1
2
4
no
I have a dbfiddle with all the data already in it:
https://dbfiddle.uk/?rdbms=postgres_14&fiddle=7d0f4c83095638cc6006b1d7876d0e01
Side question: Should I be concerned about the size of the votes table in this schema? I expect it to quickly blow up to millions of rows. Is a schema where options are stored as an array in the polls table and votes stored in the participants table a better idea?
Thank you for your help.
You seem to be looking for a JOIN of participants with options, EXCEPT the rows that already are in votes. There are various ways to do that, but most straightforward:
INSERT INTO votes(poll_id, participant_id, option_id, type)
SELECT poll_id, participant_id, option_id, 'no'
FROM (
SELECT o.poll_id, p.id, o.id
FROM options o
JOIN participants p ON o.poll_id = p.poll_id
EXCEPT
SELECT poll_id, participant_id, option_id
FROM votes
) AS missing;
Alternatively:
INSERT INTO votes(poll_id, participant_id, option_id, type)
SELECT o.poll_id, p.id, o.id, 'no'
FROM options o
JOIN participants p ON o.poll_id = p.poll_id
WHERE NOT EXISTS (
SELECT *
FROM votes
WHERE poll_id = o.poll_id AND participant_id = p.id AND option_id = o.id
);
Or, assuming you already have UNIQUE index on votes, just
INSERT INTO votes(poll_id, participant_id, option_id, type)
SELECT o.poll_id, p.id, o.id, 'no'
FROM options o
ON CONFLICT ON CONSTRAINT votes_p_key
DO NOTHING;
I have three tables:
posts (id, content)
posts_tags (posts_id, tags_id)
tags (id, tag)
How do I select all posts that have (at least) 2 specific tags (lets say tags with id 1 and 2)?
For example, posts table:
id content
---- -------
1 post1
2 post2
3 post3
tags table:
id tag
---- ------
1 tag1
2 tag2
3 tag3
posts_tags table:
posts_id tags_id
---------- ---------
1 1
1 2
2 1
3 1
3 2
3 3
I then expect the following result:
id content
---- ---------
1 post1
3 post3
Post with ID 3 (since it has tags 1, 2, and 3) and post with id 1 (since it has tags with id 1, and 2) but not post 2 since it doesn't have tag with id 2.
Assume I can not change the table structure.
SELECT *
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
WHERE pt.tags_id IN (1,2);
SELECT *
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
WHERE pt.tags_id = 1 OR pt.tags_id = 2;
SELECT *
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
WHERE pt.tags_id = 1 AND pt.tags_id = 2;
EDIT: Quick and dirty
WITH j AS (
SELECT pt.posts_id AS post,
p.content AS content,
STRING_AGG(pt.tags_id::TEXT,',') AS agg
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
GROUP BY pt.posts_id, p.content
)
SELECT post,content
FROM j
WHERE STRING_TO_ARRAY(agg,',') #> ('{2,1}'::TEXT[])
Found an answer to my own question:
SELECT posts.*
FROM posts
INNER JOIN posts_tags ON posts_tags.posts_id = posts.id
INNER JOIN tags ON tags.id = posts_tags.tags_id
WHERE tags.id IN (1, 2)
GROUP BY posts.id
HAVING COUNT(*) > 1
I have a scenario where I am joining three tables and getting the results.
My problem is i have apply limit for joined table.
Take below example, i have three tables 1) books and 2) Customer 3)author. I need to find list of books sold today with author and customer name however i just need last nth customers not all by passing books Id
Books Customer Authors
--------------- ---------------------- -------------
Id Name AID Id BID Name Date AID Name
1 1 1 ABC 1 A1
2 2 1 CED 2 A2
3 3 2 DFG
How we can achieve this?
You are looking for LATERAL.
Sample:
SELECT B.Id, C.Name
FROM Books B,
LATERAL (SELECT * FROM Customer WHERE B.ID=C.BID ORDER BY ID DESC LIMIT N) C
WHERE B.ID = ANY(ids)
AND Date=Current_date
I'm trying to do something simple in JPA.
I have a table Businesses:
BusinessId name
------------ ------
1 Joe
2 bob
And table Products:
productID name
------------ ------
1 Pen
2 paper
Because they related as meny-to-many I created another table businessesHasProductID:
BusinessId productID
------------ -----------
1 1
1 2
2 2
Now I want to select BusinessId and productID form businessesHasProductID where the name of BusinessId = 'x' and the name of productID = 'y'.
I built the tables and then I created the entity classes (from wizard in netBeans). I know how to get the "Businesses" table where Businesses.name = 'x' and I know how to get "Products" table where Products.name = 'y'. but I want to combine these results and get the IDs.
I tried to do :
Query query = getEntityManager().createQuery("
SELECT b FROM businessesHasProductID WHERE b.BusinessId IN
(SELECT t0.BusinessId FROM Businesses t0 WHERE t0.BusinessId = 'x')
AND b.productID IN
(SELECT t1.productID FROM Products t1 WHERE t1.productID = 'y')
");
That's not worked. It complains that the IN contains invalid data.
If I understand correctly, you want to get all the [bId, pId] tuples that exist in the join table and for which the name of the business identified by bId is 'x' and the name of the product identified by pId is 'y'.
If so, the following query should do what you want:
select business.businessId, product.productId
from Business business
inner join business.products product
where business.name = 'x'
and product.name = 'y'
I am using SQL Server 2008 R2
I am trying to write a single query that will return only exactly what I need. I will drop in a MovieID and get back a list of ALL genres. If the movie represents a specific genre (has an associated record in the junction table), the Checked value will be 1. If not, then 0.
My result set should look like this:
GenreID Genre Checked
1 ABC 0
2 DEF 1
3 HIJ 0
4 KLM 1
My First table is named Genres. It looks like this:
GenreID Genre
1 ABC
2 DEF
3 HIJ
4 KLM
My second table is named Movies. It looks like this:
MovieID Title
1 Blah
2 Foo
3 Carpe
4 Diem
My third table is a junction table named Movies_Genres. It looks like this:
MovieID GenreID
1 2
1 1
1 4
2 1
2 3
3 4
4 1
I would normally, do a couple of queries and a couple of loops to handle this, but I want to really just make the database do the work here. How do I tweak my query so that I can get the resultset that I need with just a single query?
Here's the starting query:
SELECT GenreID,
Genre
FROM Genres
Thanks in advance for your help!!!
SELECT g.GenreID, g.Genre, Checked = CASE WHEN EXISTS
(SELECT 1 FROM dbo.Movies_Genres AS mg
INNER JOIN dbo.Movies AS m
ON mg.MovieID = m.MovieID
WHERE mg.GenreID = g.GenreID
AND m.MovieID = #MovieID) THEN 1 ELSE 0 END
FROM dbo.Genres AS g
ORDER BY g.GenreID;
If there is a unique constraint or primary key on dbo.Movies_Genres(MovieID, GenreID) then this can be simply:
SELECT g.GenreID, g.Genre, Checked = COUNT(mg.GenreID)
FROM dbo.Genres AS g
LEFT OUTER JOIN dbo.Movies_Genres AS mg
ON g.GenreID = mg.GenreID
AND mg.MovieID = #MovieID
GROUP BY g.GenreID, g.Genre;
...since the count for any genre can only be 0 or 1 given a single #MovieID.
Pretty straight forward using CASE;
SELECT DISTINCT g.GenreID, g.Genre,
CASE WHEN mg.MovieID IS NULL THEN 0 ELSE 1 END Checked
FROM Genres g
LEFT JOIN Movies_Genres mg
ON g.GenreID=mg.GenreID
AND mg.MovieId=#MovieID;
Demo here.
Edit: If entries are guaranteed to be unique in Movies_Genres, you could choose to drop the DISTINCT.
The #MovieID is the movie, you want to filter by.
SELECT Genres.GenreID,
Genres.Genre,
CASE WHEN (Movies_Genres.GenreID IS NULL)
THEN 0
ELSE 1
END AS Checked
FROM Genres LEFT JOIN
Movies_Genres ON Movies_Genres.GenreID = Genres.GenreID AND
MovieID = #MovieID