Более умная сортировка

master
vitalif 2010-08-14 20:17:15 +00:00
parent 71e241150e
commit 69e3e13733
1 changed files with 79 additions and 22 deletions

101
olap.php
View File

@ -1,6 +1,7 @@
<?php
# Простая OLAPообразная статистика
# См. http://yourcmc.ru/wiki/Общий_вид_статистики
# admin_olap.tpl, admin_olap_csv.tpl
# (c) Vitaliy Filippov 2010
@ -16,7 +17,6 @@ class OLAP
{
static $sources;
static $current_srcid, $current_src;
static $current_spec;
static $add_spanned;
static $functions = array(
'year' => array('name' => 'Год', 'time_format' => 'Y', 'sql' => 'SUBSTR($,1,4)'),
@ -54,6 +54,8 @@ class OLAP
foreach (self::$sources as $k => &$s)
$s['id'] = $k;
/* выбираем источник данных */
self::$current_srcid = $request['datasource'];
self::$current_src = self::$sources[self::$current_srcid];
$template->vars('sources', array_values(self::$sources));
@ -64,10 +66,12 @@ class OLAP
exit;
}
/* задаём параметры соединения с БД из источника*/
global $db, $dbhost, $dbuser, $dbpwd;
if (self::$current_src['db'])
list($db, $dbhost, $dbuser, $dbpwd) = self::$current_src['db'];
/* дополняем всяким бредом всякий бред */
foreach (self::$current_src['fielddescs'] as $k => &$v)
{
$v['id'] = $k;
@ -88,11 +92,13 @@ class OLAP
foreach (self::$group_types as $k => $v)
self::$group_types[$k] = array('id' => $k, 'num' => $i++, 'name' => $v);
/* читаем параметры запроса */
$wh = array();
$where = array();
$group_fields = array();
foreach ($request as $k => $v)
{
/* условия выборки: where-ОПЕРАЦИЯ-ПОЛЕ = ЗНАЧЕНИЕ */
if (substr($k, 0, 6) == 'where-')
{
list($t, $f) = explode('-', substr($k, 6), 2);
@ -115,6 +121,7 @@ class OLAP
else
unset($wh[$k]);
}
/* описание группировки */
elseif (substr($k, 0, 11) == 'group-type-')
{
$i = substr($k, 11);
@ -136,6 +143,7 @@ class OLAP
if (count($group_fields) > $nfields)
$nfields = count($group_fields);
/* описание показателя */
foreach(array('field', 'func', 'aggr') as $x)
self::$cell[$x] = $request["cell_$x"];
@ -160,12 +168,12 @@ class OLAP
}
$fields = array();
$options = array('GROUP BY' => array());
$group_by = array();
foreach ($group_fields as $i => &$v)
{
$v['sql'] = self::sql_trans_field($v['field'], $v['func']);
$fields[] = $v['sql'].' gf_'.$i;
$options['GROUP BY'][] = $v['sql'];
$fields[] = $v['sql'].' f'.$i;
$group_by[] = $v['sql'];
}
$cell['sql'] = self::sql_trans_field($cell['field'], $cell['func']);
@ -173,12 +181,14 @@ class OLAP
$fields[] = str_replace('$', $cell['sql'], self::$aggregates[$cell['aggr']]['sql']).' c';
else
{
/* поддерживаются агрегатные функции, вычисляемые внутри PHP, а не в базе */
$do_aggr = true;
$fields[] = $cell['sql'].' c';
$options['GROUP BY'][] = $cell['sql'];
$group_by[] = $cell['sql'];
}
$result = mysql_select(self::$current_src['tables'], $fields, array_merge(self::$current_src['where'], $where), $options, MS_RESULT);
/* сохраняем данные в $data и наборы значений для колонок в $tdkeys */
$result = mysql_select(self::$current_src['tables'], $fields, array_merge(self::$current_src['where'], $where), array('GROUP BY' => $group_by), MS_RESULT);
$data = array();
$tdkeys = array();
while ($r = mysql_fetch_assoc($result))
@ -187,13 +197,13 @@ class OLAP
$tdv = &$tdkeys;
foreach ($group_fields as $i => &$v)
{
$v['keys'][$r["gf_$i"]] = true;
$dv = &$dv[$r["gf_$i"]];
$v['keys'][$r["f$i"]] = true;
$dv = &$dv[$r["f$i"]];
if ($v['type'] == 'tables' || $v['type'] == 'td')
{
if (!$tdkeys[$r["gf_$i"]])
$tdkeys[$r["gf_$i"]] = array();
$tdv = &$tdv[$r["gf_$i"]];
if (!$tdv[$r["f$i"]])
$tdv[$r["f$i"]] = array();
$tdv = &$tdv[$r["f$i"]];
}
}
if ($do_aggr)
@ -202,6 +212,7 @@ class OLAP
$dv = $r['c'];
}
/* поддерживается "завершение" вычисления агрегатных функций, которые вне базы */
if ($do_aggr && is_callable('self::aggr_finish_'.$cell['aggr']))
{
$code = '';
@ -215,19 +226,35 @@ class OLAP
eval($code);
}
foreach ($group_fields as &$v)
/* читаем значения, по которым следует сортировать значения полей группировки */
foreach ($group_fields as $i => &$v)
{
$fields = array();
$group_by = array();
if (strlen($v['sort_aggr']) && strlen($v['sort_field']))
{
$fields = array($v['sql'].' f');
$code = 'while($r = mysql_fetch_assoc($result)) { $v[\'sort\']';
foreach ($group_fields as $j => $prev)
{
if ($j > $i)
break;
/* сюда также включается само поле $v! */
if ($prev['type'] == 'tables' || $prev['type'] == $v['type'])
{
$fields[] = $prev['sql']." f$j";
$group_by[] = $prev['sql'];
$code .= "[\$r['f$j']]";
}
}
$code .= ' = $r[\'s\']; }';
$v['sort_sql'] = self::sql_trans_field($v['sort_field'], $v['sort_func']);
$fields[] = str_replace('$', $v['sort_sql'], self::$aggregates[$v['sort_aggr']]['sql']).' s';
$result = mysql_select(self::$current_src['tables'], $fields, array_merge(self::$current_src['where'], $where), array('GROUP BY' => $v['sql']), MS_RESULT);
while ($r = mysql_fetch_row($result))
$v['sort'][$r[0]] = $r[1];
$result = mysql_select(self::$current_src['tables'], $fields, array_merge(self::$current_src['where'], $where), array('GROUP BY' => $group_by), MS_RESULT);
eval($code);
}
$v['keys'] = array_keys($v['keys']);
self::do_sort($v['keys'], $v['sort'], $v['sort_dir']);
if (count($group_by) <= 1)
self::do_sort($v['keys'], $v['sort'], $v['sort_dir']);
}
if ($request['csv'])
@ -261,10 +288,18 @@ class OLAP
{
$tables = array();
$my = array_shift($fields);
foreach ($my['keys'] as $k)
foreach ($fields as &$v)
if ($v['sort'])
$v['orig_sort'] = &$v['sort'];
$keys = $my['sort'] ? array_keys($my['sort']) : $my['keys'];
self::do_sort($keys, $my['sort'], $my['sort_dir']);
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
foreach ($fields as &$v)
if ($v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$sub = self::recurse_tables($fields, $bytype, $data[$k], $tdkeys[$k]);
foreach ($sub as &$t)
{
@ -349,13 +384,21 @@ class OLAP
if (!$fields)
return self::field_format(self::$cell['field'], self::$cell['func'], self::$cell['aggr'], $data);
$my = array_shift($fields);
foreach ($fields as &$v)
if ($v['sort'])
$v['orig_sort'] = &$v['sort'];
$keys = $my['sort'] ? array_keys($my['sort']) : $my['keys'];
self::do_sort($keys, $my['sort'], $my['sort_dir']);
if ($my['type'] == 'tr')
{
$rows = array();
foreach ($my['keys'] as $k)
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
foreach ($fields as &$v)
if ($v['type'] == $my['type'] && $v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$r = self::recurse_cells($fields, $data[$k], $tdkeys);
if (is_array($r))
$rows = array_merge($rows, $r);
@ -368,10 +411,13 @@ class OLAP
elseif ($my['type'] == 'td')
{
$cols = array();
foreach ($my['keys'] as $k)
foreach ($keys as $k)
{
if (array_key_exists($k, $tdkeys))
{
foreach ($fields as &$v)
if ($v['type'] == $my['type'] && $v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$r = self::recurse_cells($fields, $data[$k], $tdkeys[$k]);
if (is_array($r))
$cols = array_merge($cols, $r[0]);
@ -384,10 +430,13 @@ class OLAP
else // if ($my['type'] == 'cell')
{
$list = '<ul>';
foreach ($my['keys'] as $k)
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
foreach ($fields as &$v)
if ($v['type'] == $my['type'] && $v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$list .= '<li>'.self::field_format($my['field'], $my['func'], '', $k).':&nbsp;';
if ($fields)
$list .= "\n";
@ -409,10 +458,18 @@ class OLAP
return $rows;
$rows[] = array();
$my = array_shift($fields);
foreach ($my['keys'] as $k)
foreach ($fields as &$v)
if ($v['sort'])
$v['orig_sort'] = &$v['sort'];
$keys = $my['sort'] ? array_keys($my['sort']) : $my['keys'];
self::do_sort($keys, $my['sort'], $my['sort_dir']);
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
foreach ($fields as &$v)
if ($v['type'] == $my['type'] && $v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$span = 0;
if ($fields)
{