我正在开发一个从各种来源获取数据并生成报告的应用程序。目前我正在将其更改为根据历史记录中给定日期的数据进行报告,之前它仅显示来自当前状态的数据。
我的一个数据源是Bugzilla,因此我需要获取历史记录中给定日期的Bugzilla数据。我有一个与Bugzilla数据库的只读连接,但没有简单的方法对服务器做任何其他事情(比如安装插件,或者在数据库中放置程序)。此外,报表服务器和Bugzilla服务器之间的连接速度很慢,因此我想在服务器上进行计算,而不是在报表服务器上获取数据并进行处理。
我实际上以最可接受的速度工作,但我不确定我是以最佳方式还是“正确”方式工作,我担心速度可能会因为我们添加更多而无法接受问题到数据库。
所以,我的解决方案如下 - 你会怎么做
对于一些背景知识,Bugzilla将表中所有错误的当前状态(称为“bugs”)和表中每个字段(“bugs_activity”)的更改历史记录存储起来,如下所示: / p>
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
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
;
然后我将其用作选择的输入,该选择计算不同的状态等。
这个查询结果太慢了,我假设是因为它获得了内部选择的整个结果,所以它可以命令然后给我最高的一个。
我确实尝试通过LEFT将bugs_activity表连接到bugs表上几次,然后对结果进行IFNULL查询,这很快但在生成代码中维护有点复杂,所以适应它: / p>
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
;
你需要两个LIMIT 1(我认为),因为有些字段已经设法在同一个时间戳上进行了两次更改(数据库故障,可能来自升级,或者两个用户编辑同一个bug - 我是不确定,我只知道它在那里,我需要处理它)。
这在大约3秒内运行,没有过滤器来减少错误列表(这是最糟糕的情况,几乎不会发生),并且它使用过滤器运行得更快。 LEFT JOIN版本以大致相同的速度运行(稍慢),所以我选择了上面的那个。目前还可以,但我可以看到它在未来变得缓慢 - 我将在GUI中添加加载消息,并且已经有消息说这些报告可能需要更长时间才能生成,我只是想知道我是否'我错过了一些让它更快的技巧。
答案 0 :(得分:1)
如果我找到你的话,你可以试试这个......
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
抱歉,我这里没有数据库来验证代码,如果有拼写错误或类似的话,很抱歉..
我不知道你之前是否正在进行左外连接,但是如果你使用会话变量重复使用会有帮助/工作吗?
不确定这是否会有任何帮助,因为你说你的左外连接方法无论如何都以相同的速度运行..也许mysql查询优化器可以找到一个更好的方法来做到这一点:/ < / p>
顺便说一句,我不是优化专家(远离它)..只是说出我想尝试的东西,而不是在旅途中得到一些索引的好建议。
修改强>
你可以尝试另一件事......我认为这应该有效..
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
我遗漏了来自外部选择的案例或if语句。我在考虑无论哪种解决方案值得测试它如何运行代码而不是数据库进行最终检查?即使它有效,你可能也不会选择它,但它可能值得检查。
就像:
<%= row->old_status ?: row->bug_status %>
(对不起,如果我的PHP关闭了..没有真正用过它)
好像应该有用吗? http://sqlfiddle.com/#!2/eff8c/1
答案 1 :(得分:1)
我建议使用Bugzilla REST API接口,而不是直接访问Bugzilla DB。 这是一个用于检索在特定日期创建的错误的示例API。
https://api-dev.bugzilla.mozilla.org/test/1.3/bug?creation_date=2008-03-31
参考文献:
https://wiki.mozilla.org/Bugzilla:REST_API
https://wiki.mozilla.org/Bugzilla:REST_API:Objects
https://wiki.mozilla.org/Bugzilla:REST_API:Search