请复杂的SQL查询优化建议

时间:2013-10-02 11:14:17

标签: php mysql sql database

enter code here我的表格结构如下:

| ID (bigint) | APPID (Bigint)| USAGE_START_TIME (datetime) | SESSION_TIME (bigint) |  USERID (bigint)   |
-----------------------------------------------------------------------------------------------------------
|  1          |        1      |         2013-05-03 04:42:55 |       400             |       12           |
|  2          |        1      |         2013-05-12 06:22:45 |       200             |       12           |
|  3          |        2      |         2013-06-12 08:44:24 |       350             |       12           |
|  4          |        2      |         2013-06-24 04:20:56 |         2             |       12           |
|  5          |        3      |         2013-06-26 08:20:26 |         4             |       12           |
|  6          |        4      |         2013-09-12 05:48:27 |        50             |       12           |

现在,如果put是userid,我想获得每个月(所有应用程序)的session_time总数(总和),过去6个月按月计算。

如果一个月内多次使用某个应用(基于usage_start_time),则该应用的总和中只会包含最新的session_time。

对于上面的例子,结果应该是这样的(如果当前月份是10月,输入是userid = 13):

| MONTH       | TOTAL_SESSION_TIME | 
------------------------------------
|  10         |        0           |            
|   9         |       50           |         
|   8         |        0           |         
|   7         |        0           |         
|   6         |        6           | 
|   5         |      200           |

这里的月份以数字形式表示月份(例如:10代表10月份)。

目前我每个月都在使用单独的查询。例如:

SELECT 
COALESCE(SUM(failcount),0) AS TOTAL_SESSION_TIME , MONTH(CURRENT_DATE) AS MONTH 
FROM appstime 
WHERE MONTH(`USAGE_START_TIME`) = MONTH(CURRENT_DATE) AND userid=12

但这并没有给我预期的结果。

此外,我想知道我是否可以使用单个查询而不是每个月的查询来执行此操作。 我正在使用PHP + Mysql。

请检查这里的sql小提琴http://sqlfiddle.com/#!2/4eaf2

谢谢你,
窗扇

5 个答案:

答案 0 :(得分:1)

您想要GROUP BY

吗?
SELECT 
SUM(COALESCE(SESSION_TIME,0)) AS TOTAL_SESSION_TIME , 
MONTH(USAGE_START_TIME) AS THE_MONTH 
FROM appstime 
WHERE userid=13 AND 
DATE(SESSION_TIME) > DATE_SUB(NOW(), INTERVAL 6 MONTH)
GROUP BY THE_MONTH

更新

这会将结果限制为每月appid的最大日期:

SELECT 
SUM(COALESCE(session_time,0)) AS TOTAL_SESSION_TIME , 
MONTH(usage_start_time) AS THE_MONTH 
FROM appstime 
INNER JOIN
    (
    SELECT appid , MAX(usage_start_time) AS max_app_date
    FROM appstime
    GROUP BY appid
    ) grouped_apps ON 
      grouped_apps.appid = appstime.appid AND
      grouped_apps.max_app_date = appstime.usage_start_time
WHERE userid=12 
GROUP BY THE_MONTH

答案 1 :(得分:1)

未经测试,但是这样的事情: -

SELECT ForMonths.aMonth, IFNULL(SUM(SESSION_TIME), 0)
FROM
(
    SELECT MONTH(DATE_ADD(NOW(), INTERVAL aInt MONTH)) AS aMonth
    FROM
    (
        SELECT 0 AS aInt UNION SELECT -1 UNION SELECT -2 UNION SELECT -3 UNION SELECT -4 UNION SELECT -5
    ) aCnt
) ForMonths
LEFT OUTER JOIN 
(
    SELECT a.USERID, a.APPID, aMonth, SESSION_TIME
    FROM appstime a
    INNER JOIN
    (
        SELECT USERID, APPID, MONTH(USAGE_START_TIME) AS aMonth, MAX(USAGE_START_TIME) AS USAGE_START_TIME
        FROM appstime
        GROUP BY USERID, APPID, aMonth
    ) b
    ON a.USERID = b.USERID
    AND a.USAGE_START_TIME = b.USAGE_START_TIME
    AND a.APPID = b.APPID
  WHERE a.USERID = 12
) ForDetails
ON ForMonths.aMonth = ForDetails.aMonth
GROUP BY ForMonths.aMonth

可以简化,但是一个子查询可以获取过去6个月中的每一个,并将其与另一个子查询相加以获取用户每月的最新金额。

编辑 - 使用年月: -

SELECT ForMonths.aMonth, IFNULL(SUM(SESSION_TIME), 0)
FROM
(
    SELECT EXTRACT(YEAR_MONTH FROM DATE_ADD(NOW(), INTERVAL aInt MONTH)) AS aMonth
    FROM
    (
        SELECT 0 AS aInt UNION SELECT -1 UNION SELECT -2 UNION SELECT -3 UNION SELECT -4 UNION SELECT -5
    ) aCnt
) ForMonths
LEFT OUTER JOIN 
(
    SELECT a.USERID, a.APPID, aMonth, SESSION_TIME
    FROM appstime a
    INNER JOIN
    (
        SELECT USERID, APPID, EXTRACT(YEAR_MONTH FROM USAGE_START_TIME) AS aMonth, MAX(USAGE_START_TIME) AS USAGE_START_TIME
        FROM appstime
        GROUP BY USERID, APPID, aMonth
    ) b
    ON a.USERID = b.USERID
    AND a.USAGE_START_TIME = b.USAGE_START_TIME
    AND a.APPID = b.APPID
  WHERE a.USERID = 12
) ForDetails
ON ForMonths.aMonth = ForDetails.aMonth
GROUP BY ForMonths.aMonth
ORDER BY ForMonths.aMonth

答案 2 :(得分:1)

虽然我在获取6个月的记录时遇到了问题,但我会在其他计算机上完成但在当前计算机上没有MySQL。我从Kickstart那里抓了那部分(因为他的功劳)。

无论如何,我从6个月前和现在的12个用户开始,最内层的“PreQuery”(别名PQ)。确保我不仅仅是从今天起6个月(月2日) ,或者即使在本月26日运行,但想要6个月前的第一个,我做了一些日期数学,直到6个月前的最后一天,然后在下个月的第一天增加1天。所以这将创建

10月2日 - 6个月= 4月2日...然后移动到4月30日的最后一天,然后加1天到5月1日。所以,现在5月1日到10月当前,10月是第6个月,如果1天到整个月。如果你想从4月1日到现在,那么请回到7个月。

因此,我得到每个应用,用户,每月和每个月的最大实例。我包括用户,以防你想要所有用户,而不是将来只有一个用户。

一旦查询每个应用/用户/月的MAX(),我就会再次加入会话,但仅限于那些匹配的会话。

最后通过左连接应用到外部月份列表中,您可以完成其余的工作。同样,如果您想要所有用户,您只需将用户添加到外部查询和分组依据。

SELECT
      AllMonths.aMonth,
      COALESCE( SUM( Ap2.Session_Time ), 0 ) as Total_Session_Time
   from
      ( SELECT MONTH(DATE_ADD(NOW(), INTERVAL aInt MONTH)) AS aMonth
           FROM
            (
                SELECT 0 AS aInt 
                    UNION SELECT -1 
                    UNION SELECT -2 
                    UNION SELECT -3 
                    UNION SELECT -4 
                    UNION SELECT -5
            ) aCnt ) AllMonths
      LEFT JOIN
         ( select
                 appstime.AppID,
                 appstime.UserID,
                 MONTH( appstime.Usage_Start_Time ) as PerMonth,
                 MAX( appstime.Usage_Start_Time ) as ThisTime
              from 
                 ( select @StartDate := last_day( now() - interval 6 month ) + interval 1 day ) sqlvars,
                 appstime
              where
                     appstime.Usage_Start_Time >= @StartDate
                 AND appstime.UserID = 12
              group by
                 appstime.AppID,
                 appstime.UserID,
                 MONTH( appstime.Usage_Start_Time ) ) PQ
         ON AllMonths.aMonth = PQ.PerMonth
         LEFT JOIN appstime Ap2
             ON PQ.AppID = Ap2.AppID
            AND PQ.UserID = Ap2.UserID
            AND PQ.ThisTime = Ap2.Usage_Start_Time
   group by         
      AllMonths.aMonth

答案 3 :(得分:0)

您需要使用聚合函数(即group by):

select   userid,
         month(usage_start_time) as month,
         sum(session_time) as total_session_time
from     appstime
group by userid,
         month(usage_start_time);

编辑要仅在session_timeuserid显示任何月份的最新appid。请注意,调用此字段total_session_time可能不是一个好主意!

select    latest.userid,
          latest.appid,
          month(latest.usage_start_time) as month,
          latest.session_time as latest_session_time

from      appstime as latest

left join appstime as later
on        later.userid           = latest.userid
and       later.appid            = latest.appid
and       later.usage_start_time > latest.usage_start_time

where     later.id is null;

答案 4 :(得分:0)

如果我理解你的问题是正确的,那么这应该是你的查询(当前日期的IBM DB2语法):

SELECT MONTH(USAGE_START_TIME) AS MONTH, SESSION_TIME as TOTAL_SESSION_TIME
FROM appstime
WHERE (MONTH(CURRENT DATE) - MONTH(USAGE_START_TIME)) BETWEEN 0 AND 6
GROUP BY MONTH(USAGE_START_TIME)
HAVING SUM(SESSION_TIME)

这会计算过去6个月内每月的所有会话时间。

干杯。

编辑: 这将在当月从同一个应用程序中获取所有应用程序调用。要从同一个应用程序ID获取最后一个调用,您需要一个存储过程来执行此操作。如果日期大于同月的最后一个日期,则每个月有6个变量,用于检查选择循环。我建议您使用MySQL的等效DAYS(当前日期)功能来比较自0年以来的日期。