SQL - Choose 4 movies (one which stars one actor and the other 3 where he doesn't participate) - sql

I have 3 tables:
FILMS
id film
1 Gladiator
2 Pulp Fiction
3 Taxi Driver
4 ...
ACTORS
id actor
1 Russell Crowe
2 Robert DeNiro
3 John Travolta
4 Samuel L. Jackson
RELATIONSHIPS
id_film id_actor
1 1
2 3
2 4
3 2
Now I'm trying to make a query where by passing an actor's id I would get 4 random movies - One where he participates and three others where he doesn't.
I'm finding it hard to find a solution. Any idea of what would be the better approach?

The canonical way would use union all. The following breaks this out into separate CTEs, just to make the logic very clear:
with a_1 as (
select top 1 r.id_file
from relationships r
where r.id_actor = #id_actor
order by newid()
),
nota_3 as (
select top 3 r.id_film
from relationships r
group by r.id_film
having sum(case when r.id_actor = #id_actor then 1 else 0 end) = 0
order by newid()
)
select * from a_1 union all
select * from nota_3;

To my mind the clearest way of expressing the query for non-participation films is with EXCEPT, like so:
;WITH ParticipationFilms AS (
SELECT F.id, F.film
FROM FILMS F INNER JOIN RELATIONSHIPS R ON F.id = R.id_film
WHERE R.id_actor = #id_actor
)
, NonParticipationFilms AS (
SELECT id, film
FROM FILMS
EXCEPT
SELECT id, film
FROM ParticipationFilms
)
SELECT TOP (1) * FROM ParticipationFilms ORDER BY NEWID()
UNION ALL
SELECT TOP (3) * FROM NonParticipationFilms ORDER BY NEWID()
;

Related

How to get these rows as columns in an SQL query

I need some help in writing up this SQL query using a single table. Something like this
User ID
Category
Spend
Transactions
Country
1
Sport
30
2
USA
1
Bills
60
3
USA
2
Sport
10
1
MEX
3
Grocery
50
8
CAN
2
Grocery
70
4
MEX
3
Sport
20
5
CAN
3
Bills
30
2
CAN
1
Petrol
60
5
USA
I then want to group the rows by the User id and group the spend and transactions each by the category and having the country as a column by itself like this.
User ID
Sport_Spend
Bills_Spend
Grocery_Spend
Petrol_Spend
Sport_Transactions
Bills_Transactions
Grocery_Transactions
Petrol_Transactions
Country
1
30
60
0
60
2
3
0
5
USA
2
10
0
70
0
1
0
4
0
MEX
3
20
30
50
0
5
2
8
0
CAN
Its stumping me a bit would appreciate some help.
#jarlh comments are most relevant and need to be addressed. But here is something to start with: (ms sql code) (I opted out from transactions columns to reduce the problem, but the coding is just the same) https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=25550539029ba1c4be0826725bf9e00a
with data (UserID,Category,Spend,Transactions,Country) as(
select 1,'Sport',30,2,'USA' union all
select 1,'Bills',60,3,'USA' union all
select 2,'Sport',10,1,'MEX' union all
select 3,'Grocery',50,8,'CAN' union all
select 2,'Grocery',70,4,'MEX' union all
select 3,'Sport',20,5,'CAN' union all
select 3,'Bills',30,2,'CAN' union all
select 1,'Petrol',60,5,'USA'
)
select UserID
,isnull(SUM([Sport]),0)as Sport
,isnull(SUM([Bills]),0)as Bills
,isnull(SUM([Grocery]),0)as Grocery
,isnull(SUM([Petrol]),0)as Petrol
,MAX(Country)as Country
from (
select UserID,Category,Spend,Transactions,Country
from data) p
PIVOT(
SUM(SPEND)
For CATEGORY in ([Sport] ,[Bills] ,[Grocery] ,[Petrol])
)as PivotTable
group by UserID
select
COALESCE(user_id,0) as user_id,
COALESCE(Sport_Spend,0) as Sport_Spend,
COALESCE(Bills_Spend,0) as Bills_Spend,
COALESCE(Grocery_Spend,0) as Grocery_Spend,
COALESCE(Petrol_Spend,0) as Petrol_Spend,
COALESCE(Sport_Transactions,0) as Sport_Transactions,
COALESCE(Bills_Transactions,0) as Bills_Transactions,
COALESCE(Grocery_Transactions,0) as Grocery_Transactions,
COALESCE(Petrol_Transactions,0) as Petrol_Transactions
,country from
(SELECT DISTINCT user_id,country from table_name) as A
LEFT JOIN
(select user_id, spend as Sport_Spend ,transactions as Sport_Transactions from table_name where category='Sport') as B using (user_id)
LEFT JOIN
(select user_id, spend as Bills_Spend ,transactions as Bills_Transactions from table_name where category='Bills') as C using (user_id)
LEFT JOIN
(select user_id, spend as Grocery_Spend ,transactions as Grocery_Transactions from table_name where category='Grocery') as D using (user_id)
LEFT JOIN
(select user_id, spend as Petrol_Spend ,transactions as Petrol_Transactions from table_name where category='Petrol') as E using (user_id)
ORDER BY user_id;

How to return top 3 rows if last row is tied

Suppose my table contains data like that
ID MovieName Rating
--------------------------------------------
1 The Shawshank Redemption 9.20
2 The Godfather: Part II 9.00
3 12 Angry Men 8.90
4 Pulp Fiction 8.90
5 The Good, the Bad and the Ugly 8.80
I want to select top 3 movies according to highest rating which contains both '12 Angry Men' and 'Pulp Fiction' movies.So query should return 4 rows instead of 3.
Just use TOP WITH TIES
SELECT TOP 3 WITH TIES ID, MovieName, Rating
FROM MyMoviesDB
ORDER BY Rating DESC
Only thing is you have to use ORDER BY
Try:
SELECT A.ID, A.MOVIENAME, A.RATING
FROM
(SELECT ID, MOVIENAME, RATING, DENSE_RANK() OVER (ORDER BY RATING DESC) AS R
FROM YOUR_TABLE) A
WHERE A.R <= 3
ORDER BY A.RATING DESC;
Dense Rank will repeat for same ratings. So if one of the top 3 ranks have more than one occurrences, you will get more than 3 entries in the output.
You would want to select the rows in the moviesdb table where the Rating is in the DISTINCT Top 3 Ratings Ordered BY Ratings in Desc Order:
SELECT *
FROM moviesdb
WHERE Rating IN (SELECT DISTINCT TOP 3 Rating
FROM moviesdb
ORDER BY Rating Desc)
Select * from Table_Name
ORDER BY Rating
LIMIT 4

How to write optimized query for multiple prioritize conditional joins in SQL server?

The scenario I'm after for is :
Result = Nothing
CollectionOfTables = Tbl1, Tbl2, Tbl3
While(True){
CurrentTable = GetHighestPriorityTable(CollectionOfTables)
If(CurrentTable) = Nothing Then Break Loop;
RemoveCurrentTableFrom(CollectionOfTables)
ForEach ID in CurrentTable as TempRow {
If(Result.DoesntContainsId(ID)) Then Result.AddRow(TempRow)
}
}
Assume I have following three tables.
IdNameTable1, Priority 1
1 John
2 Mary
3 Elsa
IdNameTable2, Priority 2
2 Steve
3 Max
4 Peter
IdNameTable3, Priority 3
4 Frank
5 Harry
6 Mona
Here is the final result I need.
IdNameResult
1 John
2 Mary
3 Elsa
4 Peter
5 Harry
6 Mona
A few tips to keep in mind.
Number of actual tables is 10.
Number of rows per table exceeds 1 Million.
It's not necessary to use join in query, but because of amount of data I'm working with the query must be optimized and used set-operations in SQL not a Cursor script.
Here's a way to do it using UNION and ROW_NUMBER():
;With Cte As
(
Select Id, Name, 1 As Prio
From Table1
Union All
Select Id, Name, 2 As Prio
From Table2
Union All
Select Id, Name, 3 As Prio
From Table3
), Ranked As
(
Select Id, Name, Row_Number() Over (Partition By Id Order By Prio) As RN
From Cte
)
Select Id, Name
From Ranked
Where RN = 1
Order By Id Asc;

select N items for every group?

I have tables:
Category: Id, Name...
News: Id, Title
News_Category_Mapping: Id, NewsId, CategoryId
Where newsid, categoryid are foreign keys to these 2 tables.
News_category_mapping:
Id NewsID CategoryId
1 1 1
2 2 1
3 3 1
4 4 3
5 5 5
6 6 3
so i may want to get maximum 2 news items from every categoryid, say like
Id NewsID CategoryId
1 1 1
2 2 1
4 5 3
6 6 3
5 5 5
Sorry for my english.
Let say you need 2 items each
Select *
From Category C
CROSS APPLY (Select top 2 Id,CatId,NewsName
From News Nw where Nw.CatId=C.Id) As N
Here is the fiddle sample
Try this:
WITH CTE AS
(SELECT C.Id,N.Id,N.Title,RN=ROW_NUMBER() OVER (PARTITION BY NC.CategoryID ORDER BY NC.NewsId)
FROM News_Category_Mapping NC JOIN
News N ON NC.NewsId=N.Id JOIN
Category C ON NC.CategoryId=C.Id)
SELECT * FROM CTE WHERE RN<3
Explanation:
Here, the inner query selects the records along a row number RN. To know how the query works, please execute the inner query first.
You can use CROSS APPLY, like so:
Select c.*, Sub.*
from
Categories c cross apply
(
select top 2
*
from
News n
where
exists
(
select 1
from NewsCategories nc
where nc.CatId = c.id and n.id = nc.NewsId
)
) Sub
Here is an SQLFiddle for this

Taking variables outside of query in sql

I know my title is a bit misleading but i am unsure of what would be a good title.
Authored has 2 columns namely ID, PubID
Is there anyway i could output the P into my result.
I would like to know for each respective ID, PubID pair, how many rows would have the same PubID but different ID.
select a.authorId, P
from Authored A
WHERE 1 <
(Select count(*) as P
from Authored B
where A.pubId = B.pubId
AND A.authorId<> B.authorId)
Thanks to all who have answered.
Table
AuthorID pubID
1 2
3 2
4 2
10 1
11 1
Expected Result
AuthorID NumberOfOccurenceOfDiffAuthIDWithSamePubID
1 3
3 3
4 3
10 2
11 2
Updated using count() over():
Fiddle demo:
select a.AuthorId, count(*) over(partition by pubId) counts
from Authored a
order by a.AuthorId;
Please try below query for MS Sql server:
select
*,
COUNT(*) over (partition by pubId)
From Authored
where authorId<>pubId
Is this what you mean, its not clear what you are asking? Fiddle here.
select
count(ID) P,
PubID
from
(select distinct ID, pubID from Authored) d
group by
PubID
This will give you the number of distinct `ID/authorId` for each `PubID\pubId`