MySQL multiple SubQueries on same table - mysql

I got a table votes that indicates me if a user voted for a specific movie. It also shows me how many movies a user has voted for.
id_film | id_user | voting
----------------------------
1 | 1 | 7
1 | 33 | 5
3 | 1 | 9
4 | 7 | 7
4 | 2 | 8
4 | 1 | 6
6 | 1 | 6
... | ... | ...
I want to get a list of id_film's which are related to id_user's in this way:
Get all id_film's from a specific id_user like
SELECT id_film FROM votes WHERE id_user = 1
Grab every id_user which is related
SELECT DISTINCT v.user FROM votes v WHERE id_film IN ( id_film's )
Then SELECT id_film's FROM votes v WHERE user IN ( "user list from previous query" ) except id_film's from first query.
This was my first attempt:
SELECT id_film, film.title, film.originaltitle, COUNT(*)
FROM votes v
INNER JOIN film ON v.id_film = film.id
WHERE user IN
(
SELECT DISTINCT v.user
FROM votes v
WHERE id_film IN
(
SELECT id_film
FROM votes v
WHERE user = 1
)
)
AND
id_film NOT IN
(
SELECT id_film
FROM votes v
WHERE user = 1
)
GROUP BY id_film
It doesn't work. MySQL took too long for a result and I restarted XAMPP.
So I tried another SELECT, this time with JOINS:
SELECT DISTINCT v.id_film AS vFilm, v1.user AS v1User, v2.id_film AS v2Film
FROM votes v
LEFT OUTER JOIN votes v1 ON v1.id_film = v.id_film
LEFT OUTER JOIN votes v2 ON v1.user = v2.user
WHERE v.user = 1
AND v1.user != 1
AND v2.id_film NOT
IN
(
SELECT id_film
FROM votes
WHERE user = 1
)
GROUP BY v2.id_film
Also doesn't work, but when I tried it without the NOT IN condition in the end it works! (It took appr. 13 sec.) :-(
Here is the working query.
SELECT DISTINCT v2.id_film AS v2Film
FROM votes v
LEFT OUTER JOIN votes v1 ON v1.id_film = v.id_film
LEFT OUTER JOIN votes v2 ON v1.user = v2.user
WHERE v.user = 1
AND v1.user != 1
With Output
v2Film
---------
1
13
14
58
4
...
But this query doesn't except id_film's from first query.
Because I know that user 1 already voted for id_film 1.
So, am I totally wrong with my logic or is my code too complex for this?

Related

How to prevent duplication on the second table Using inner Joins and the Count

SELECT T.column_11,
count(column_11) count
FROM wp_tablesome_table_4695 T
JOIN wp_fea_submissions S ON T.column_2 = S.title
group BY T.column_11
wp_tablesome_table_4695
column 2 | column 11
1 | location 1
2 | location 2
3 | Location 3
wp_fea_submissions
title
1
1
2
3
Result
column 11| count
1 | 2
2 | 1
3 | 1
The count result is also counting the duplicate S.Title how to prevent counting the Duplicate
The count need to be done inside a subquery.
SELECT T.column11,
count(column11) count
FROM wp_tablesome_table_4695 T
INNER JOIN (select distinct title
from wp_fea_submissions
) S ON T.column2 = S.title
group BY T.column11;
Or
with cte as ( select distinct title
from wp_fea_submissions
) select wp.column11, count(wp.column11) as cnt
from cte
inner join wp_tablesome_table_4695 wp on wp.column2=cte.title
group by wp.column11;
https://dbfiddle.uk/iVprcr8z

Finding the next available value in MySQL

I have a database table products with the following columns.
ID | segment_key | segment_value
1 | Mo | 1
2 | Mo | 3
4 | Jo | 1
5 | Jo | 2
6 | Ta | 1
For any given key I need to find the next available segment_value for me to record in the same table.
ie. for the following segment_key list, the expected outputs are
Mo -> 2
Jo -> 3
Ta -> 2
Ji -> 1
I tried the solution mentioned here but I cannot seem to get the right output.
This is my failed attempt.
SELECT t1.segment_value
FROM products t1
WHERE NOT EXISTS (
SELECT *
FROM products t2
WHERE t2.segment_value = t1.segment_value + 1 and t2.segment_key='Mo' and t2.is_active=1
)
LIMIT 1
You can try to use CTE RECURSIVE to get the gap of all values. then do CROSS JOIN fill in the gap of value from each segment_key.
Final using OUTER JOIN and filter segment_key IS NULL which represent the gap of values
Query #1
WITH RECURSIVE CTE AS(
SELECT MIN(segment_value) val,MAX(segment_value) + 1 max_val
FROM products
UNION ALL
SELECT val + 1 ,max_val
FROM CTE c
WHERE val + 1 <= max_val
)
SELECT c.segment_key,MIN(val) segment_value
FROM (
SELECT DISTINCT val,segment_key
FROM CTE
CROSS JOIN products
) c
LEFT JOIN products p
ON c.val = p.segment_value AND c.segment_key = p.segment_key
WHERE p.segment_key IS NULL
GROUP BY c.segment_key;
segment_key
segment_value
Mo
2
Jo
3
Ta
2
View on DB Fiddle

Select top X records for each group or default

I have the following schema:
users:
id email
1 'user.one#test.com'
2 'user.two#test.com'
video_group:
id title
1 'Group 1'
2 'Group 2'
videos:
id group_id rank title
1 1 1 'Group 1 - Video 1'
2 1 2 'Group 1 - Video 2'
3 2 1 'Group 2 - Video 1'
user_video_play_times:
video_id user_id time last_update
2 1 12 01-02-2018
1 1 120 01-01-2018
I need to get the time, user_id, video_id, and group_id of the last video played by a user in specific groups, but if there's no records on user_video_play_times for a group, the video with the lowest rank should be returned. For example:
user_id group_id video_id time
1 1 2 12 -- user.one + group 1
1 2 3 0 -- user one + group 2
This is the query I have so far:
SELECT
pt.user_id user_id,
v.id video_id,
g.id group_id,
pt.time time
FROM
videos v
INNER JOIN video_groups g ON g.id = v.group_id
LEFT JOIN user_video_play_times pt ON
pt.video_id = v.id AND
pt.user_id = 1
LEFT JOIN (
SELECT
g.id AS g_id,
MAX(pt.last_update) AS pt_last_update
FROM
user_video_play_times pt
INNER JOIN videos v ON v.id = pt.video_id
INNER JOIN video_groups g ON g.id = v.group_id
WHERE
pt.user_id = 1 AND
g.id IN (1, 2)
GROUP BY
g.id
) lpt ON lpt.g_id = g.id AND lpt.pt_last_update = pt.last_update
WHERE
g.id IN (1, 2)
GROUP BY
g.id
It is sort of working, but...
Adding v.title to the column selection messes the results for some reason, making everything return only videos with rank 1. Any idea why?
Could this query be optimized, or is there another slicker way to achieve the same results?
Any help with this is really appreciated!
DB fiddle here
Update 1:
This issue seems to only happen when the column os of type text.
Since your db<>fiddle is for MariaDB version 10.3; I am presuming that you have Window Functions available.
We can use Row_number() function over a partition of group_id to get row number values, as per the defined rules. Video with latest last_update value will have Row number of 1 and so on. If there is no video played, then the one with least value of Rank will have Row number = 1.
We can use this result-set as a Derived Table, and consider only those rows where Row number = 1.
SELECT
dt.user_id,
dt.group_id,
dt.video_id,
dt.video_title,
dt.time
FROM
(
SELECT
pt.user_id AS user_id,
g.id AS group_id,
v.id AS video_id,
v.title AS video_title,
pt.time AS time,
ROW_NUMBER() OVER(PARTITION BY v.group_id
ORDER BY pt.last_update DESC,
v.`rank` ASC) AS row_num
FROM videos AS v
INNER JOIN video_groups AS g
ON g.id = v.group_id AND
g.id IN (1,2)
LEFT JOIN user_video_play_times AS pt
ON pt.video_id = v.id AND
pt.user_id = 1
) AS dt
WHERE dt.row_num = 1
View on DB Fiddle
Result:
| user_id | group_id | video_id | video_title | time |
| ------- | -------- | -------- | ----------------- | ---- |
| 1 | 1 | 2 | Group 1 - Video 2 | 12 |
| | 2 | 3 | Group 2 - Video 1 | |
PS: Note that Rank is a Reserved Keyword, and you should really avoid using it as column/table name.

MySQL INNER JOIN from second table (TOP10)

$stmt = $conn->prepare('SELECT a.*, c.*, SUM(a.money+b.RESULT) AS ARESULT
FROM users a
INNER JOIN bankaccounts c
ON a.id = c.owner
INNER JOIN
(
SELECT owner, SUM(amount) AS RESULT
FROM bankaccounts
GROUP BY owner
) b ON a.id = b.owner
ORDER BY ARESULT DESC LIMIT 10');
What's problem, it show wrong only one record? I want list max 10 records - like TOP 10 richest who has [money+(all his bankaccounts amount)]
Lets say.. I have 2 tables.
Table: users
ID | username | money
1 | richman | 500
2 | richman2 | 600
Table: bankaccounts
ID | owner | amount
65 | 1 | 50
68 | 1 | 50
29 | 2 | 400
So it would list:
richman2 1000$
richman 600$
Try using a subqueries...
$stmt = $conn->prepare('SELECT a.*,
IFNULL((SELECT SUM(amount) FROM bankaccounts b WHERE b.owner=a.id),0) AS BANK_MONEY,
(IFNULL(a.money,0) + IFNULL((SELECT SUM(amount) FROM bankaccounts c WHERE c.owner=a.id),0)) AS ARESULT
FROM users a
ORDER BY ARESULT DESC LIMIT 0, 10');
EDIT: Added a field for bank account totals
EDIT2: Added IFNULL to SQL statement in case user is not in BankAccounts table
Try this:
SELECT a.*, (a.money + b.RESULT) AS ARESULT
FROM users a
INNER JOIN (SELECT owner, SUM(amount) AS RESULT
FROM bankaccounts
GROUP BY owner
) b ON a.id = b.owner
ORDER BY ARESULT DESC
LIMIT 10

How to join these two tables?

I have:
TABLE: USERS
UID | NAME
1 | Bob
2 | John
And I have:
TABLE: HITS
HITID | UID
1 | 1
2 | 2
3 | 2
4 | 1
5 | 2
6 | 2
I want:
UID | HITS
1 | 2
2 | 4
Seems simple enough, but I can't seem to do it?
Try this:
SELECT UID, COUNT(UID) HITS FROM HITS
GROUP BY UID;
This Might help you
DECLARE #USERS TABLE(UID INT, NAME VARCHAR(20))
INSERT INTO #USERS (UID,NAME) VALUES ('1','Bob'),('2','John')
DECLARE #HITS TABLE(HITID INT,UID INT)
INSERT INTO #HITS (HITID,UID) VALUES('1','1'),('2','2'),('3','2'),('4','1'),('5','2'),('6','2')
Use JOIN if you want to use Both USERS Table and HITS Table
SELECT U.UID,COUNT(H.HITID) AS HITS FROM #USERS AS U INNER JOIN #HITS AS H ON U.UID = H.UID GROUP BY U.UID
OR Use Simple Query If you want to use only HITS Table
SELECT UID, COUNT(UID) HITS FROM #HITS GROUP BY UID
I also Created Temp tables in this.
SELECT b.UID, COUNT(b.UID) HITS FROM HITS a, USERS b
WHERE a.UID=b.UID
GROUP BY UID
This should work
This would do the trick
SELECT H.UID, COUNT(*) AS 'HITS'
FROM HITS H
GROUP BY H.UID
if you want to have the name of the users, then you need to join it
SELECT a.Name, COUNT(*) totalCount
FROM users a
INNER JOIN HITS b
ON a.UID = b.UID
GROUP BY a.UID
SQLFiddle Demo