get 1st and 2nd highest vlaue rows in case of similar values - mysql

I have a table with the columns : id, status, value.
id status value
-- ------ -----
1 10 100
2 10 100
3 10 60
4 11 20
5 11 15
6 12 100
7 12 50
8 12 50
I would like to get the id and value of the first and second highest valued rows, from each status group. My table should have the following columns:
status, id of the first highest value, first highest value, id of second highest value, second highest value.
I should get:
status 1stID 1stValue 2ndID 2ndValue
------ ----- -------- ----- --------
10 1/2 100 2/1 100
11 4 20 5 15
12 6 100 7/8 50
I tried all kinds of solutions, but I couldn't find a solution for same-value 1st s (two rows with the same value, which happened to be the highest in that status group) or same-value seconds.
For example, in case of two rows sharing the highest value in their status group, this not-so-elegant query will return two rows with the same status, different 1sts and same 2nd:
SELECT 2nds.status, 1sts.id AS "1stID",1sts.value AS "1stValue",
2nds.id AS "2ndID",2nds.value AS "2ndValue"
FROM
(SELECT v.* FROM
(SELECT status, MAX(value) AS "SecMaxValue" FROM table o
WHERE value < (SELECT MAX(value) FROM table
WHERE status = o.status
GROUP BY status) AS m
INNER JOIN table v
ON v.status = m.status AND v.value = m.SecMaxValue) AS 2nds
INNER JOIN
(SELECT v.* FROM
(SELECT status, MAX(value) AS maxValue FROM table
GROUP BY status) AS m
INNER JOIN table v
ON v.status = m.status AND v.value = m.MaxValue) AS 1sts
ON 1sts.status = 2nds.status ;
This query will give me:
status 1stID 1stValue 2ndID 2ndValue
------ ----- -------- ----- --------
10 1 100 3 60
10 2 100 3 60
11 4 20 5 15
12 6 100 7 50
12 6 100 8 50
To conclude, I would like to find a solution in which:
a. if there are two rows with the highest value the query puts the details one of them in the column of the 1st and the details of other in 2nd (no mather which)
b. if there are two rows with the second highst value it puts the highest in its place and one of the seconds in the second place.
Is there a way to change the query above? someone has a nicer solution?
I came across several 1st and 2nd queries but they had the same problem - for example this solution: Finding the highest n values of each group in MySQL. it does not deliver 1st and 2nd in the same row, but the main problem it provides only one of the firsts.
Thanks

After spent a lot of time, finally I found a solution for above problem. Please try it out:
select 1st.status as Status,
SUBSTRING_INDEX(1st.id,'/',1) as 1stID,
1st.value as 1stValue,
(case when locate('/',1st.id) > 0 then SUBSTRING_INDEX(1st.id,'/',-1)
else 2nd.id
end) as 2ndID,
(case when locate('/',1st.id) > 0 then 1st.value
else 2nd.value
end) as 2ndValue
from
(
(select status, SUBSTRING_INDEX(Group_concat(id separator '/'),'/',2) as id,value
from t1
where (status,value) in (select status,value
from t1
group by status
having max(value))
group by status) 1st
inner join
(select status,id,value
from t1
where (status,value) not in (select status,value
from t1
group by status
having max(value))
group by status,value
order by status,value desc) 2nd
on 1st.status = 2nd.status)
group by 1st.status;
Just replace t1 with your tablename and it should work like a charm.
Click here for Updated Demo
If you have any doubt(s), feel free to ask.
Hope it helps!

Related

how to select row with max value, while excluding the newest added row

I have a table with user ids and the total spend for every order the user has placed. I need to find the highest total spend for every order excluding their latest order.
I'm not sure how to drop their last order without purchase dates- the table is in ascending order so the last row would be the latest purchase.
user_id total_spend
1 234
2 123
3 56
1 453
5 560
1 232
2 345
3 210
ideal output
user_id total_spend
1 453
2 123
3 56
select user_id, max(total_spend)
from t
group by user_id
does anyone have any ideas of how to solve this?
Assuming you have an extra column such as id of integer type,which determines the row adding order, and starting from upside with 1 and incrementing by 1 upto 8th row in your illustration, then you can use this query :
select t1.user_id, max(t1.total_spend)
from tab t1
join
(
select max(id) as id, user_id
from tab
group by user_id ) t2
on t1.id < t2.id
and t1.user_id = t2.user_id
group by t1.user_id;
which filters out the latest id columns first, and then gets maximum total spend money grouped by the rest of user ids.
Demo

configure query to bring rows which have more than 1 entries

How to get those entries which have more than 1 records?
If it doesn't make sense... let me explain:
From the below table I want to access the sum of the commission of all rows where type is joining and "they have more than 1 entry with same downmem_id".
I have this query but it doesn't consider more entries scenario...
$search = "SELECT sum(commission) as income FROM `$database`.`$memcom` where type='joining'";
Here's the table:
id mem_id commission downmem_id type time
2 1 3250 2 joining 2019-09-22 13:24:40
3 45 500 2 egbvegr new time
4 32 20 2 vnsjkdv other time
5 23 2222 2 vfdvfvf some other time
6 43 42 3 joining time
7 32 353 5 joining time
8 54 35 5 vsdvsdd time
Here's the expected result: it should be the sum of the id no 2, 7 only
ie. 3250+353=whatever.
It shouldn't include id no 6 because it has only 1 row with the same downmem_id.
Please help me to make this query.
Another approach is two levels of aggregation:
select sum(t.commission) income
from (select sum(case when type = 'joining' then commission end) as commission
from t
group by downmem_id
having count(*) > 1
) t;
The main advantage to this approach is that this more readily supports more complex conditions on the other members of each group -- such as at most one "joining" record or both "joining" records and no more than two "vnsjkdv" records.
Use EXISTS:
select sum(t.commission) income
from tablename t
where t.type = 'joining'
and exists (
select 1 from tablename
where id <> t.id and downmem_id = t.downmem_id
)
See the demo.
Results:
| income |
| ----- |
| 3603 |
You can use subquery that will find all downmem_id having more than one occurrence in the table.
SELECT Sum(commission) AS income
FROM tablename
WHERE type = 'joining'
AND downmem_id IN (SELECT downmem_id
FROM tablename t
GROUP BY downmem_id
HAVING Count(id) > 1);
DEMO

Previous records in mysql

id game_id user_id user_playing_status user_turn_status
1 1 2 1 1
2 1 4 1 0
3 1 6 1 0
How can we access previous record of current record ?
If there are three records r1,r2,r3 in table
so previous record of r2 should be r1 , r3 should be r1 and r1 should be r3.
I use the following query
select user_id
from current_playing_users
where id < (select id from current_playing_users where user_id = 2)
But i am not getting previous record of first record.I want to get records in anticlockwise manner. Like in image previous user_id of 2 should be 6
Are looking you for the correlated subquery ? :
select cu.*.
(select cu1.user_id
from current_playing_users cu1
where cu1.id < cu.id
order by cu1.id desc
limit 1
) as prev_user_id
from current_playing_users cu;
you could use LAG function
select LAG(user_id) over (order by {your desired order}) as previous_row
from ..
LAG function return the former cell at the desired column with a pre-defined order.

Count() return total instead distinct count

I have qualified risks with description and creation date, who are attached to subcategory of risks this last ones are attached to category of risks, each risk has a name like 'Risk_1' , my aim is to count the number of risks by month and risk category including zero.
I have this request :
SELECT DISTINCT risk_names.type as risk_name, MONTH(risk.creation_date) as month, count(risk.id) as number FROM risk As risk , risk_category
JOIN (
SELECT risk_category.name as type
FROM
risk_category
) as risk_names on risk_names.type = risk_category.name
where risk.creation_date >= (NOW()-INTERVAL 3 MONTH) GROUP BY MONTH(risk.creation_date), risk_names.type;
Who return this result :
Risk_name month number
---------------------------------
Risk_1 1 10 ---> instead 8
Risk_2 1 10 ---> instead 1
Risk_3 1 10 ---> instead 1
Risk_1 2 12 ......
Risk_2 2 12
Risk_3 2 12
Risk_1 12 4
Risk_2 12 4
Risk_3 12 4
As you can see the number returned is the total for each month , but my aim is to get total for each distinct risk.
Can you help me . thanks
The comma in your FROM is doing a CROSS JOIN. A Cartesian product is unnecessary and throws all the counts off.
I suspect you want something like this:
SELECT rc.type as risk_name, MONTH(r.creation_date) as month,
count(r.id) as number
FROM risk_category rc LEFT JOIN
risk r
ON r.?? = rc.??
where risk.creation_date >= (NOW()-INTERVAL 3 MONTH)
GROUP BY rc.type, MONTH(r.creation_date);
I don't know what the JOIN criterion is between risk and risk_category.
Then try using distinct keuword with count() like count(distinct risk.id) as number instead

Access Totals Query Not Necessarily Returning First Record

I have a table of data like this:
id user_id A B C
=====================
1 15 1 2 3
2 15 1 2 5
3 20 1 3 9
4 20 1 3 7
I need to remove duplicate user ids and keep the record that sorts lowest when sorting by A then B then C. So using the above table, I set up a temp query (qry_temp) that simply does the sort--first on user_id, then on A, then on B, then on C. It returns the following:
id user_id A B C
====================
1 15 1 2 3
2 15 1 2 5
4 20 1 3 7
3 20 1 3 9
Then I wrote a Totals Query based on qry_temp that just had user_id (Group By) and then id (First), and I assumed this would return the following:
user_id id
===========
15 1
20 4
But it doesn't seem to do that--instead it appears to be just returning the lowest id in a group of duplicate user ids (so I get 1 and 3 instead of 1 and 4). Shouldn't the Totals query use the order of the query it's based upon? Is there a property setting in the query that might impact this or another way to get what I need? If it helps, here is the SQL:
SELECT qry_temp.user_id, First(qry_temp.ID) AS FirstOfID
FROM qry_temp
GROUP BY qry_temp.user_id;
You need a different type of query, for example:
SELECT tmp.id,
tmp.user_id,
tmp.a,
tmp.b,
tmp.c
FROM tmp
WHERE (( ( tmp.id ) IN (SELECT TOP 1 id
FROM tmp t
WHERE t.user_id = tmp.user_id
ORDER BY t.a,
t.b,
t.c,
t.id) ));
Where tmp is the name of your table. First, Last, Min and Max are not dependent on a sort order. In relational databases, sort orders are quite ephemeral.