Select top 3 viewed images of every user from a table - mysql

I know this type of question has been asked multiple times but I am not able to find a good answer of my question.
I tried to fetch top 3 viewed records of every person from the table.
mytable
id user_id views
1 5 6
2 5 3
3 6 5
4 7 4
5 9 14
6 5 3
7 5 8
8 6 7
9 7 15
10 9 13
11 9 13
12 9 18
13 6 8
14 8 0
result
id user_id views
12 9 18
9 7 15
5 9 14
10 9 13
13 6 8
7 5 8
8 6 7
1 5 6
3 6 5
4 7 4
2 5 3
14 8 0
I followed this link http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
My Query
select *
from mytable
where (
select count(*) from mytable as f
where f.user_id = mytable.user_id and f.views >= mytable.views
) < 4 order by user_id desc;
But this query is not fetching those records where views are equal.
Please point me to the right direction.

Try this:
SELECT id, user_id, views
FROM (SELECT IF(#lastUserId = #lastUserId:=t.user_id, #ID:=#ID+1, #ID:=1) AS userNo,
t.id, t.user_id, t.views
FROM mytable t, (SELECT #ID:=0, #lastUserId:=0) A
ORDER BY t.user_id, t.views DESC
) AS A
WHERE userNo <= 3
ORDER BY views DESC
Check the SQL FIDDLE DEMO
OUTPUT
| ID | USER_ID | VIEWS |
|----|---------|-------|
| 12 | 9 | 18 |
| 9 | 7 | 15 |
| 5 | 9 | 14 |
| 10 | 9 | 13 |
| 7 | 5 | 8 |
| 13 | 6 | 8 |
| 8 | 6 | 7 |
| 1 | 5 | 6 |
| 3 | 6 | 5 |
| 4 | 7 | 4 |
| 2 | 5 | 3 |
| 14 | 8 | 0 |

Related

Get single unique row from duplicate data with multiple tables in mysql

I want to get the unique data from multiple tables with the highest id
Refer below data
Users Table
id firstname_english
------ --------------------
2 Lama
4 Akram
6 Ammar
8 Basil
10 Sami
12 Hasan
14 Adnan
16 Mamoon
18 Sulaiman
20 Wasfi
22 Mervat
Users form status with their id
id users_id status_id
------ ----------- -----------
3 2 10
368 4 10
5 4 10
402 6 9
7 6 10
9 8 10
11 10 10
223 10 10
13 12 10
3253 14 2
15 14 10
17 16 10
1488 16 9
231 16 10
19 18 10
22 20 10
750 20 9
232 22 10
24 22 10
2935 22 9
297 22 10
The Result I need
id firstname_english status_id form_id
------ -------------------- --------------------
2 Lama 10 3
4 Akram 10 368
6 Ammar 9 402
8 Basil 10 8
10 Sami 10 223
12 Hasan 10 12
........and so on
I need to display the highest data from the table 2 with the matching id of table 1
For MySql 8.0+ use row_number() window function:
select
u.id, u.firstname_english,
f.status_id, f.id form_id
from users u inner join (
select *,
row_number() over (partition by users_id order by id desc) rn
from users_form
) f on f.users_id = u.id
where f.rn = 1
See the demo.
For previous versions of MySql:
select
u.id, u.firstname_english,
f.status_id, f.id form_id
from users u inner join (
select uf.* from users_form uf
where not exists (
select 1 from users_form
where users_id = uf.users_id and id > uf.id
)
) f on f.users_id = u.id
See the demo.
Results:
| id | firstname_english | status_id | form_id |
| --- | ----------------- | --------- | ------- |
| 2 | Lama | 10 | 3 |
| 4 | Akram | 10 | 368 |
| 6 | Ammar | 9 | 402 |
| 8 | Basil | 10 | 9 |
| 10 | Sami | 10 | 223 |
| 12 | Hasan | 10 | 13 |
| 14 | Adnan | 2 | 3253 |
| 16 | Mamoon | 9 | 1488 |
| 18 | Sulaiman | 10 | 19 |
| 20 | Wasfi | 9 | 750 |
| 22 | Mervat | 9 | 2935 |

MySQL CTE - is it possible to iterate over range of numbers?

I'm curious if is that possible to do similar query using CTE (the values of "id" may vary, not necessarily in succession)
SELECT
elt((#mxId := if(#mxId + 1 <= 3, #mxId + 1, 1)), 5, 10, 22, 33) val,
id
FROM my_table
INNER JOIN (SELECT #mxId := 0) tmp;
Expected output:
val | id
-----+----
5 | 1
10 | 2
22 | 3
5 | 4
10 | 5
22 | 6
5 | 7
10 | 8
22 | 9
5 | 10
10 | 11
22 | 12
5 | 13
10 | 14
22 | 15
5 | 16
10 | 17
22 | 18
5 | 19
10 | 20
with my_table_numbered as (
select ((row_number() over ())-1)%3 as rn_mod, id from my_table
)
select elt(rn_mod+1, 5, 10, 22, 33) as val, id
from my_table_numbered;
Output:
+------+----+
| val | id |
+------+----+
| 5 | 1 |
| 10 | 2 |
| 22 | 3 |
| 5 | 4 |
| 10 | 6 |
| 22 | 7 |
| 5 | 8 |
| 10 | 9 |
| 22 | 13 |
| 5 | 14 |
| 10 | 15 |
| 22 | 16 |
+------+----+
You should specify an ORDER BY in the CTE to get a meaningful and reliable row numbering, but you didn't have one in the query in your question.
row_number() OVER ()
that's do the magic - Thanks very much

How to compare two column and how to get how get all rows?

I have one table. Here my question is how to find how many rows with same value compare two column.
id true_ans your_ans que_id
------+---------+--------+---------
1 | 2 | 2 | 1
2 | 5 | 4 | 3
3 | 8 | 9 | 4
4 | 14 | 14 | 6
5 | 19 | 19 | 7
6 | 21 | 22 | 9
Here i want find how many rows correct answer. Please reply me.
Do this :
SELECT COUNT(*) CorrectAnswers
FROM YourTable
WHERE
true_ans = your_ans

Mysql find the range with most elements [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have a simple mysql table with name and age columns. I need to find the age range (say with length 5) which contains the most number of records. Please note that the range can be from anything to anything (like 1 to 5 years or 2 to 6 years). I have created a sqlfiddle for the same at http://sqlfiddle.com/#!2/a65265/1
I have tried using DIV and searched through the forums, but the closest i can get is predefined ranges like age 5-10, 10-15 etc. I need a more generic solution for all possible age ranges.
select 5 * floor((t.age-o.offset)/5) + o.offset as from_age
,5 * (floor((t.age-o.offset)/5) + 1) + o.offset - 1 as to_age
,count(*) as cnt
from test as t
cross join ( select 0 as offset
union all select 1
union all select 2
union all select 3
union all select 4
) as o
group by o.offset
,floor((t.age-o.offset)/5)
order by cnt desc
limit 1
The basic idea -
Each row is being duplicated 5 times, with offset in the range of 0 to 4.
Each offset is causing a different distribution of the elements as described in the following diagrams:
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| | | | | | | | | | | | | | | | | | | |
------------- ------------- ------------- -------------
floor((x-0)/5): 0 1 2 3
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| | | | | | | | | | | | | | | | | | | |
- ------------- ------------- ------------- -------------
floor((x-1)/5): 0 1 2 3
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| | | | | | | | | | | | | | | | | | | |
---- ------------- ------------- ------------- -------------
floor((x-2)/5): 0 1 2 3
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| | | | | | | | | | | | | | | | | | | |
------- ------------- ------------- ------------- -------------
floor((x-3)/5): 0 1 2 3
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| | | | | | | | | | | | | | | | | | | |
---------- ------------- ------------- ------------- -------------
floor((x-4)/5): 0 1 2 3

Mysql optimization and explode

I have the following query that displays the top 10 most drawn pairs of numbers from the whole
table
select
p, count(p) as frequency
from
(SELECT
id,
CASE power1 <= power2 WHEN TRUE THEN CONCAT(power1,"-",power2) ELSE CONCAT(power2,"-",power1)
END p
FROM power
UNION
SELECT
id,
CASE power1<=power3 WHEN TRUE THEN CONCAT(power1,"-",power3) ELSE CONCAT(power3,"-",power1) END p
FROM power
UNION
SELECT
id,
CASE power1<=power4 WHEN TRUE THEN CONCAT(power1,"-",power4) ELSE CONCAT(power4,"-",power1) END p
FROM power
UNION
...............................................
SELECT
id,
CASE power19<=power20 WHEN TRUE THEN CONCAT(power19,"-",power20) ELSE CONCAT(power20,"-",power19)
END p
FROM power) as b
group by
p
order by
frequency desc, p asc
limit
0, 10
How can I impose a limit to take just the first 100 lines in descending order by ID? The query would be like this:
ORDER BY id LIMIT 0,100
But I haven't been able to adapt it for the above.
Could the code be optimized more than that?
power1, Power2 are values from tables.... would it work if i would have a string like 3,4,5,6 and then explode "," and after that power1 becomes 3, power2 to become 4, etc?
I mean the table format to look something like this :
table2
LATER EDIT :
I have table like this :
Table: data
+----+----+-----+
| id | nr | set |
+----+----+-----+
| 1 | 52 | 1 |
| 2 | 47 | 1 |
| 3 | 4 | 1 |
| 4 | 3 | 1 |
| 5 | 77 | 1 |
| 6 | 71 | 1 |
| 7 | 6 | 1 |
| 8 | 41 | 1 |
| 9 | 15 | 1 |
| 10 | 79 | 1 |
| 11 | 35 | 2 |
| 12 | 50 | 2 |
| 13 | 16 | 2 |
| 14 | 1 | 2 |
| 15 | 32 | 2 |
| 16 | 77 | 2 |
| 17 | 30 | 2 |
| 18 | 7 | 2 |
| 19 | 20 | 2 |
| 20 | 28 | 2 |
| .. | .. | ... |
+----+----+-----+
I have like 34360 id
And the following query :
SELECT
`n1`.`nr` AS `num_1`,
`n2`.`nr` AS `num_2`,
COUNT(1) AS `total`
FROM (select * from data ORDER BY id DESC limit 0,1000) AS `n1`
JOIN `data` AS `n2`
ON `n1`.`set` = `n2`.`set` AND `n1`.`nr` < `n2`.`nr`
GROUP BY `n1`.`nr`, `n2`.`nr`
ORDER BY `total` DESC
LIMIT 20
And is working fine !
I would like to know how i can find out the pairs of numbers that have not been drawn together for the longest time. Per example:
1,42 (together, as a pair) has not been drawn for 24 draws
32,45-as a pair as well-has not been drawn for 22 draws
etc
Consider the following:
Un-normalised:
id power1 power2 power3 power4
1 4 9 10 16
2 6 12 15 19
3 2 4 6 7
4 3 8 15 17
5 2 10 11 14
6 4 10 12 19
7 1 4 9 11
Normalised:
id power value
1 1 4
1 2 9
1 3 10
1 4 16
2 1 6
2 2 12
2 3 15
2 4 19
3 1 2
3 2 4
3 3 6
3 4 7
4 1 3
4 2 8
4 3 15
4 4 17
5 1 2
5 2 10
5 3 11
5 4 14
6 1 4
6 2 10
6 3 12
6 4 19
7 1 1
7 2 4
7 3 9
7 4 11
So...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL
,power INT NOT NULL
,value INT NOT NULL
,PRIMARY KEY(id,power)
);
INSERT INTO my_table VALUES
(1,1,4),(1,2,9),(1,3,10),(1,4,16),
(2,1,6),(2,2,12),(2,3,15),(2,4,19),
(3,1,2),(3,2,4),(3,3,6),(3,4,7),
(4,1,3),(4,2,8),(4,3,15),(4,4,17),
(5,1,2),(5,2,10),(5,3,11),(5,4,14),
(6,1,4),(6,2,10),(6,3,12),(6,4,19),
(7,1,1),(7,2,4),(7,3,9),(7,4,11);
SELECT LEAST(x.value,y.value)a -- LEAST/GREATEST is only necessary in the event that
, GREATEST(x.value,y.value) b -- power1 value may be greater than powerN value
, COUNT(*) freq
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.power < x.power
GROUP
BY LEAST(x.value, y.value) -- again only necessary if using LEAST/GREATEST above
, GREATEST(x.value,y.value)
ORDER
BY freq DESC
, a
, b;
+----+----+------+
| a | b | freq |
+----+----+------+
| 4 | 9 | 2 |
| 4 | 10 | 2 |
| 12 | 19 | 2 |
| 1 | 4 | 1 |
| 1 | 9 | 1 |
| 1 | 11 | 1 |
| 2 | 4 | 1 |
| 2 | 6 | 1 |
| 2 | 7 | 1 |
| 2 | 10 | 1 |
| 2 | 11 | 1 |
| 2 | 14 | 1 |
| 3 | 8 | 1 |
| 3 | 15 | 1 |
| 3 | 17 | 1 |
| 4 | 6 | 1 |
| 4 | 7 | 1 |
| 4 | 11 | 1 |
| 4 | 12 | 1 |
| 4 | 16 | 1 |
| 4 | 19 | 1 |
| 6 | 7 | 1 |
| 6 | 12 | 1 |
| 6 | 15 | 1 |
| 6 | 19 | 1 |
| 8 | 15 | 1 |
| 8 | 17 | 1 |
| 9 | 10 | 1 |
| 9 | 11 | 1 |
| 9 | 16 | 1 |
| 10 | 11 | 1 |
| 10 | 12 | 1 |
| 10 | 14 | 1 |
| 10 | 16 | 1 |
| 10 | 19 | 1 |
| 11 | 14 | 1 |
| 12 | 15 | 1 |
| 15 | 17 | 1 |
| 15 | 19 | 1 |
+----+----+------+
While I fully agree with #Strawberry about normalising your data, the following is an example of how to possibly do it with your current data structure (not tested).
SELECT CASE a.power_val <= b.power_val WHEN TRUE THEN CONCAT(a.power_val,"-",b.power_val) ELSE CONCAT(b.power_val,"-",a.power_val) END p,
COUNT(a.id) as frequency
FROM
(
SELECT id,1 AS power_col, power1 AS power_val FROM power UNION
SELECT id,2, power2 FROM power UNION
SELECT id,3, power3 FROM power UNION
SELECT id,4, power4 FROM power UNION
SELECT id,5, power5 FROM power UNION
SELECT id,6, power6 FROM power UNION
SELECT id,7, power7 FROM power UNION
SELECT id,8, power8 FROM power UNION
SELECT id,9, power9 FROM power UNION
SELECT id,10, power10 FROM power UNION
SELECT id,11, power11 FROM power UNION
SELECT id,12, power12 FROM power UNION
SELECT id,13, power13 FROM power UNION
SELECT id,14, power14 FROM power UNION
SELECT id,15, power15 FROM power UNION
SELECT id,16, power16 FROM power UNION
SELECT id,17, power17 FROM power UNION
SELECT id,18, power18 FROM power UNION
SELECT id,19, power19 FROM power UNION
SELECT id,20, power20 FROM power
ORDER BY id DESC
LIMIT 2000
) a
INNER JOIN
(
SELECT id, 1 AS power_col, power1 AS power_val FROM power UNION
SELECT id, 2, power2 FROM power UNION
SELECT id,3, power3 FROM power UNION
SELECT id,4, power4 FROM power UNION
SELECT id,5, power5 FROM power UNION
SELECT id,6, power6 FROM power UNION
SELECT id,7, power7 FROM power UNION
SELECT id,8, power8 FROM power UNION
SELECT id,9, power9 FROM power UNION
SELECT id,10, power10 FROM power UNION
SELECT id,11, power11 FROM power UNION
SELECT id,12, power12 FROM power UNION
SELECT id,13, power13 FROM power UNION
SELECT id,14, power14 FROM power UNION
SELECT id,15, power15 FROM power UNION
SELECT id,16, power16 FROM power UNION
SELECT id,17, power17 FROM power UNION
SELECT id,18, power18 FROM power UNION
SELECT id,19, power19 FROM power UNION
SELECT id,20, power20 FROM power
ORDER BY id DESC
LIMIT 2000
) b
ON a.id = b.id
AND a.power_col != b.power_col
GROUP BY p
ORDER BY frequency DESC, p ASC
LIMIT 0,10
Note using normalised data structures would likely be far quicker.
EDIT
Think something like the following might give you what you need.
The big sub query is to get every possible combination (idea is to also cope with pairs that have never been used), with the first number being smaller than the 2nd just for consistency. This is then joined against the tables of data to get the matching numbers and the respective id fields. Then uses MIN to get the smallest id:-
SELECT all_combo.num_1,
all_combo.num_2,
MIN(d1.id)
FROM
(
SELECT sub0.nr AS num_1,
sub1.nr AS num_2
FROM
(
SELECT DISTINCT nr
FROM data
) sub0
INNER JOIN
(
SELECT DISTINCT nr
FROM data
) sub1
WHERE sub0.nr < sub1.nr
) all_combo
LEFT OUTER JOIN data d1 ON all_combo.num_1
LEFT OUTER JOIN data d2 ON all_combo.num_2 AND d1.set = d2.set
GROUP BY all_combo.num_1,
all_combo.num_2