Я создаю сценарий CGI, который опрашивает базу данных SQLite и создает таблицу статистики. Таблица исходной базы данных описана ниже, как и часть соответствующего кода. Все работает (функционально), но сам CGI работает очень медленно, так как у меня несколько вложенных вызовов SELECT COUNT(id)
. Я полагаю, что мой лучший шанс на оптимизацию - спросить SO-сообщество, поскольку мое время с Google было относительно бесплодным.
Таблица:
CREATE TABLE messages (
id TEXT PRIMARY KEY ON CONFLICT REPLACE,
date TEXT,
hour INTEGER,
sender TEXT,
size INTEGER,
origin TEXT,
destination TEXT,
relay TEXT,
day TEXT);
(Да, я знаю, что таблица не нормализована, но она заполнена выдержками из почтового журнала ... Я был достаточно счастлив, чтобы получить извлечение и заполнение, не говоря уже о нормализации. Я не думаю, что таблица На данный момент структура во многом связана с моим вопросом, но я могу ошибаться.)
Пример строки:
476793200A7|Jan 29 06:04:47|6|admin@mydomain.com|4656|web02.mydomain.pvt|user@example.com|mail01.mydomain.pvt|Jan 29
И код Python, который создает мои таблицы:
#!/usr/bin/python
print 'Content-type: text/html\n\n'
from datetime import date
import re
p = re.compile('(\w+) (\d+)')
d_month = {'Jan':1,'Feb':2,'Mar':3,'Apr':4,'May':5,'Jun':6,'Jul':7,'Aug':8,'Sep':9,'Oct':10,'Nov':11,'Dec':12}
l_wkday = ['Mo','Tu','We','Th','Fr','Sa','Su']
days = []
curs.execute('SELECT DISTINCT(day) FROM messages ORDER BY day')
for day in curs.fetchall():
m = p.match(day[0]).group(1)
m = d_month[m]
d = p.match(day[0]).group(2)
days.append([day[0],"%s (%s)" % (day[0],l_wkday[date.weekday(date(2010,int(m),int(d)))])])
curs.execute('SELECT DISTINCT(sender) FROM messages')
senders = curs.fetchall()
for sender in senders:
curs.execute('SELECT COUNT(id) FROM messages WHERE sender=%s',(sender[0]))
print ' <div id="'+sender[0]+'">'
print ' <h1>Stats for Sender: '+sender[0]+'</h1>'
print ' <table><caption>Total messages in database: %d</caption>' % curs.fetchone()[0]
print ' <tr><td> </td><th colspan=24>Hour of Day</th></tr>'
print ' <tr><td class="left">Day</td><th>%s</th></tr>' % '</th><th>'.join(map(str,range(24)))
for day in days:
print ' <tr><td>%s</td>' % day[1]
for hour in range(24):
sql = 'SELECT COUNT(id) FROM messages WHERE sender="%s" AND day="%s" AND hour="%s"' % (sender[0],day[0],str(hour))
curs.execute(sql)
d = curs.fetchone()[0]
print ' <td>%s</td>' % (d>0 and str(d) or '')
print ' </tr>'
print ' </table></div>'
print ' </body>\n</html>\n'
Я не уверен, есть ли какие-либо способы, которыми я могу объединить некоторые запросы или подойти к нему под другим углом, чтобы извлечь данные. Я также думал о создании второй таблицы с подсчетами в ней и просто обновлении ее при обновлении исходной таблицы. Я слишком долго смотрел на это сегодня, поэтому завтра я снова буду атаковать его снова, надеюсь, с некоторой проницательностью от экспертов;)
Редактировать: Используя ответ GROUP BY, представленный ниже, я смог получить необходимые данные из базы данных в одном запросе. Я переключился на Perl, поскольку поддержка Python для вложенных dict не очень хорошо работала для того, как мне нужно было подходить к этому (создание набора таблиц HTML особым образом). Вот фрагмент исправленного кода:
my %data;
my $rows = $db->selectall_arrayref("SELECT COUNT(id),sender,day,hour FROM messages GROUP BY sender,day,hour ORDER BY sender,day,hour");
for my $row (@$rows) {
my ($ct, $se, $dy, $hr) = @$row;
$data{$se}{$dy}{$hr} = $ct;
}
for my $se (keys %data) {
print "Sender: $se\n";
for my $dy (keys %{$data{$se}}) {
print "Day: ",time2str('%a',str2time("$dy 2010"))," $dy\n";
for my $hr (keys %{$data{$se}{$dy}}) {
print "Hour: $hr = ".$data{$se}{$dy}{$hr}."\n";
}
}
print "\n";
}
То, что когда-то выполнялось за 28.024 с, теперь занимает 0.415 с!