简体   繁体   English

SQL查询非常大的问题

[英]Issues with a very large SQL query

Problem: 问题:

I'm creating a Trac report that shows how many tickets are at each stage in our development cycle per chapter of our library. 我正在创建一个Trac报告,显示我们库中每章开发周期中每个阶段的票数。 A tickets represents a single piece of work, usually an individual routine. 门票代表单件作品,通常是个人例程。

For instance how many tickets for the upcoming release (milestone) are at the peer review stage for chapter X . 例如,即将发布的版本(里程碑)的票数在X章的同行评审阶段。

There are 10 development stages and 47 chapters. 共有10个开发阶段和47个章节。

The given MySQL query is for all 10 development stages but for only one chapter and is 25 lines long, the whole query for all chapters is therefore over 1200 lines. 给定的MySQL查询适用于所有10个开发阶段,但只有一个章节并且长度为25行,因此所有章节的整个查询超过1200行。

The error given by Trac is KeyError: 'numrows' where the query gets to large. Trac给出的错误是KeyError: 'numrows' ,查询变得很大。

When entering the query directly into MySQL the error given is Out of resources when opening file (Errcode: 24) (23) 当直接向MySQL输入查询时,给出的错误是Out of resources when opening file (Errcode: 24) (23)

Question : 问题

  • Refactoring - can this be done 'better' sql gurus, are there some clever tricks/advance techniques? 重构 - 这可以做'更好'的SQL大师,有一些聪明的技巧/推进技术?

  • Approach - do I need a different approach completely? 方法 - 我是否需要完全不同的方法?

  • Configuration - can MySQL and/or Trac be configured to accept very large queries 配置 - 可以将MySQL和/或Trac配置为接受非常大的查询

Notes: 笔记:

The data in the tables is small, the query doesn't take long to execute when it's under the apparent size limitations. 表中的数据很小,查询在表观大小限制下执行时间不长。

The query is passed from the Trac system to MySQL which places some restrictions on what can be done, for example only a single query can be sent from trac to generate a report. 查询从Trac系统传递到MySQL,这对可以执行的操作设置了一些限制,例如,只能从trac发送一个查询来生成报告。

An example of what a Trac report looks like can be seen here. 可以在此处看到Trac报告的示例

The %c%* in the query is just the unique string I use for replacing the actual chapters when the query is generated via a script. 查询中的%c%*只是我通过脚本生成查询时用于替换实际章节的唯一字符串。

SELECT '%c%' as Chapter,
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status IN ('new','assigned') ) AS 'New',
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='document_interface' ) AS 'Document\
 Interface',
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='interface_development' ) AS 'Inter\
face Development',
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='interface_check' ) AS 'Interface C\
heck',
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='document_routine' ) AS 'Document R\
outine',
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='full_development' ) AS 'Full Devel\
opment',
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='peer_review_1' ) AS 'Peer Review O\
ne',
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%'AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='peer_review_2' ) AS 'Peer Review Tw\
o',
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='qa' ) AS 'QA',
(SELECT count(ticket.id) AS Matches FROM engine.ticket INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%'AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='closed' ) AS 'Closed',
count(id) AS Total,
ticket.id AS _id
FROM engine.ticket
INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine'

Not going to completely rewrite it... but here's my suggestion: 不会完全改写它......但这是我的建议:

SELECT '%c%' as Chapter,
    SUM(CASE WHEN ticket.status IN ('new','assigned') THEN 1 ELSE 0 END) as `New`,
    ...
    SUM(CASE WHEN ticket.status='closed' THEN 1 ELSE 0 END) as 'Closed',
    count(id) AS Total,
    ticket.id AS _id
FROM engine.ticket
INNER JOIN engine.ticket_custom 
    ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' 
    AND ticket_custom.value LIKE '%c%' 
    AND type='New material' 
    AND milestone='1.1.12' 
    AND component NOT LIKE 'internal_engine'
GROUP BY ticket.id
;

Instead of making a subquery for every count, use a case to count from the data that already is fetched for the query: 不是为每个计数创建一个子查询,而是使用一个case来计算已经为查询提取的数据:

select '%c%' as Chapter,
  sum(case when ticket.status IN ('new','assigned') then 1 else 0 end) as 'New',
  sum(case when ticket.status='document_interface' then 1 else 0 end) as 'DocumentInterface',
  sum(case when ticket.status='interface_development' then 1 else 0 end) as 'Interface Development',
  sum(case when ticket.status='interface_check' then 1 else 0 end) as 'Interface Check',
  sum(case when ticket.status='document_routine' then 1 else 0 end) as 'Document Routine',
  sum(case when ticket.status='full_development' then 1 else 0 end) as 'Full Development',
  sum(case when ticket.status='peer_review_1' then 1 else 0 end) as 'Peer Review One',
  sum(case when ticket.status='peer_review_2' then 1 else 0 end) as 'Peer Review Two',
  sum(case when ticket.status='qa' then 1 else 0 end) as 'QA',
  sum(case when ticket.status='closed' then 1 else 0 end) as 'Closed',
  count(id) as Total,
  ticket.id as _id
from
  engine.ticket
  inner join engine.ticket_custom on ticket.id = ticket_custom.ticket
where
  ticket_custom.name='chapter' and
  ticket_custom.value LIKE '%c%' and
  type='New material' and
  milestone='1.1.12' and
  component NOT LIKE 'internal_engine'

When it comes to generating complex reports like this through Trac, it's better to not use a report at all. 当通过Trac生成这样的复杂报告时,最好不要使用报告。 Reports are OK for relatively simple queries, but they become unwieldy when you are organizing that many different stages and chapters. 对于相对简单的查询,报告是可以的,但是当您组织许多不同的阶段和章节时,它们变得难以处理。

Instead, try creating your "report" using a wiki page. 相反,请尝试使用Wiki页面创建“报告”。 This will give you more control over layout and presentation, plus you can avoid writing SQL altogether. 这将使您可以更好地控制布局和演示,还可以避免完全编写SQL。 Here's some sample wiki code for a faux report that shows all non-closed tickets grouped by milestone and then by status: 这是一个虚假报告的示例维基代码,显示按里程碑分组的所有非关闭故障单,然后按状态分组:

= Custom Report =
My custom report, as a wiki page

== Tickets for Milestone A ==
[[TicketQuery(milestone=MilestoneA,status!=closed,group=status,format=table)]]

== Tickets for Milestone B ==
[[TicketQuery(milestone=MilestoneB,status!=closed,group=status,format=table)]]

...

I don't know how you have your stages and chapters defined, so you'll need to tweak the query parameters to use the appropriate ticket fields. 我不知道如何定义您的阶段和章节,因此您需要调整查询参数以使用相应的故障单字段。 You can also play around with format and other options to tweak the output format. 您还可以使用format和其他选项来调整输出格式。

That's just a simple example. 这只是一个简单的例子。 The TicketQuery macro is capable of generating more complex reports. TicketQuery宏能够生成更复杂的报告。 For an example of something that's probably closer (complexity-wise) to what you are looking for, take a look at the Trac project's release notes . 有关可能与您正在寻找的内容更接近(复杂性)的事物的示例,请查看Trac项目的发行说明 The entire "Detailed List of Changes" section is generated with a single TicketQuery macro (hit the 'edit' button to see how they did it). 使用单个TicketQuery宏生成整个“详细更改列表”部分(点击“编辑”按钮以查看它们是如何做到的)。


Another example 另一个例子

To re-create the chart that you linked to, you can do something like this: 要重新创建链接到的图表,可以执行以下操作:

||= **Id** =||= **Enhancements** =||= **Defects** =||= **Tasks** =||
||[milestone:v1.0 v1.0] || [[TicketQuery(milestone=v1.0,type=enhancement,format=count)]] || [[TicketQuery(milestone=v1.0,type=defect,format=count)]] || [[TicketQuery(milestone=v1.0,type=task,format=count)]] ||
... repeat for each milestone ...

Essentially, using format=count in the macro gives you what the database is returning for one of the sum(case ...) statements in Guffa's answer. 基本上,在宏中使用format=count可以为Guffa的答案中的一个sum(case ...)语句提供数据库返回的内容。

The big benefit of doing this using macros instead of direct SQL is that it becomes database-agnostic. 使用宏而不是直接SQL来做这件事的最大好处是它变得与数据库无关。 You don't have to worry about differences between database engines (mysql, sqlite, etc), changes in Trac's database layout, special code for handling custom fields, etc. 您不必担心数据库引擎(mysql,sqlite等)之间的差异,Trac数据库布局的更改,处理自定义字段的特殊代码等。

Yeek, that's ugly. 是的,那很难看。 Do you HAVE to fetch all that data in a single row? 您是否必须在一行中获取所有数据? That many subqueries is going to slam the server. 许多子查询将抨击服务器。 Can you not do a standard grouped fetch and do the pivot table stuff client-side? 你不能做一个标准的分组抓取并做客户端的数据透视表吗?

eg 例如

SELECT count(ticket.id) AS Matches, ticket_custom.name, ticket.status
FROM engine.ticket
INNER JOIN engine.ticket_custom ON ticket.id = ticket_custom.ticket
WHERE ticket_custom.name='chapter' AND ticket_custom.value LIKE '%c%' AND type='New material' AND milestone='1.1.12' AND component NOT LIKE 'internal_engine' AND ticket.status='qa' AND (ticket.status IN (........))
GROUP BY ticket.id, ticket_custom.name

then, in pseudo-code: 然后,在伪代码中:

data = array()
while(row = fetch($result)) {
   data[ticket.id]][ticket.status] = row[ticket.status];
}

This way you'd be running only one single query, and doing some minor extra work in the fetch logic to recreate what your original query is laboring to do. 这样,您只需运行一个查询,并在获取逻辑中执行一些额外的小工作,以重新创建原始查询正在进行的操作。

I ended up writing my own trac reporting system in php . 我最终在php编写了自己的trac报告系统。 This allows a lot more flexibility to create the kind of reports I need and doesn't involve ridiculous mysql queries . 这允许更灵活地创建我需要的报告类型,并且不涉及荒谬的mysql queries Too intergrate the script with the trac project the report items ie the number of tickets at stage QA in chapter XX are hyperlinks to the tickets themselves in trac: http://myhost.co.uk/trac-project/query?id=10&id=15 . 将脚本与trac项目集成在一起的报告项目, 即第XX章阶段QA的门票数量是 trac中门票本身的超链接: http ://myhost.co.uk/trac-project/query?id = 10& id = 15 Also the NavAddPluggin allows the main trac navigation bar to be customized so a menu option that links to the script can be added. 此外, NavAddPluggin允许自定义主trac导航栏,因此可以添加链接到脚本的菜单选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM