Show only 2 softwares for each category - mysql

I have a table software (id_software, software_name, category)
What SQL query to show only 2 software for each category ?
For example I want to get :
|id_software | software_name | category|
-+------------+---------------+---------+-
| 1 | Photoshop | 5 |
| 2 | illustrator | 5 |
| 3 | Firefox | 1 |
| 4 | I.E | 1 |
-+--------------------------------------+-

select * from
(select *
from table1 n
where
( select count(*)
from table1 m
where n.categorie = m.categorie
and n.id_software <= m.id_software) <= 2
order by n.id_software, n.id_software desc) as tn
Source: http://mindbuffer.wordpress.com/2013/07/09/mysql-get-the-top-2-rows-for-each-category-with-certain-condition/

You could use the row_number function for this.
SELECT *
FROM
(SELECT
Category,
id_software,
software_name,
[Nth_Software] = ROW_NUMBER() OVER (Partition by Category ORDER BY Id_software)
FROM
table
) T
WHERE
T.Nth_Software <=2
This gives you the first two software entries based on softwareId for every category.

Related

Get the last know value for a specific reference in inconsistent timeline

I'm trying to get records from the stats table and if there is no data for the specific day and reference get the latest known value for a given ref.
stats table:
| ref | date | views |
| --- | -------- | ----- |
| 1 |2022-01-01|1 |
| 2 |2022-01-01|1 |
| 1 |2022-01-02|2 |
| 2 |2022-01-02|1 |
| 1 |2022-01-03|2 |
| 1 |2022-01-04|3 |
| 2 |2022-01-04|3 |
As you see above there the record for ref 2 at 2022-01-03 is missing.
Now I want to sum views for those records and group them by the ref column and since there is one missing record for ref 2 the value for the summation should be taken from the latest record (2022-01-02).
I have also the posts table:
| id | title |
| --- | --------- |
| 1 |title no. 1|
| 2 |title no. 2|
Also, I have to create a timeline from the oldest stat to the current date.
What do I have is:
WITH RECURSIVE timeline (
date
) AS (
SELECT
MIN(date)
FROM
stats
UNION ALL
SELECT
DATE_ADD(date, INTERVAL 1 day)
FROM
timeline
WHERE (timeline.date < CURRENT_DATE),
posts_days AS (
SELECT timeline.date, posts.id
FROM
posts
CROSS JOIN timeline
),
view_stats AS (
SELECT posts_days.date, posts_days.id, stats.views
FROM
posts_days
LEFT JOIN stats ON (stats.ref = posts_days.id and stats.date = posts_days.date)
)
SELECT
view_stats.date, SUM(view_stats.views) AS views
-- ,SUM(prev_stats.views) AS prev_views,
FROM view_stats
LEFT JOIN (
SELECT id, (view_stats.views) as views, date FROM view_stats GROUP BY id, date
) as prev_stats on prev_stats.date = (
SELECT date FROM view_stats s1
WHERE s1.date < view_stats.date and s1.id = view_stats.id ORDER BY date desc limit 1
) and prev_stats.id = view_stats.id
GROUP BY date
ORDER BY date
But it obviously behaves in the wrong way.
I would be appreciated any tips on how to solve this one.

How to retrieve one random row for each category?

I would like to retrieve one random row for each user_group. How can I do this ?
Here find 2 tables, user and user_group.
user :
id
firstname
user_group_id
user_group :
id
label
Data
users
1 | Thor | 1
2 | Iron man | 2
3 | Hulk | 3
4 | Groot | 3
user_groups
1 | admin
2 | support
3 | user
Results expected
1 | Thor | admin
2 | Iron man | support
4 | Groot | user (or 3 | Hulk | user)
The answer using GROUP BY will conflict with the default SQL mode in MySQL 5.7 and later, which makes it an error to reference columns in the select-list that are neither in the GROUP BY, nor in an aggregate function.
The solution in MySQL 8.0 is to use window functions:
SELECT r.id, r.firstname, r.user_group_id, g.label
FROM (
SELECT id, firstname, user_group_id,
ROW_NUMBER() OVER (PARTITION BY user_group_id ORDER BY RAND()) AS rownum
FROM users
) AS r
JOIN user_groups AS g ON (r.user_group_id = g.id)
WHERE r.rownum = 1
Re your comment:
SELECT r.id, r.firstname, r.user_group_id, r.count, g.label
FROM (
SELECT id, firstname, user_group_id,
ROW_NUMBER() OVER (PARTITION BY user_group_id ORDER BY RAND()) AS rownum,
COUNT(*) OVER (PARTITION BY user_group_id) AS count
FROM users
) AS r
JOIN user_groups AS g ON (r.user_group_id = g.id)
WHERE r.rownum = 1
Result:
+------+-----------+---------------+-------+---------+
| id | firstname | user_group_id | count | label |
+------+-----------+---------------+-------+---------+
| 1 | Thor | 1 | 1 | admin |
| 2 | Iron Man | 2 | 1 | support |
| 3 | Hulk | 3 | 2 | user |
+------+-----------+---------------+-------+---------+
You can use aggregate functions over windows. Read https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html for details on this.
A solution that can work with MySQL 5.7 and later
You can create a temporary table from users using ORDER BY rand() to order your users randomly.
Then you can user GROUP BY with ANY_VALUE() in that temporary table to get the id of a random user from that group and just JOIN with your tables to get the other data you want.
You can see a working example here:
https://www.db-fiddle.com/f/jtjPDxFVyzh4zGjXNbLAiL/5
CREATE TEMPORARY TABLE temp_tbl
SELECT * FROM `users` ORDER BY rand() ;
SELECT u.id,u.firstname,g.label FROM
(
SELECT ANY_VALUE(id) AS id FROM temp_tbl GROUP BY user_group_id
) t
INNER JOIN `users` u ON t.id=u.id
INNER JOIN `user_groups` g ON g.id=u.user_group_id

Getting the most recent row and linking it with another table?

Im trying to get the most recent row of a table
user_quiz:
+--------+-----------+-------------+-------------------+------------+
|quiz_id |userid | module_id |number_of_questions| user_score |
+--------+-----------+-------------+-------------------+-------- ---+
| 1 | 1 | 1 | 5 | 5 |
| 2 | 2 | 2 | 10 | 9 |
| 3 | 1 | 1 | 10 | 9 |
+--------+-----------+-------------+-------------------+------------+
I have used the query:
SELECT * FROM user_quiz WHERE userid = 1 ORDER BY quiz_id DESC LIMIT 1
which correctly retrieves the last row.
However I want to link the module_id with another table:
module:
+---------+------------+
|module_id|module_name |
+---------+------------+
| 1 | Forces |
| 2 | Electricity|
+---------+------------+
And retrieve the module name.
The result of the query will be used to print out the users most recent quiz:
Most recent quiz: Forces - Number of questions: 10 - User Score: 9
Is this possible using just one query?
You just need a JOIN:
SELECT uq.*, m.module_name
FROM user_quiz uq JOIN
modules m
ON uq.module_id = m.module_id
WHERE uq.userid = 1
ORDER BY uq.quiz_id DESC
LIMIT 1;
A more simple query to achieve the same would be
SELECT
user_quiz.quiz_id,
user_quiz.number_of_questions,
user_quiz.user_score,
modules .module_name
FROM user_quiz JOIN modules
ON user_quiz.module_id = modules.module_id
WHERE user_quiz.userid = 1
ORDER BY user_quiz.quiz_id DESC
LIMIT 1
If you want to get the same results for all the users, you could use a bit more sophisticated query
SELECT
user_quiz_virtual_table.userid,
user_quiz_virtual_table.quiz_id,
user_quiz_virtual_table.number_of_questions,
user_quiz_virtual_table.user_score,
modules.module_name
FROM (
SELECT
user_quiz.userid
user_quiz.quiz_id,
user_quiz.module_id
user_quiz.number_of_questions,
user_quiz.user_score
FROM user_quiz
ORDER BY user_quiz.quiz_id DESC
GROUP BY userid
) AS user_quiz_virtual_table
JOIN modules ON user_quiz_virtual_table.module_id = modules.module_id

How can I identify if a certain row is the last by using an ID in MySQL

Sorry to confuse you about my title. I am building an auction system and I am having a difficulty in getting the user's winning item.
Example I have a table like this:
the columns are:
id, product_id, user_id, status, is_winner, info, bidding_price, bidding_date
here's my sql fiddle:
http://sqlfiddle.com/#!9/7097d/1
I want to get every user's item that they already win. So I need to identify if they are the last who bid in that item.
I need to filter it using a user_id.
If I do a query like this:
SELECT MAX(product_id) AS product_id FROM auction_product_bidding
WHERE user_id = 3;
it will get only the product_id that is 12 and the product_id of 9 did not get. Product ID 9 is also that last bid of the user_id 3.
Can you help me? I hope you got my point. Thanks. Sorry if my question a little bit confusing.
According to your question, seems 11 is also what you want, try this query:
SELECT apd.product_id
FROM auction_product_bidding apd
JOIN (
SELECT MAX(bidding_date) AS bidding_date, product_id
FROM auction_product_bidding
GROUP BY product_id
) t
ON apd.product_id = t.product_id
AND apd.bidding_date = t.bidding_date
WHERE apd.user_id = 3;
Check Demo Here
select id,product_id,user_id,status,is_winner,info,bidding_price,bidding_date,rank
from
( SELECT apb.*,
greatest(#rank:=if(product_id=#prodGrp,#rank+1,1),-1) as rank,
#prodGrp:=product_id as dummy
FROM auction_product_bidding apb
cross join (select #prodGrp:=-1,#rank:=0) xParams
order by product_id,bidding_date DESC
) xDerived
where user_id=3 and rank=1;
That user won 9,11,12
+----+------------+---------+--------+-----------+------+---------------+---------------------+------+
| id | product_id | user_id | status | is_winner | info | bidding_price | bidding_date | rank |
+----+------------+---------+--------+-----------+------+---------------+---------------------+------+
| 60 | 9 | 3 | | 0 | | 75000.00 | 2016-08-02 16:31:23 | 1 |
| 59 | 11 | 3 | | 0 | | 15000.00 | 2016-08-02 12:04:16 | 1 |
| 68 | 12 | 3 | | 0 | | 18000.00 | 2016-08-10 09:20:01 | 1 |
+----+------------+---------+--------+-----------+------+---------------+---------------------+------+
SELECT product_id FROM auction_product_bidding where bidding_price= any
(select max(bidding_price) from auction_product_bidding group by product_id)
and user_id='3';
select * from
(select product_id,user_id,max(bidding_price) from
(select * from auction_product_bidding order by bidding_price desc) a
group by product_id) b
where user_id=3;
Answer:
product_id user_id max(bidding_price)
9 3 75000
11 3 15000
12 3 18000
An idea could be to sort the table desc by date and select every distinct row by product_id and customer_id. Something like
SELECT DISTINCT prod_id, user_id FROM (
SELECT * FROM auction_product_bidding ORDER BY date DESC
)
You want everything that bids last in 3, is it right ?

Get second highest values from a table

I have a table like this:
+----+---------+------------+
| id | conn_id | read_date |
+----+---------+------------+
| 1 | 1 | 2010-02-21 |
| 2 | 1 | 2011-02-21 |
| 3 | 2 | 2011-02-21 |
| 4 | 2 | 2013-02-21 |
| 5 | 2 | 2014-02-21 |
+----+---------+------------+
I want the second highest read_date for particular 'conn_id's i.e. I want a group by on conn_id. Please help me figure this out.
Here's a solution for a particular conn_id :
select max (read_date) from my_table
where conn_id=1
and read_date<(
select max (read_date) from my_table
where conn_id=1
)
If you want to get it for all conn_id using group by, do this:
select t.conn_id, (select max(i.read_date) from my_table i
where i.conn_id=t.conn_id and i.read_date<max(t.read_date))
from my_table t group by conn_id;
Following answer should work in MSSQL :
select id,conn_id,read_date from (
select *,ROW_NUMBER() over(Partition by conn_id order by read_date desc) as RN
from my_table
)
where RN =2
There is an intresting article on use of rank functions in MySQL here : ROW_NUMBER() in MySQL
If your table design as ID - date matching (ie a big id always a big date), you can group by id, otherwise do the following:
$sql_max = '(select conn_id, max(read_date) max_date from tab group by 1) as tab_max';
$sql_max2 = "(select tab.conn_id,max(tab.read_date) max_date2 from tab, $sql_max
where tab.conn_id = tab_max.conn_id and tab.read_date < tab_max.max_date
group by 1) as tab_max2";
$sql = "select tab.* from tab, $sql_max2
where tab.conn_id = tab_max2.conn_id and tab.read_date = tab_max2.max_date2";