0

I want select rows from my table with last status_Id if there is a row with status_Id = 2 for that rows

ticketStatus_Id  ticket_Id   status_Id
======================================
1                 1            1
2                 1            2 -
3                 1            3 *
4                 2            1
5                 3            1
6                 3            2 - *
7                 4            1
8                 4            2 -
9                 4            3      
10                4            4 *

I want select just rows 3, 6, 10. there are another rows with status_Id = 2 (rows 2, 6, 8) for that ticket_Id, In other word How to select rows 3,6,10 with ticket_Id =1,3,4 that there are another row with these ticket_Ids and status_Id=2 (rows 2,6,8)

majid_shoorabi
  • 121
  • 1
  • 1
  • 16
  • 1
    I don't follow the pattern for retaining records. Can you better explain your logic? – Tim Biegeleisen Dec 25 '19 at 12:27
  • You want to select only those tickets with a status > 2, and for each of those tickets get the rows with the highest status. What version of MySQL? – Schwern Dec 25 '19 at 12:30
  • No I just want select rows if there is a row with status_Id = 2 for them. I want select rows 3,6,10 with ticket_Id = 1,3,4 that there are another row with status_Id = 2 for them – majid_shoorabi Dec 25 '19 at 12:40
  • 1
    @majid_shoorabi And if we added a row `(11, 5, 3)` that would not be selected? – Schwern Dec 25 '19 at 12:49
  • [Here's a dbfiddle for folks to use](https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=80fdbf720dd82c5526cd15e2b0519d52) with a couple curve balls added to the data to catch folks taking short cuts. – Schwern Dec 25 '19 at 13:10

5 Answers5

1

First, for each ticket we get the row with the highest status. We can do this with a self-join. Each row is joined with the row with the next highest status. We select the rows which have no higher status, those will be the highest. Here's a more detailed explanation.

  select ts1.*
  from ticket_statuses ts1
  left outer join ticket_statuses ts2
    on  ts1.ticket_Id = ts2.ticket_Id
    and ts1.status_Id < ts2.status_Id
  where ts2.ticketStatus_Id is null

3   1   3
4   2   1
6   3   2
10  4   4
11  5   3

Note that I've added a curve-ball of 11, 5, 3 to ensure we only select tickets with a status of 2, not greater than 2.

Then we can use that as a CTE (or subquery if you're not using MySQL 8) and select only those tickets who have a status of 2.

with max_statuses as (
  select ts1.*
  from ticket_statuses ts1
  left outer join ticket_statuses ts2
    on  ts1.ticket_Id = ts2.ticket_Id
    and ts1.status_Id < ts2.status_Id
  where ts2.ticketStatus_Id is null
)
select ms.*
from max_statuses ms
join ticket_statuses ts
  on  ms.ticket_id = ts.ticket_id
  and ts.status_id = 2;

3   1   3
6   3   2
10  4   4

This approach ensures we select the complete rows with the highest statuses and any extra data they may contain.

dbfiddle

Schwern
  • 153,029
  • 25
  • 195
  • 336
1

If you want the complete row, then I would view this as exists:

select t.*
from t
where exists (select 1
              from t t2
              where t2.ticket_id = t.ticket_id and t2.status_id = 2
             ) and
      t.status_Id = (select max(t2.status_id)
                      from t t2
                      where t2.ticket_id = t.ticket_id 
                     );

If you just want the ticket_id and status_id (and not the whole row), I would recommend aggregation:

select ticket_id, max(status_id)
from t
group by ticket_id
having sum(status_id = 2) > 0;

In your case, ticketStatus_Id seems to increase with status_id, so you can use:

select max(ticketStatus_Id) as ticketStatus_Id, ticket_id, max(status_id) as Status_Id
from t
group by ticket_id
having sum(status_id = 2) > 0;
Schwern
  • 153,029
  • 25
  • 195
  • 336
Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
  • The correlation between `ticketStatus_Id` and `status_id` is likely an artifact of how the test data was generated. It is not reliable unless that constraint is explicit in the schema. – Schwern Dec 25 '19 at 13:08
1

This is basicaly a "last row per group" problem. You will find some solutions here. My prefered solution would be:

select t.*
from (
  select max(ticketStatus_Id) as ticketStatus_Id
  from mytable
  group by ticket_Id
) tmax
join mytable t using(ticketStatus_Id)

The difference in your question is that you have a condition requiring a specific value within the group. This can be solved with a JOIN within the subquery:

select t.*
from (
  select max(t1.ticketStatus_Id) as ticketStatus_Id
  from mytable t2
  join mytable t1 using(ticket_Id)
  where t2.status_Id = 2
  group by t2.ticket_Id
) tmax
join mytable t using(ticketStatus_Id)

Result:

| ticketStatus_Id | ticket_Id | status_Id |
| --------------- | --------- | --------- |
| 3               | 1         | 3         |
| 6               | 3         | 2         |
| 10              | 4         | 4         |

View on DB Fiddle

A solution using window functions could be:

select ticketStatus_Id, ticket_Id, status_Id
from (
  select *
  , row_number() over (partition by ticket_Id order by ticketStatus_Id desc) as rn
  , bit_or(status_Id = 2) over (partition by ticket_Id) > 0 as has_status2
  from mytable
) x
where has_status2 and rn = 1

A quite expressive way is to use EXISTS and NOT EXISTS subquery conditions:

select t.*
from mytable t
where exists (
    select *
    from mytable t1
    where t1.ticket_Id = t.ticket_Id
      and t1.status_Id = 2
  )
  and not exists (
    select *
    from mytable t1
    where t1.ticket_Id = t.ticket_Id
      and t1.ticketStatus_Id > t.ticketStatus_Id
  )
Paul Spiegel
  • 30,925
  • 5
  • 44
  • 53
0
SELECT a.*
FROM t a 
JOIN 
(
  SELECT ticket_id, MAX(status_id) max_status_id 
  FROM t 
  WHERE status_id >= 2 
  GROUP BY ticket_id 
) b 
ON a.ticket_id = b.ticket_id 
  AND a.status_id = b.max_status_id;
Schwern
  • 153,029
  • 25
  • 195
  • 336
seunggabi
  • 1,699
  • 12
  • 12
  • 1
    I believe you want `select a.*`. The OP wants only tickets who have a status of 2. If we add a ticket like `11 5 3` which has a status > 2 but no status = 2 this query will incorrectly pick that up. – Schwern Dec 25 '19 at 12:59
-2
SELECT
  MAX(m1.ticketstatus_Id) as ticket_status,
  m1.ticket_Id             as ticket,
  MAX(m1.status_Id)        as status
FROM mytable m1
WHERE
  m1.ticket_Id in (select m2.ticket_Id from mytable m2 where m2.ticket_Id=m1.ticket_Id and m2.status_Id=2)
GROUP BY m1.ticket_Id
Luuk
  • 12,245
  • 5
  • 22
  • 33
  • isn't it? `status >= 2` – seunggabi Dec 25 '19 at 12:39
  • 2
    no, op writes `if there is a row with status_Id = 2 for that rows`, so MAX(status) for every row that also has a status=2. – Luuk Dec 25 '19 at 12:40
  • max ticket id, max satus id not same status – seunggabi Dec 25 '19 at 12:51
  • oops, corrected the query, see above, and [dbfiddle](https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=88b97c4dadfb50957e2d839e430eff81) – Luuk Dec 25 '19 at 13:50
  • This only gets the IDs, not the rows; if there's extra data in a row it can be incorrect. And this only works if the rows are in a particular order. For example, if we change `2, 1, 2` to `12, 1, 2` we get `12, 1, 3` instead of `3, 1, 3`. [dbfiddle](https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=5ef19b0f0fafec31958942d8e3c657f2). – Schwern Dec 25 '19 at 14:14
  • @Schwern: if the change is done, as you proposed, the correct answer should probably be `12,1,2`, because I think the last 'ticket_status_id' is reflecting the correct status of a 'ticket' ? – Luuk Dec 26 '19 at 08:30
  • @Luuk The answer remains the same. 12 is the `ticketStatus_Id`, a unique row identifier (ie. primary key) whose value and order are arbitrary. It shouldn't matter if it's 2, 12, 12000 or 'Walla Walla, Washington'. Your query assumes there is an ordered relationship between `ticketstatus_Id` and `status_Id` when there is none. It's just an artifact of how the test data was generated. – Schwern Dec 26 '19 at 17:20
  • So, in this case, it's not possible to set the new status to a lower value than the current status? In cases where I have to do with a status, this certainly should be possible. – Luuk Dec 27 '19 at 07:43