简体   繁体   中英

Optimal SQL query help please

I have two tables:

  • Actions
  • Hosts

Sample data for the tables is as below:

Actions

  | ID (bigint) |   ACTION_TIME (datetime)  |   ACTION_NAME (varchar)   |
  -----------------------------------------------------------------------
  |         1   |   2013-08-03 04:42:55     |           test            |
  |         2   |   2013-10-01 06:22:45     |         test56            |
  |         3   |   2013-12-12 08:44:24     |          cloud            |
  |         4   |   2014-01-05 04:20:56     |           demo            |
  |         5   |   2014-03-10 08:20:26     |        console            |
  |         6   |   2014-03-12 05:48:27     |           temp            |

Hosts

  | ID (bigint) |   ACTION_ID (bigint)  |       DEVICE_ID (bigint) |  CRITICAL_COUNT (bigint)  |  HIGH_COUNT (bigint)   |
  ------------------------------------------------------------------------------------------------------------------------
  |         1   |        1              |                      1   |         200               |            400         |   
  |         2   |        1              |                      2   |         100               |            200         |
  |         3   |        2              |                      1   |         350               |            100         |
  |         4   |        4              |                      3   |           2               |             50         |
  |         5   |        5              |                      2   |           4               |              2         |
  |         6   |        6              |                      2   |          50               |            100         |

I need to use this data to generate a line graph (trend). I was able to figure out how to generate the graph but unable to write the proper SQL to retrieve data.

Explanation : As given in above tables, there are actions and each action involved host(s) (one or more than one). And each host have critical_count, high_count.

Now i want to get monthly data of critical_count, high_count (SUM's).

**There is one very **important** condition here. If more than one action has same device_id (hosts) in a month, only the latest data (basing on action_time) for that device should be used. The result should include data for last 12 months(including present month). At this month it should be Apr 2013 - Mar 2014**

For example : For the above data, result should be:

  |     MONTH   |   CRITICAL_COUNT   |  HIGH_COUNT  |
  ---------------------------------------------------
  |         4   |        0           |       0      |
  |         5   |        0           |       0      |
  |         6   |        0           |       0      |
  |         7   |        0           |       0      |
  |         8   |      300           |     600      |
  |         9   |        0           |       0      |
  |        10   |      350           |     100      |
  |        11   |        0           |       0      |
  |        12   |        0           |       0      |
  |         1   |        2           |      50      |
  |         2   |        0           |       0      |
  |         3   |       50           |     100      |

Please use the below SQL Fiddle:

http://sqlfiddle.com/#!2/d179d

Thank you.

I am sure this could be done better but MySql does not seem to have window function support or the unpivot operator. It is also very ugly as MySql doesn't allow for CTE's so had to use nested derived tables. It should be noted that my solution assumes that the ACTION_TIME increases with ACTION_ID. If this is not the case you will have to work out the MAX_ACTION_TIME and join to that instead of the MAX_ACTION_ID which will be more inefficient.

Select MONTH,
    Sum(CRITICAL_COUNT) As CRITICAL_COUNT, 
    Sum(HIGH_COUNT) As HIGH_COUNT
From (  
    Select m.ACTION_MONTH As MONTH, 
        Sum(CRITICAL_COUNT) As CRITICAL_COUNT, 
        Sum(HIGH_COUNT) As HIGH_COUNT
    From `hosts` h
        Inner Join `actions` a On h.ACTION_ID = a.ID
        Inner Join (  
            Select DEVICE_ID,
                Month(ACTION_TIME) As ACTION_MONTH, 
                Max(ACTION_ID) As MAX_ACTION_ID -- Max(ACTION_TIME) As MAX_ACTION_TIME
            From `hosts` h
                Inner Join `actions` a On h.ACTION_ID = a.ID
            Group By DEVICE_ID, 
                Month(ACTION_TIME)          
        ) m On m.DEVICE_ID = h.DEVICE_ID
            And m.ACTION_MONTH = Month(a.ACTION_TIME)
            And m.MAX_ACTION_ID = h.ACTION_ID -- And m.MAX_ACTION_TIME = a.ACTION_TIME
    Group By m.ACTION_MONTH

    Union All Select 1,0,0
    Union All Select 2,0,0
    Union All Select 3,0,0
    Union All Select 4,0,0
    Union All Select 5,0,0
    Union All Select 6,0,0
    Union All Select 7,0,0
    Union All Select 8,0,0
    Union All Select 9,0,0
    Union All Select 10,0,0
    Union All Select 11,0,0
    Union All Select 12,0,0
) ALL_MONTHS
Group By MONTH
Order By (Case When MONTH > 3 Then MONTH - 3 Else MONTH + 9 End)

Here is the working Fiddle:

http://sqlfiddle.com/#!2/d179d/57

And the query goes something like this:

SELECT
m.month,
ifnull(SUM(hosts.critical_count),0) as host_critical,
ifnull(SUM(hosts.high_count),0) as host_high
FROM
(SELECT
MAX(actions.action_time) as maxtime
FROM actions
GROUP BY MONTH(actions.action_time)) a
LEFT OUTER JOIN actions ON a.maxtime = actions.action_time
LEFT OUTER JOIN hosts ON actions.id = hosts.action_id
RIGHT OUTER JOIN 
(SELECT 4 AS MONTH
UNION SELECT 5 AS MONTH
UNION SELECT 6 AS MONTH
UNION SELECT 7 AS MONTH
UNION SELECT 8 AS MONTH
UNION SELECT 9 AS MONTH
UNION SELECT 10 AS MONTH
UNION SELECT 11 AS MONTH
UNION SELECT 12 AS MONTH
UNION SELECT 1 AS MONTH
UNION SELECT 2 AS MONTH
UNION SELECT 3 AS MONTH) m ON m.MONTH = MONTH(a.maxtime)
GROUP BY MONTH(actions.action_time),hosts.action_id,m.month
ORDER BY m.month < 4, m.month;

Please note that, I have not handled the scenario of multiple years in this code since you wanted it for one fiscal year. If you want to handle the year part too, you can use YEAR() and do it accordingly.

Hope this helps. Good Luck!

select MONTH, CRITICAL_COUNT, HIGH_COUNT from
(
select 
       YEAR(action_time) YEAR, 
       MONTH(action_time) MONTH, 
       SUM(critical_count) CRITICAL_COUNT,
       SUM(high_count) HIGH_COUNT
FROM hosts H1 inner join actions A1 on A1.id = H1.action_id
WHERE DATE_SUB(CURRENT_DATE,INTERVAL 1 YEAR) <= action_time
AND A1.action_time = 
(
  select max(A2.action_time)
  from hosts H2 inner join actions A2 on A2.id = H2.action_id
  where 
  H2.device_id = H1.device_id 
  and MONTH(A2.action_time) = MONTH(A1.action_time)
  and YEAR(A2.action_time) = YEAR(A1.action_time)
)
group by MONTH(action_time), year(action_time)

Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 11 MONTH)), MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 11 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 10 MONTH)), MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 10 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 9 MONTH)),  MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 9 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 8 MONTH)),  MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 8 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 7 MONTH)),  MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 7 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 6 MONTH)),  MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 6 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 5 MONTH)),  MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 5 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 4 MONTH)),  MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 4 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 3 MONTH)),  MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 3 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 2 MONTH)),  MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 2 MONTH)),0,0
Union All Select YEAR(DATE_SUB(CURRENT_DATE,INTERVAL 1 MONTH)),  MONTH(DATE_SUB(CURRENT_DATE,INTERVAL 1 MONTH)),0,0
) ALL_MONTHS
where (CRITICAL_COUNT > 0 or HIGH_COUNT> 0)
or (CRITICAL_COUNT = 0 and HIGH_COUNT = 0)
group by YEAR,MONTH
order by YEAR,MONTH

Fiddle Demo

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