MYSQL, Calculating Patient hospital readmissions 28 Days - mysql

I'm am having difficulty in trying to make this work. I have the following table that has patients stay in a hospital. They each have an admission date and a discharge date, each stay has an episode id. For the life of me, I can not figure out how to query the table to work out if the patient has been discharged and been readmitted within 28 days of the previous stay. Any pointers would be appreciated.
ID
Admission
Discharge
EpisodeID
PatientID
01
2020-02-17
2020-03-10
1234
1
02
2020-02-18
2020-03-15
1235
2
03
2020-02-20
2020-03-19
1236
3
04
2020-03-17
2020-03-30
1237
1
05
2020-03-19
2020-03-25
1238
4
06
2020-03-22
2020-03-29
1239
5
07
2020-03-29
2020-04-03
1240
6
08
2020-03-30
2020-04-10
1241
2
09
2020-04-01
1242
7
10
2020-04-17
1243
2
Output
ID
Admission
Discharge
EpisodeID
PatientID
Readmit
01
2020-02-17
2020-03-10
1234
1
N
02
2020-02-18
2020-03-15
1235
2
N
03
2020-02-20
2020-03-19
1236
3
N
04
2020-03-17
2020-03-30
1237
1
Y
05
2020-03-19
2020-03-25
1238
4
N
06
2020-03-22
2020-03-29
1239
5
N
07
2020-03-29
2020-04-03
1240
6
N
08
2020-03-30
2020-04-10
1241
2
Y
09
2020-04-01
1242
7
N
10
2020-04-17
1243
2
Y

You can use simple LEFT JOIN in this case:
SELECT
p.*,
IF(prev.PatientID IS NOT NULL, 'Y', 'N') Readmit
FROM p
LEFT JOIN p prev ON
p.PatientID = prev.PatientID AND
DATEDIFF(p.Admission, prev.Discharge) BETWEEN 1 AND 28 ;
Look MySQL online fiddle

Thanks to Slava I was able to get what I needed. If anyone else comes across this, here is the solution that I used; (If I have made any mistakes I apologize)
CREATE TABLE p (ID int, Admission date, Discharge date, EpisodeID int, PatientID int);
INSERT INTO p VALUES
('01', '2020-02-17', '2020-03-10', 1234, 1),
('02', '2020-02-18', '2020-03-15', 1235, 2),
('03', '2020-02-20', '2020-03-19', 1236, 3),
('04', '2020-03-17', '2020-03-30', 1237, 1),
('05', '2020-03-19', '2020-03-25', 1238, 4),
('06', '2020-03-22', '2020-03-29', 1239, 5),
('07', '2020-03-29', '2020-04-03', 1240, 6),
('08', '2020-03-30', '2020-04-10', 1241, 2),
('09', '2020-04-01', null, 1242, 7),
('10', '2020-04-17', '2020-04-18', 1243, 2),
('11', '2020-04-17', null, 1244, 2);
 
SELECT
p.*,
IF(DATEDIFF(p.Admission, prev.Discharge)<29,'Y','N') Readmit,
DATEDIFF(p.Admission, prev.Discharge) Days_Between
FROM p
LEFT JOIN p prev ON p.PatientID = prev.PatientID
AND DATEDIFF(p.Admission, prev.Discharge) BETWEEN 1 AND 28 ;
 
+====+============+============+===========+===========+=========+==============+
| ID | Admission | Discharge | EpisodeID | PatientID | Readmit | Days_Between |
+====+============+============+===========+===========+=========+==============+
| 1 | 2020-02-17 | 2020-03-10 | 1234 | 1 | N | (null) |
+----+------------+------------+-----------+-----------+---------+--------------+
| 2 | 2020-02-18 | 2020-03-15 | 1235 | 2 | N | (null) |
+----+------------+------------+-----------+-----------+---------+--------------+
| 3 | 2020-02-20 | 2020-03-19 | 1236 | 3 | N | (null) |
+----+------------+------------+-----------+-----------+---------+--------------+
| 4 | 2020-03-17 | 2020-03-30 | 1237 | 1 | Y | 20 |
+----+------------+------------+-----------+-----------+---------+--------------+
| 5 | 2020-03-19 | 2020-03-25 | 1238 | 4 | N | (null) |
+----+------------+------------+-----------+-----------+---------+--------------+
| 6 | 2020-03-22 | 2020-03-29 | 1239 | 5 | N | (null) |
+----+------------+------------+-----------+-----------+---------+--------------+
| 7 | 2020-03-29 | 2020-04-03 | 1240 | 6 | N | (null) |
+----+------------+------------+-----------+-----------+---------+--------------+
| 8 | 2020-03-30 | 2020-04-10 | 1241 | 2 | Y | 26 |
+----+------------+------------+-----------+-----------+---------+--------------+
| 9 | 2020-04-01 | (null) | 1242 | 7 | N | (null) |
+----+------------+------------+-----------+-----------+---------+--------------+
| 10 | 2020-04-17 | 2020-04-18 | 1243 | 2 | Y | 8 |
+----+------------+------------+-----------+-----------+---------+--------------+
| 11 | 2020-04-17 | (null) | 1244 | 2 | N | 382 |
+----+------------+------------+-----------+-----------+---------+--------------+
SQLize

Related

MySQL: SUM() on Self-Joined table

I have the following table named Stock_Level. (It has around 200,000 rows)
| sku | location_id | stock_level | allocated | on_order | date_booked_in | date_last_sold | date_last_stock_take| status_since |
|-----|-------------|-------------|-----------|----------|---------------------|---------------------|---------------------|---------------------|
| 1 | 1 | 100 | 2 | 15 | 2020-12-01 12:34:00 | 2020-11-25 12:34:00 | 2021-01-01 12:00:00 | 2021-02-09 12:55:00 |
| 2 | 1 | 50 | 1 | 0 | 2020-11-02 12:34:00 | 2020-10-09 12:34:00 | 2021-01-01 12:00:00 | 2021-02-09 12:55:00 |
| 3 | 1 | 10 | 0 | 12 | 2020-08-05 12:34:00 | 2020-05-04 12:34:00 | 2021-01-01 12:00:00 | 2021-02-09 12:55:00 |
| 1 | 2 | 80 | 0 | 13 | 2020-09-09 12:34:00 | 2020-12-06 12:34:00 | 2021-01-03 12:00:00 | 2021-02-09 13:55:00 |
| 2 | 2 | 35 | 3 | 0 | 2020-07-10 12:34:00 | 2020-08-05 12:34:00 | 2021-01-03 12:00:00 | 2021-02-09 12:55:00 |
| 1 | 3 | 60 | 1 | 0 | 2020-08-12 12:34:00 | 2020-09-04 12:34:00 | 2021-02-03 12:00:00 | 2021-02-09 12:55:00 |
| 2 | 3 | 9 | 0 | 17 | 2020-05-30 12:34:00 | 2020-01-01 12:34:00 | 2021-02-03 12:00:00 | 2021-02-09 12:55:00 |
PRIMARY key is on sku and location_id
There is also an INDEX on status_since
A SKU can be in multiple locations. When an item is booked in, sold or stock taken, status_since is updated to NOW()
What I'm trying to do
I have a script that runs every 15 minutes, selecting items that have been updated in the last 15 minutes, with the SUM of (stock_level - allocated) for that sku, along with the total number on order and the MAX() values of date_booked_in, date_last_sold and date_last_stock_take
The code that I've currently got is this:
SELECT sku,
SUM(stock_level - allocated) AS available,
SUM(on_order) AS on_order,
MAX(date_booked_in) AS date_booked_in,
MAX(date_last_sold) AS date_last_sold,
MAX(date_last_stock) AS date_last_stock
FROM Stock_Level
WHERE
sku IN(
SELECT sku
FROM Stock_Level
WHERE
status_since>= DATE_SUB(NOW(), INTERVAL 15 MINUTE)
AND status_since<= NOW()
)
GROUP BY sku
This works fine when there are only a few items that have been recently updated; however sometimes many hundreds can be updated within a 15 minute period, and this query grinds to a halt.
I suspect there is a way to do this with a self join rather than using an IN() clause but I can't for the life of me work it out.
What would be some potential ways to rewrite this query?
You can use EXISTS which is much faster than IN and is always recommended:
SELECT sku,
SUM(stock_level - allocated) AS available,
SUM(on_order) AS on_order,
MAX(date_booked_in) AS date_booked_in,
MAX(date_last_sold) AS date_last_sold,
MAX(date_last_stock) AS date_last_stock
FROM Stock_Level
WHERE
EXISTS(
SELECT 1
FROM Stock_Level sl2
WHERE
sl2.status_since>= DATE_SUB(NOW(), INTERVAL 15 MINUTE)
AND sl2.status_since<= NOW()
AND sku.sku = sl2.sku
)
GROUP BY sku

MySQL select MAX value from subquery only returns one results from outer query. Why?

(Using MariaDB 5.5 I am aware this is old but it's a 3rd party server I can't update it )
I have a table of data with a date/year column. I need to list all the values based on a reference id but only with the most recent year.
Based on my recent question about subqueries I thought this was going to be a simple case of using a subquery with a MAX() selection, but that only ever returns one results. Why? And how do I get the full set of results.
The table:
create table ee_stats
(
stat_id int(8) unsigned auto_increment
primary key,
ee_id smallint(6) unsigned null,
ref_id int null comment 'external table reference.',
date_year year null,
value_counter int(8) unsigned null,
value_percentage double(6,3) unsigned null
)
The data
stat_id | ee_id | ref_id | date_year | value_counter | value_percentage
------------------------------------------------------------------------
301 | 32 | 14 | 2020 | 0 | 0
302 | 32 | 1 | 2020 | 0 | 0
303 | 32 | 21 | 2020 | 0 | 0
304 | 32 | 22 | 2020 | 0 | 0
305 | 464 | 17 | 2020 | 40 | 3
306 | 464 | 18 | 2020 | 0 | 0
307 | 464 | 20 | 2020 | 0 | 0
308 | 464 | 2 | 2020 | 87 | 6.6
309 | 464 | 19 | 2020 | 0 | 0
310 | 464 | 7 | 2020 | 15 | 1.1
311 | 464 | 10 | 2020 | 29 | 2.2
312 | 464 | 11 | 2019 | 29 | 2.2
313 | 464 | 16 | 2019 | 13 | 1.0
314 | 464 | 7 | 2019 | 116 | 8.8
315 | 464 | 19 | 2019 | 71 | 5.3
316 | 464 | 4 | 2019 | 67 | 5
Intended result is to select all of the values with ee_id = 464 and the most recent year on record (2020).
My Query
SELECT es.stat_id, es.ee_id, es.ref_id, es.date_year, es.value_count,
es.value_percentage, eed.descr
FROM ee_stats es
LEFT JOIN ee_descriptor eed on es.ref_id = eed.id
WHERE es.ee_id = 464 AND es.date_year = (
SELECT MAX(zz.date_year) FROM ee_stats zz WHERE zz.ee_id = es.ee_id
)
But this only returns the first outer result:
305 | 464 | 17 | 2020 | 40 | 3
My attempts
The subquery correctly returns the value 2020 when run independently (substituting the outer value). This question is very similar but that answer seems to be exactly what should be working here.
The works:
SELECT es.stat_id, es.ee_id, es.ref_id, es.date_year, es.value_count,
es.value_percentage, eed.descr
FROM ee_stats es
LEFT JOIN ee_descriptor eed on es.ref_id = eed.id
WHERE es.ee_id = 464 AND es.date_year = '2020'
And This works:
SELECT es.stat_id, es.ee_id, es.ref_id, es.date_year, es.value_count,
es.value_percentage, eed.descr
FROM ee_stats es
LEFT JOIN ee_descriptor eed on es.ref_id = eed.id
WHERE es.ee_id = 464 AND es.date_year = (
SELECT MAX(zz.date_year) FROM ee_stats zz WHERE zz.ee_id = '464'
)
but somehow the dynamic outer reference to the ee_id value only returns one result on the SQL.
Intended result:
305 | 464 | 17 | 2020 | 40 | 3
306 | 464 | 18 | 2020 | 0 | 0
307 | 464 | 20 | 2020 | 0 | 0
308 | 464 | 2 | 2020 | 87 | 6.6
309 | 464 | 19 | 2020 | 0 | 0
310 | 464 | 7 | 2020 | 15 | 1.1
311 | 464 | 10 | 2020 | 29 | 2.2
I got it!
I found I had to CAST the MAX entity to being the correct format; so I had to cast it to a DATE format.
CAST(MAX(zz.date_year) AS DATE)
This works.
SELECT es.stat_id, es.ee_id, es.ref_id, es.date_year, es.value_count,
es.value_percentage, eed.descr
FROM ee_stats es
LEFT JOIN ee_descriptor eed on es.ref_id = eed.id
WHERE es.ee_id = 464 AND es.date_year = (
SELECT CAST(MAX(zz.date_year) AS DATE) FROM ee_stats zz WHERE zz.ee_id = es.ee_id
)

How to get the opposite of a join?

I am trying to get the rows that don't exist in one table where one table called schedules (match_week, player_home_id, player_away_id) and the other table called match (match_week, Winner_id, Defeated_id) are joined. The players look at their schedule and play a match. I am trying to get a list of the scheduled matches that do not exist in the match table. The IDs in the match table can be in either column Winner_id or Defeated_id.
I have reviewed a number of Stack Exchange examples, but most use "IS NULL" and I don't have null values. I have used a Join that does give the output of the matches played. I would like the matches that have not been played.
CSV - wp_schedule_test
+----+------------+--------------+--------------+-----------------+-----------------+
| ID | match_week | home_player1 | away_player1 | player1_home_id | player1_away_id |
+----+------------+--------------+--------------+-----------------+-----------------+
| 1 | WEEK 1 | James Rives | Dale Hemme | 164 | 169 |
| 2 | WEEK 1 | John Head | David Foster | 81 | 175 |
| 3 | WEEK 1 | John Dalton | Eric Simmons | 82 | 23 |
| 4 | WEEK 2 | John Head | James Rives | 81 | 164 |
| 5 | WEEK 2 | Dale Hemme | John Dalton | 169 | 82 |
| 6 | WEEK 2 | David Foster | Eric Simmons | 175 | 23 |
| 7 | WEEK 3 | John Dalton | James Rives | 82 | 164 |
| 8 | WEEK 3 | John Head | Eric Simmons | 81 | 23 |
| 9 | WEEK 3 | Dale Hemme | David Foster | 169 | 175 |
| 10 | WEEK 4 | Eric Simmons | James Rives | 23 | 164 |
| 11 | WEEK 4 | David Foster | John Dalton | 175 | 82 |
| 12 | WEEK 4 | Dale Hemme | John Head | 169 | 81 |
+----+------------+--------------+--------------+-----------------+-----------------+
CSV - wp_match_scores_test
+----+------------+------------+------------+
| ID | match_week | player1_id | player2_id |
+----+------------+------------+------------+
| 5 | WEEK 1 | 82 | 23 |
| 20 | WEEK 1 | 164 | 169 |
| 21 | WEEK 2 | 164 | 81 |
| 25 | WEEK 2 | 82 | 169 |
| 61 | WEEK 3 | 175 | 169 |
| 62 | WEEK 4 | 175 | 82 |
| 69 | WEEK 2 | 175 | 23 |
| 85 | WEEK 3 | 164 | 82 |
| 86 | WEEK 4 | 164 | 23 |
+----+------------+------------+------------+
The output from the mysql query are the matches that have been played. I am trying to figure out how to list the matches that have not been played from the table Schedule.
CSV - MySQL Output
+------------+------------+------------+
| match_week | player1_id | player2_id |
+------------+------------+------------+
| WEEK 1 | 164 | 169 |
| WEEK 1 | 82 | 23 |
| WEEK 2 | 164 | 81 |
| WEEK 2 | 82 | 169 |
| WEEK 2 | 175 | 23 |
| WEEK 3 | 175 | 169 |
| WEEK 3 | 164 | 82 |
| WEEK 4 | 175 | 82 |
| WEEK 4 | 164 | 23 |
+------------+------------+------------+
MYSQL
select DISTINCT ms.match_week, ms.player1_id , ms.player2_id FROM
wp_match_scores_test ms
JOIN wp_schedules_test s
ON (s.player1_home_id = ms.player1_id or s.player1_away_id =
ms.player2_id)
Order by ms.match_week
The expected output is:
CSV - Desired Output
+------------+----------------+----------------+
| match_week | player_home_id | player_away_id |
+------------+----------------+----------------+
| WEEK 1 | 81 | 175 |
| WEEK 3 | 81 | 23 |
| WEEK 4 | 169 | 81 |
+------------+----------------+----------------+
The added code I would like to use is
SELECT s.*
FROM wp_schedules_test s
WHERE NOT EXISTS
(select DISTINCT ms.match_week, ms.player1_id , ms.player2_id FROM
wp_match_scores_test ms
JOIN wp_schedules_test s
ON (s.player1_home_id = ms.player1_id or s.player1_away_id =
ms.player2_id)
Order by ms.match_week)
Unfortunately, the output yields "No Rows"
You can use a LEFT JOIN to achieve the desired results, joining the two tables on matching player ids (noting that player id values in wp_match_scores_test can correspond to either player1_home_id or player1_away_id in wp_schedules_test). If there is no match, the result table will have NULL values from the wp_match_scores_test table values, and you can use that to select the matches which have not been played:
SELECT sch.*
FROM wp_schedule_test sch
LEFT JOIN wp_match_scores_test ms
ON (ms.player1_id = sch.player1_home_id
OR ms.player2_id = sch.player1_home_id)
AND (ms.player1_id = sch.player1_away_id
OR ms.player2_id = sch.player1_away_id)
WHERE ms.ID IS NULL
Output:
ID match_week home_player1 away_player1 player1_home_id player1_away_id
2 Week 1 John Head David Foster 81 175
8 Week 3 John Head Eric Simmons 81 23
12 Week 4 Dale Hemme John Head 169 81
Note that you can also use a NOT EXISTS query, using the same condition as I used in the JOIN:
SELECT sch.*
FROM wp_schedule_test sch
WHERE NOT EXISTS (SELECT *
FROM wp_match_scores_test ms
WHERE (ms.player1_id = sch.player1_home_id
OR ms.player2_id = sch.player1_home_id)
AND (ms.player1_id = sch.player1_away_id
OR ms.player2_id = sch.player1_away_id))
The output of this query is the same. Note though that conditions in the WHERE clause have to be evaluated for every row in the result set and that will generally make this query less efficient than the LEFT JOIN equivalent.
Demo on dbfiddle

How to find intersection time of multiple time periods?

How we can find the intersection time of multiple times in mysql? I have this table:
Input:
id | circuit_id | downtime | uptime | duration | Location
---+------------+-----------------+-----------------+----------+----------
1 | 1 | 6/12/2019 11:22 | 6/12/2019 11:27 | 0:05:00 | Bhopal
2 | 2 | 6/12/2019 04:55 | 6/12/2019 07:19 | 2:24:00 | Bhopal
3 | 3 | 6/04/2019 17:59 | 6/06/2019 18:57 | 48:58:00 | Bhopal
4 | 4 | 6/02/2019 03:06 | 6/02/2019 03:12 | 0:06:00 | Bhopal
5 | 1 | 6/01/2019 19:49 | 6/01/2019 20:00 | 0:11:00 | Bhopal
6 | 2 | 5/24/2019 14:59 | 5/24/2019 15:03 | 0:04:00 | Bhopal
7 | 3 | 5/19/2019 03:15 | 5/19/2019 03:54 | 0:39:00 | Bhopal
8 | 4 | 5/15/2019 19:47 | 5/15/2019 19:50 | 0:03:00 | Bhopal
9 | 1 | 5/10/2019 01:21 | 5/10/2019 03:52 | 0:31:00 | Bhopal
10 | 2 | 5/10/2019 02:55 | 5/10/2019 03:37 | 2:42:00 | Bhopal
11 | 3 | 5/10/2019 03:18 | 5/10/2019 04:37 | 1:19:00 | Bhopal
12 | 4 | 5/10/2019 03:20 | 5/10/2019 03:30 | 1:25:00 | Bhopal
13 | 1 | 5/09/2019 05:06 | 5/09/2019 14:17 | 9:11:00 | Bhopal
14 | 2 | 5/09/2019 04:31 | 5/09/2019 04:34 | 0:03:00 | Bhopal
15 | 3 | 4/17/2019 18:37 | 4/17/2019 18:44 | 0:07:00 | Bhopal
16 | 6 | 4/17/2019 17:23 | 4/17/2019 18:05 | 0:42:00 | Delhi
17 | 7 | 4/17/2019 17:00 | 4/17/2019 17:05 | 0:05:00 | Delhi
18 | 8 | 4/10/2019 06:16 | 4/10/2019 06:22 | 0:06:00 | Delhi
19 | 9 | 4/10/2019 02:20 | 4/10/2019 02:45 | 0:25:00 | Delhi
20 | 6 | 4/08/2019 23:01 | 4/08/2019 23:05 | 0:04:00 | Delhi
21 | 7 | 4/05/2019 11:20 | 4/05/2019 11:27 | 0:07:00 | Delhi
22 | 8 | 4/04/2019 01:16 | 4/04/2019 01:27 | 0:11:00 | Delhi
23 | 9 | 4/02/2019 16:30 | 4/02/2019 16:31 | 0:01:00 | Delhi
How to check date-wise and location-wise which is the overlap of times? If all circuits are down within the same time span, then treat it as down, otherwise as up. I only need downtime data.
Expected output:
Bhopal on 05/10/2019 is down for ten minutes 00:10, as all four circuits are down between 03:20 to 03:30 on that particular day.
Location | downtime | date
---------+----------+-----------
Bhopal | 00:10 | 2019-05-10
You could use a self-join to match overlaps, and a having clause to filter for those that include all links.
To get the total number of circuits, you could do another self-join to find all circuits that have ever appeared in the table, per location:
select a.location,
num_circuits,
a.downtime,
min(b.uptime) uptime,
timediff(min(b.uptime), a.downtime) duration,
count(distinct b.circuit_id) num_circuits_down
from tbl a
inner join tbl b
on a.location = b.location
and a.downtime between b.downtime and b.uptime
inner join (
select location, count(distinct circuit_id) num_circuits
from tbl
group by location
) c
on a.location = c.location
group by a.location, num_circuits, a.downtime
having count(distinct b.circuit_id) = num_circuits;
It would be better to have a separate reference-table which lists the circuits per location (4 records per location in your case).
Then the above query would select from that reference table, so instead of:
select location, count(distinct circuit_id) num_circuits
from tbl
group by location
... you would do:
select location, count(distinct circuit_id) num_circuits
from reftable
group by location
That will make the above query more efficient and reliable.

mysql grouped ranking with ties

so i have data in a table like this:
id total group_id
1897 738 1
2489 716 2
2325 715 3
1788 702 2
1707 699 3
2400 688 3
2668 682 2
1373 666 1
1494 666 1
1564 660 1
2699 659 1
1307 648 4
1720 645 4
2176 644 1
1454 644 4
2385 639 3
1001 634 2
2099 634 4
1006 632 1
2587 630 3
1955 624 3
1827 624 4
2505 623 4
2062 621 3
1003 618 1
2286 615 4
2722 609 4
how can i rank the ids per group based on the total and giving the same rank when there is a tie?
i have tried this solution below but it doesnt take care of the ties.
SELECT g1.admission_no
, g1.total
, g1.stream_id
, COUNT(*) AS rn
FROM ranktest AS g1
JOIN ranktest AS g2
ON (g2.total, g2.admission_no) >= (g1.total, g1.admission_no)
AND g1.stream_id = g2.stream_id
GROUP BY g1.admission_no
, g1.stream_id
, g1.total
ORDER BY g1.stream_id
, total ;
expected
id total group_id rank
1897 738 1 1
2489 716 2 1
2325 715 3 1
1788 702 2 2
1707 699 3 2
2400 688 3 3
2668 682 2 3
1373 666 1 2
1494 666 1 2
1564 660 1 3
2699 659 1 4
1307 648 4 1
1720 645 4 2
2176 644 1 4
1454 644 4 3
2385 639 3 4
1001 634 2 4
2099 634 4 4
1006 632 1 5
2587 630 3 5
1955 624 3 6
1827 624 4 5
2505 623 4 6
2062 621 3 6
1003 618 1 6
2286 615 4 7
2722 609 4 8
If original order is not very important you can start from:
http://sqlfiddle.com/#!9/a15a2/10
SELECT
ranktest.*,
IF(#rank IS NULL,#rank:=1, IF(#prev!=group_id,#rank:=1,#rank:=#rank+1) ) rank,
#prev:=group_id
FROM ranktest
ORDER BY group_id, total
but keep in mind that this is not very efficient query from performance perspective.
From the MySQL Manual Page entitled User-Defined Variables:
In the following statement, you
might think that MySQL will evaluate #a first and then do an
assignment second:
SELECT #a, #a:=#a+1, ...;
However, the order of evaluation for expressions involving user
variables is undefined.
This is why so few people write these answers correctly and safely. The object is not to hit the desired results once, but to do so again and again in real-world environments using best practices and guaranteed results.
If you don't read the Obligatory Doc, and implement it, your results are not guaranteed. There is no lazy way to do this and one shouldn't even bother :p
select id,total,group_id,rank
from
( select id,total,group_id,
#rn:=if( group_id!=#curr_grp, greatest(#grp_rank:=1,0),
if(total=#prev_grp_total,#grp_rank,greatest(#grp_rank:=#grp_rank+1,0) ) ) as rank,
#curr_grp:=group_id,
#prev_grp_total:=total
from trans01
cross join (select #grp_rank:=0,#curr_grp:=0,#prev_grp_total:=-1) xParams
order by group_id,total desc
)xDerived;
+------+-------+----------+------+
| id | total | group_id | rank |
+------+-------+----------+------+
| 1897 | 738 | 1 | 1 |
| 1373 | 666 | 1 | 2 |
| 1494 | 666 | 1 | 2 |
| 1564 | 660 | 1 | 3 |
| 2699 | 659 | 1 | 4 |
| 2176 | 644 | 1 | 5 |
| 1006 | 632 | 1 | 6 |
| 1003 | 618 | 1 | 7 |
| 2489 | 716 | 2 | 1 |
| 1788 | 702 | 2 | 2 |
| 2668 | 682 | 2 | 3 |
| 1001 | 634 | 2 | 4 |
| 2325 | 715 | 3 | 1 |
| 1707 | 699 | 3 | 2 |
| 2400 | 688 | 3 | 3 |
| 2385 | 639 | 3 | 4 |
| 2587 | 630 | 3 | 5 |
| 1955 | 624 | 3 | 6 |
| 2062 | 621 | 3 | 7 |
| 1307 | 648 | 4 | 1 |
| 1720 | 645 | 4 | 2 |
| 1454 | 644 | 4 | 3 |
| 2099 | 634 | 4 | 4 |
| 1827 | 624 | 4 | 5 |
| 2505 | 623 | 4 | 6 |
| 2286 | 615 | 4 | 7 |
| 2722 | 609 | 4 | 8 |
+------+-------+----------+------+
came up with this answer after googling a bit..not sure is its the best but it works for my case:
SELECT id, group_id, total,
#std:=CASE WHEN #grp <> group_id THEN concat(left(#grp:=group_id, 0), 1) ELSE if(#prev=total,#std,#std+1) END AS rn,#prev:=total
FROM
(SELECT #std:= -1) s,
(SELECT #grp:= -1,#prev:=null) c,
(SELECT *
FROM table
ORDER BY group_id, total desc
) s