Более умная сортировка
parent
71e241150e
commit
69e3e13733
101
olap.php
101
olap.php
|
@ -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).': ';
|
||||
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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue