请优化SQL查询帮助

时间:2014-03-18 11:03:03

标签: mysql sql

我有两张桌子:

  • 操作
  • 主机

表格的示例数据如下:

操作

  | 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            |

主机

  | 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         |

我需要使用此数据生成折线图(趋势图)。我能够弄清楚如何生成图形,但无法编写正确的SQL来检索数据。

解释:如上表所示,有一些操作,每个操作都涉及主机(一个或多个)。每个主机都有critical_count,high_count。

现在我想获得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**

例如:对于上述数据,结果应为:

  |     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      |

请使用以下SQL小提琴:

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

谢谢。

3 个答案:

答案 0 :(得分:1)

我确信这可以做得更好,但MySql似乎没有窗口功能支持或unpivot运算符。它也非常难看,因为MySql不允许使用CTE,因此不得不使用嵌套的派生表。应该注意,我的解决方案假设ACTION_TIME随ACTION_ID增加。如果不是这种情况,你将不得不计算MAX_ACTION_TIME并加入到MAX_ACTION_ID而不是MAX_ACTION_ID,效率会更低。

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)

答案 1 :(得分:1)

这是工作小提琴:

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

查询是这样的:

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;

请注意,我没有在此代码中处理多年的情况,因为您希望它在一个会计年度。如果你也想处理年份部分,可以使用YEAR()并相应地进行处理。

希望这会有所帮助。祝你好运!

答案 2 :(得分:1)

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