BugZilla,在给定日期获得错误状态(等)的最佳(最快)方式

Spa*_*Dog 5 sql bugzilla report

我正在开发一个从各种来源获取数据并生成报告的应用程序.目前我正在将其更改为根据历史记录中给定日期的数据进行报告,之前它仅显示来自当前状态的数据.

我的一个数据源是Bugzilla,因此我需要获取历史记录中给定日期的Bugzilla数据.我有一个与Bugzilla数据库的只读连接,但没有简单的方法对服务器做任何其他事情(比如安装插件,或者在数据库中放置程序).此外,报表服务器和Bugzilla服务器之间的连接速度很慢,因此我想在服务器上进行计算,而不是在报表服务器上获取数据并进行处理.

我实际上以最可接受的速度工作,但我不确定我是以最好还是'正确'的方式做到这一点,我担心速度可能会停止接受,因为我们在数据库.

所以,我的解决方案如下 - 你会怎么做

对于一些背景知识,Bugzilla将表中所有错误的当前状态(称为"bugs")和表中每个字段("bugs_activity")的更改历史记录存储起来,如下所示:

fieldid   INTEGER,   -- References the fielddefs table
bug_when  TIMESTAMP, -- Time the change happend
added     TEXT,      -- New text for the field
removed   TEXT,      -- Old text for the field
Run Code Online (Sandbox Code Playgroud)

Bugzilla数据库是MySQL.我认为正确的方法是使用存储过程或临时表,但我没有任何选项可用.我知道还有Bugzilla的报告工具,但是我没有安装它们的权限,我生成的报告还包含来自其他来源的数据(并且具有特定的格式).

报告服务器上有一个本地PostgreSQL数据库,所以我可以定期将所有数据镜像到那里,但我真的不想这样做,因为在两个地方存储相同的数据似乎有点浪费.

我的解决方案是在子选择中构建一个看起来像普通错误表的表(对于我对给定报告感兴趣的数据),然后使用此选择作为正常选择的源,其工作方式与查询相同报告基于今天的数据.

SELECT bug_status, bug_id, op_sys, resolution, rep_platform   
  FROM (SELECT bug_id, 
        IFNULL((SELECT removed FROM bugs_activity a, fielddefs f  
                 WHERE a.fieldid = f.id   
                   AND bug_id = b.bug_id AND f.name = 'bug_status' 
                   AND bug_when >= '2012-01-01 00:00:00'  
                 ORDER BY bug_when DESC LIMIT 1), bug_status) AS bug_status,
    -- Repeat IFNULL clause for op_sys, resolution and rep_platform
        FROM bugs b 
        WHERE b.creation_ts <= '2012-01-01 00:00:00' ) bug_subselect
        -- Some other filters to reduce the bugs (specific product, ect)
      )
    -- More filters based on the new values that have been derived
     ;
Run Code Online (Sandbox Code Playgroud)

然后我用它作为选择的输入,计算不同的状态等.

这个查询结果太慢了,我假设是因为它获得了内部选择的整个结果,所以它可以命令然后给我最高的一个.

我确实尝试通过LEFT将bugs_activity表连接到bugs表上几次,然后对结果进行IFNULL查询,这很快但在生成代码中维护有点复杂,所以适应它:

SELECT bug_status, bug_id, op_sys, resolution, rep_platform   
  FROM (SELECT bug_id,
    IFNULL((SELECT removed FROM bugs_activity a, fielddefs f 
             WHERE a.fieldid = f.id AND bug_id = b.bug_id AND f.name = 'bug_status'
               AND bug_when = (
                     SELECT MIN(bug_when) FROM bugs_activity a, fielddefs f 
                      WHERE a.fieldid = f.id 
                            AND bug_id = b.bug_id 
                            AND f.name = 'bug_status'
                        AND bug_when >= '2012-01-01 00:00:00' 
                          LIMIT 1 
                        )
             LIMIT 1), bug_status) AS bug_status,
        -- Repeat IFNULL clause for op_sys, resolution and rep_platform
        FROM bugs b 
        WHERE b.creation_ts <= '2012-01-01 00:00:00' ) bug_subselect
        -- Some other filters to reduce the bugs (specific product, ect)
      )
    -- More filters based on the new values that have been derived
      ;
Run Code Online (Sandbox Code Playgroud)

你需要两个LIMIT 1(我认为),因为有些字段已经设法在同一个时间戳上进行了两次更改(数据库故障,可能来自升级,或者两个用户编辑相同的bug - 我不确定,我只知道它在那里,我需要处理它).

这在大约3秒内运行,没有过滤器来减少错误列表(这是最糟糕的情况,几乎不会发生),并且它使用过滤器运行得更快.LEFT JOIN版本以大致相同的速度运行(稍慢),所以我选择了上面的那个.目前还可以,但我可以看到它在未来变得缓慢 - 我将在GUI中添加加载消息,并且已经有消息说这些报告可能需要更长时间才能生成,我只是想知道我是否'我错过了一些让它更快的技巧.

小智 2

如果我的理解正确,你可以尝试这个..

SET @tdate = '2012-01-01 00:00:00';

SELECT  
  b.bug_id
  ,CASE 
    WHEN s.removed IS NULL THEN b.bug_status
    ELSE s.removed
  END AS statusAtDate
  ,CASE 
    WHEN o.removed IS NULL THEN b.op_sys
    ELSE o.removed
  END AS apSysAtDate
FROM
  bugs AS b 
  LEFT OUTER JOIN (
    SELECT 
      a.bug_id
      ,a.bug_when
      ,a.removed
      ,a.bug_when
      ,@row_num := IF(@last=a.bug_id,@row_num+1,1) AS rnk
      ,@last:=a.bug_id
    FROM 
      bug_activity AS a
      INNER JOIN fielddefs AS f
        ON a.fieldid = f.id
          AND f.name = 'bug_status'
    WHERE
        a.bug_when <= @tdate
    ORDER BY 
      a.bug_id
      ,a.bug_when
    ) AS s
      ON b.bug_id = s.bug_id
      AND s.rnk=1
  LEFT OUTER JOIN (
    SELECT 
      a.bug_id
      ,a.bug_when
      ,a.removed
      ,a.bug_when
      ,@row_num := IF(@last=a.bug_id,@row_num+1,1) AS rnk
      ,@last:=a.bug_id
    FROM 
      bug_activity AS a
      INNER JOIN fielddefs AS f
        ON a.fieldid = f.id
          AND f.name = 'op_sys'
    WHERE
        a.bug_when <= @tdate
    ORDER BY 
      a.bug_id
      ,a.bug_when
    ) AS o
      ON b.bug_id = o.bug_id
      AND o.rnk=1

--repeat for resolution and rep_platform
Run Code Online (Sandbox Code Playgroud)

抱歉,我这里没有数据库来验证代码,如果有拼写错误或类似的情况,抱歉。

我不知道这是否是您之前进行左外连接的方式,但是如果您使用会话变量进行重用,这是否有帮助/工作?

不确定这是否有任何帮助,正如您所说的,您的左外连接方法无论如何都以相同的速度运行..也许mysql查询优化器可以找到一种更好的方法来做到这一点,而无需这样做:/

顺便说一句,我不是优化专家(远非如此).. 只是说除了在旅途中获取一些索引的好建议之外我会尝试什么。

编辑:

你可以尝试的另一件事..我认为这应该有效..

SELECT
  bug_id
  ,bug_status
  ,op_sys
  ,max(old_status)
  ,max(old_opSys)
(
SELECT  
  *
FROM
  bugs AS b 
  LEFT OUTER JOIN (
    SELECT 
      a.bug_id
      ,a.bug_when
      ,if(f.name = 'bug_status',a.removed,NULL) AS old_status
      ,if(f.name = 'op_sys',a.removed,NULL) AS old_opSys
      ,a.bug_when
      ,@row_num := IF(@last=a.bug_id AND@lastField=f.name ,@row_num+1,1) AS rnk
      ,@last:=a.bug_id
      ,@lastField:=f.name
    FROM 
      bug_activity AS a
      INNER JOIN fielddefs AS f
        ON a.fieldid = f.id

    WHERE
        a.bug_when <= '2012-01-01 00:00:00'
        AND f.name in( 'bug_status','op_sys')
    ORDER BY 
      a.bug_id
      ,f.name
      ,a.bug_when
    ) AS s
      ON b.bug_id = s.bug_id
      AND s.rnk=1
) AS T
  GROUP BY
    bug_id
    ,bug_status
    ,op_sys
Run Code Online (Sandbox Code Playgroud)

我在这里省略了外部选择中的 case 或 if 语句。我在想,无论哪种解决方案,都值得测试它如何运行,在代码而不是数据库中进行最终检查?即使这有效,您可能不会选择它,但可能值得检查。

就像这样:

<%= row->old_status ?: row->bug_status %>
Run Code Online (Sandbox Code Playgroud)

(抱歉,如果我的 PHP 关闭了..并没有真正使用它)

看起来应该有效?http://sqlfiddle.com/#!2/eff8c/1