简体   繁体   中英

MySQL, Get number of last records in second table for each element of first, divided by specific time intervals

I have a two tables:

case_map

case_id | creation_date
________|___________________
49      | 2013-04-30
51      | 2013-05-15
82      | 2014-05-23
109     | 2013-06-01
123     | 2013-07-23

case_legend

id | case_id | operation_number | operation_date | failure
___|_________|__________________|________________|________
1  | 49      | 105              | 2013-05-03     | 0
2  | 51      | 105              | 2013-05-28     | 0
3  | 51      | 110              | 2013-05-29     | 0
4  | 51      | 115              | 2013-06-02     | 1
5  | 51      | 110              | 2013-06-05     | 0
6  | 82      | 105              | 2013-05-28     | 0
7  | 82      | 110              | 2013-05-30     | 0
8  | 82      | 115              | 2013-06-01     | 0
9  | 82      | 120              | 2013-06-01     | 0
10 | 82      | 125              | 2013-06-02     | 0
11 | 109     | 105              | 2013-06-27     | 0
12 | 123     | 105              | 2013-07-27     | 0
13 | 123     | 110              | 2013-08-10     | 0

And I want to know how many cases was recieved and how many of these cases are on operation 105 and 125 ( 'on operation' = value of operation_number for row with max operation_date for this case_id ) in a certain time interval, for example 'from May, 2013 to Jule, 2013', splited by mounth.

For these purposes, I made ​​the following query:

SELECT
            `recieved_cases`.`abbreviated_date`,
            `recieved_cases`.`recieved_count`,
            `operation_105`.`105_count`,
            `operation_125`.`125_count`
        FROM (
            SELECT DATE_FORMAT( `creation_date`, '%Y-%m' ) AS `abbreviated_date`, COUNT( `case_id` ) AS `recieved_count`
            FROM `case_map`
            WHERE `creation_date` BETWEEN '2013-05-01' AND '2013-07-31'
            GROUP BY `abbreviated_date`
        ) AS `recieved_cases`
        LEFT JOIN (
            SELECT DATE_FORMAT( `t1`.`operation_date`, '%Y-%m' ) AS `abbreviated_date`, COUNT( `t1`.`operation_number` ) AS `105_count`
            FROM `case_legend` AS `t1`
            WHERE `t1`.`id` = (
                SELECT `t2`.`id`
                FROM `case_legend` AS `t2`
                WHERE `t2`.`case_id` = `t1`.`case_id`
                ORDER BY `t2`.`operation_date` DESC, `t2`.`id` DESC
                LIMIT 1
            )
            AND `operation_number` = 105
            GROUP BY `abbreviated_date`
        ) AS `operation_105`
        ON `recieved_cases`.`abbreviated_date` = `operation_105`.`abbreviated_date`
        LEFT JOIN (
            SELECT DATE_FORMAT( `t1`.`operation_date`, '%Y-%m' ) AS `abbreviated_date`, COUNT( `t1`.`operation_number` ) AS `125_count`
            FROM `case_legend` AS `t1`
            WHERE `t1`.`id` = (
                SELECT `t2`.`id`
                FROM `case_legend` AS `t2`
                WHERE `t2`.`case_id` = `t1`.`case_id`
                ORDER BY `t2`.`operation_date` DESC, `t2`.`id` DESC
                LIMIT 1
            )
            AND `operation_number` = 125
            GROUP BY `abbreviated_date`
        ) AS `operation_125`
        ON `recieved_cases`.`abbreviated_date` = `operation_125`.`abbreviated_date`
        ORDER BY `recieved_cases`.`abbreviated_date`

My problem is that such a request takes into account also cases with creation_date not in a specified time interval. Ie case, which has the last operation in the specified interval, but was created earlier - is taken into account, and it should not be. How can I fix this query?

Desired result for provided example is:

abbreviated_date | recieved_count | 105_count | 125_count
_________________|________________|___________|__________
2013-05          | 2              | 0         | 0
2013-06          | 1              | 1         | 1
2013-07          | 1              | 0         | 0

Can a temporary table helps me here? I mean if I first create table like case_legend with creation_date field. I think on this one for the last hour, but not sure how to do it.

PS Also, if my query is bad and you can give me an advise how to optimize it - I would be grateful.

This may be what you want:

select year(cm.creation_date), month(cm.creation_date),
       count(distinct case when cl.operation_number = 105 then case_id end) as op105,
       count(distinct case when cl.operation_number = 125 then case_id end) as op125
from case_map cm join
     case_legend cl
     on cm.case_id = cl.case_id
group by year(cm.creation_date), month(cm.creation_date)
order by 1, 2;

If not, you can start with the most recent date for each of the operations:

select cm.*,
       max(case when cl.operation_number = 105 then operation_date end) as op105,
       max(case when cl.operation_number = 125 then operation_date end) as op125
from case_map cm join
     case_legend cl
     on cm.case_id = cl.case_id
group by cm.case_id;

And then work from there.

EDIT:

Now that you supplied the desired results, the query is not too difficult:

select date_format(cm.creation_date, '%Y-%m' ) as yyyymm,
       sum(cl.opeartion_number = 105) as op105,
       sum(cl.opeartion_number = 125) as op125
from case_map cm join
     case_legend cl
     on cm.case_id = cl.case_id
where not exists (select 1
                  from case_legend cl2
                  where cl2.case_id = cl.case_id and
                        cl2.operation_date > cl.operation_date
                 )
group by date_format(cm.creation_date, '%Y-%m')
order by 1;

So the answer is: You don't need temporary tables.

Here's this fiddle:

http://sqlfiddle.com/#!2/8673a3/9

It gives you the same results but in a different format perhaps, than what you prefer.

select 
DATE_FORMAT( operation_date, '%Y-%m' ) as `Date`, 
count(cl.case_id), cl.operation_number,
t.count
from 
case_legend cl
left join case_map cm
on cl.case_id = cm.case_id

left join (
  select 
  DATE_FORMAT( cl.operation_date, '%Y-%m' ) as `Date`, count(cl.case_id) as `count`, 'All'     as operation_number
  from 
  case_legend cl
  left join case_map cm
  on cl.case_id = cm.case_id
  where operation_date BETWEEN '2013-05-01' AND '2013-07-31'
  GROUP BY DATE_FORMAT( operation_date, '%Y-%m' )

  ) t

on t.Date = DATE_FORMAT( operation_date, '%Y-%m' )

where cl.operation_number in (105, 125)
and operation_date BETWEEN '2013-05-01' AND '2013-07-31'
GROUP BY DATE_FORMAT( operation_date, '%Y-%m' ), cl.operation_number
;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM