优化Oracle数据库查询/ fetchall

时间:2012-10-27 14:20:06

标签: python oracle query-optimization cx-oracle fetchall

数据库设计远非最佳,但我必须处理它,现在我真的被卡住了。

编辑:我正在使用cx_Oracle

好的,这是我的疑问:

query="select degree, spectraldev.event.eventnumber \
       from spectraldev.degree \
       join spectraldev.alignment on \
            (spectraldev.version_id = alignment.version_id) \
       join spectraldev.event on \
            (alignment.timestamp between event.eventstart and event.eventstop) \
       join spectraldev.eventsetup on \
            (spectraldev.event.eventsetup = spectraldev.eventsetup.oid) \
       where spectraldev.event.eventnumber>=" + options.start + " AND spectraldev.event.eventnumber<=" + options.stop + " AND \
            HITS>=" + str(options.minimum_hits)+" \
       order by spectraldev.event.eventnumber"

db_cursor.execute(query)

为许多事件返回一堆degree s(12.34等),这些事件由唯一编号(eventnumber如346554)标识。

所以我得到一张这样的表:

454544    45.2
454544    12.56
454544    41.1
454544    45.4
454600    22.3
454600    24.13
454600    21.32
454600    22.53
454600    54.51
454600    33.87
454610    32.7
454610    12.99

等等......

现在我需要为每个事件创建一个带有平均度的字典(总结所有相应的浮点数并除以它们的数量)。

我认为这可以在SQL中完成,但我无法让它工作。目前我正在使用python执行此操作,但是fetch命令需要1-2个小时来完成大约2000个事件,这太慢了,因为我需要处理大约1000000个事件。

这是我的提取部分,需要很长时间:

_degrees = []
for degree, eventNumber in cursor.fetchall():
    _degrees.append([eventNumber, degree])

然后排序(这真的很快,<1秒)并计算平均值(也非常快):

_d={}
for eventNumber, degree in _degrees:
    _d.setdefault(eventNumber, []).append(degree)

for event in events:
    _curDegree = _degrees[int(event)]
    _meanDegree = sum(_curDegree) / float(len(_curDegree))
    meanDegrees.append(_meanDegree)

有没有办法在SQL中执行python部分?

1 个答案:

答案 0 :(得分:1)

这是一个旁边,但重要的一个。您对SQL Injection非常开放。 可能在您的特定实例中无关紧要,但最好总是编写最差的代码。

您没有提到您正在使用的模块,但假设它符合PEP 249(您可能正在使用cx_Oracle),那么您可以传递带有命名绑定参数的字典。典型的查询可能如下所示:

query = """select column1 from my_table where id = :my_id"""
bind_vars = {'my_id' : 1}

db_cursor.execute(query, bind_vars)

在实际查询中,您将一些变量(例如options.start)转换为Python中的字符串,但不在SQL中引用它们,这意味着它们被隐式转换回数字。这几乎绝对不需要。


关于你的实际问题1-2小时完成2000个事件,你是对的,很荒谬。您尚未发布架构,但我的猜测是您缺少任何indexes

要获得每个事件编号的平均学位数,您应该使用avg()函数。这将使您的查询:

select spectraldev.event.eventnumber, avg(degree) as degree
  from spectraldev.degree
  join spectraldev.alignment 
        -- I think this is wrong on your query
    on (degree.version_id = alignment.version_id)
  join spectraldev.event 
    on (alignment.timestamp between event.eventstart and event.eventstop)
  join spectraldev.eventsetup 
    on (spectraldev.event.eventsetup = spectraldev.eventsetup.oid)
 where spectraldev.event.eventnumber >= :start
   and spectraldev.event.eventnumber <= :stop
   and hits >= :minimum_hits
 group by spectraldev.event.eventnumber
 order by spectraldev.event.eventnumber

我已经对您的查询进行了格式化,使其更具可读性(从我的角度来看),并使您在需要索引时更加明显。

由此判断,您需要对以下表格和列进行索引;

  • 活动 - eventnumbereventstarteventstopeventsetup
  • DEGREE - version_id
  • 对齐 - version_idtstamp
  • EVENTSETUP - oid

以及hits可能在哪里。

说完所有问题可能 索引。您尚未提供解释计划或架构或行数,因此这将是一个猜测。但是,如果您在表中选择了很大一部分行,那么CBO 可能正在使用索引。例如,使用full hint/*+ full(event) */强制进行全表扫描可能会解决您的问题。

删除order by,如果不需要,也可以显着加快查询速度。