2016-03-20 3 views
1

У меня есть довольно тривиальная задача расчета записей бюджета (доход/результат/баланс). Могут быть тысячи записей, и я могу изменить любой из них посередине. В результате все последующие балансы позиций должны быть пересчитаны.Процедуры MySQL - поэтапно пересчитывать строки

Прямо сейчас я делаю это в PHP, итерируя через массив всех записей и обновляя строки, которые изменились. Это занимает слишком много времени - мой сервер перестает отвечать на несколько минут.

Я полагаю, что это происходит из-за того, что PHP вызывает MySQL для каждого обновления записи, хотя для самого PHP эта задача итерации массива и пересчета довольно дешевая. Я думаю, что должен быть способ бросить эту задачу в MySQL, поэтому он сам выполняет итерацию/перерасчет/обновление, что также может быть дешевым.

Я вообще не эксперт в MySQL, но я слышал, что есть хранимые процедуры, которые могут быть излечением.

Вот мой MySQL (5.5.33) таблица:

CREATE TABLE `entry` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `date` date DEFAULT NULL, 
    `is_income` tinyint(1) NOT NULL DEFAULT '0', 
    `income` decimal(20,2) DEFAULT NULL, 
    `outcome` decimal(20,2) DEFAULT NULL, 
    `balance` decimal(20,2) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) 

Вот мой PHP (5.3.27):

//... 
//$DB is a class for operating DB 
$entries = $DB->get_all('entry'); //retrieves all entries from 'entry' table, sorted by date 
$balance = 0; 
foreach ($entries as $e) { 
    if ($e['is_income']) { 
     $balance += $e['income']; 
    } else { 
     $balance -= $e['outcome']; 
    } 
    if ($balance <> $e['balance']) { 
     $e1 = $e; 
     $e1['balance'] = $balance; 
     $DB->update('entry', $e1); //update the row by doing query('UPDATE `entry` ... WHERE id=' . $entry_id); 
    } 
} 

Можете ли вы указать мне правильное направление? Благодаря!

ответ

2

Я думаю, вы можете сделать это в одном запросе SQL UPDATE, никакой процедуры не требуется.

UPDATE entry AS e1 
JOIN (SELECT * FROM entry ORDER BY date) AS e2 ON e1.id = e2.id 
CROSS JOIN (SELECT @balance := 0) AS var 
SET e1.balance = (@balance := @balance + IF(e2.is_income, e2.income, -e2.outcome)) 

Переменная пользователя @balance служит той же цели, что и переменная PHP $balance. Подзапрос необходим, потому что MySQL не разрешает использовать ORDER BY в многозадачном запросе UPDATE, поэтому вам нужно присоединиться к подзапросу, который создает идентификаторы в порядке даты.

+0

Спасибо, Бармар! Это полное решение? Просто запустите этот запрос? –

+1

Должно быть, но я его не тестировал. – Barmar

0

«Правильный» способ состоит в том, чтобы делать суммирование при отображении отчета, а не хранить его в таблице.

Только для «тысяч» это не должно быть проблемой с производительностью.

+0

Это не лучший способ, потому что вам нужно делать расчет времени при каждом отображении данных. Лучше сделать это один раз, сохранить результат, а затем просто показать его. –

+0

Вы измеряли, сколько времени это займет? –

Смежные вопросы