Задавайте вопросы, мы ответим
Вы не зашли.
Страниц: 1
Заранее извиняюсь, если повтор, но поиском не нашел ничего похожего.
Имеем, упрощенно, таблицу, в которой хранится детализация звонков за календарный месяц:
CREATE TABLE IF NOT EXISTS `detail` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`client_id` bigint(20) DEFAULT NULL COMMENT 'ID Клиента',
`timestamp` timestamp NOT NULL COMMENT 'Время звонка',
`dec_duration` decimal(7,2) NOT NULL DEFAULT '0.00' COMMENT 'Длительность звонка',
`cost` decimal(7,2) NOT NULL COMMENT 'Стоимость звонка',
PRIMARY KEY (`id`),
KEY `client_id` (`client_id`)
) ENGINE=MyISAM COMMENT='Детализация' AUTO_INCREMENT=1;
Необходимо каждому `client_id` изменить (обнулить) `cost` первых звонков с начала календарного месяца, сумма `dec_duration` которых <= 100.
Так называемый, "пакет включенных минут".
Сейчас мне приходится это делать руками на языке.
Неактивен
Напишите программу, которая это будет делать для каждого человека: простого
хорошего способа нет (хотя бы потому что не понятно, что делать с одним един-
ственным звонком на 102 минуты).
Неактивен
Программа-то есть, просто количество клиентов > 10k и лавинообразно увеличивается.
Расчетом стоимости пограничного звонка пока готов пожертвовать.
Неактивен
Даже если Вы готовы пожертвовать стоимостью пограничного звонка, всё равно
одним запросом это не сделаешь База данных умеет или группировать, или не
группировать.
Если, например, денормализовать данные, и писать помимо длины звонка еще и
суммарную длину звонков, то тогда можно всё свести к одному запросу. Но всё
равно прийдется где-то исправлять программу (в этом случае — в месте, где пи-
шется информация о звонке).
Неактивен
Что-ж, спасибо за ответ.
По крайней мере не буду тратить время на решение неразрешимой задачки
Неактивен
Продолжаем разговор.
Запихнуть во временную таблицу список `id` из `detail`, собраный следующим селектом:
SELECT * FROM (SELECT `id`, `client_id`, `timestamp`, `dec_duration`, (SELECT SUM(`dec_duration`) FROM `detail` WHERE `timestamp` <= `t1`.`timestamp` AND `client_id` = `t1`.`client_id`) `dur_sum` FROM `detail` `t1` ORDER BY `timestamp`) `t2` WHERE `t2`.`dur_sum` <= 100;
Далее UPDATE `detail`, __tmp_table SET `detail`.`cost` = 0 WHERE `detail`.`id` = __tmp_table.id;
Следующий шаг: Помогите описать UPDATE с использованием вышеуказанного селекта без временной таблицы.
PS.
Задачка, на самом деле, оказалась стандартной и имеет название "Накопительные итоги"
http://www.sql-tutorial.ru/ru/book_running_totals.html
Отредактированно Supremum (05.04.2011 11:46:24)
Неактивен
Собственно, я теперь уже и сам, наверно:
UPDATE `detail` INNER JOIN (SELECT `id`, `client_id`, `timestamp`, `dec_duration`, (SELECT SUM(`dec_duration`) FROM `detail` WHERE `timestamp` <= `t1`.`timestamp` AND `client_id` = `t1`.`client_id`) `dur_sum` FROM `detail` `t1` ORDER BY `timestamp`) `t2` ON `t2`.`id` = `detail`.`id` SET `detail`.`cost` = 0 WHERE `t2`.`dur_sum` <= 100;
И, отбросив лишнее:
UPDATE `detail` INNER JOIN (SELECT `id`, (SELECT SUM(`dec_duration`) FROM `detail` WHERE `timestamp` <= `t1`.`timestamp` AND `client_id` = `t1`.`client_id`) `dur_sum` FROM `detail` `t1` ORDER BY `timestamp`) `t2` ON `t2`.`id` = `detail`.`id` SET `detail`.`cost` = 0 WHERE `t2`.`dur_sum` <= 100;
Остается открытым вопрос о порганичном звонке:
Его стоимость необходимо расчитать из исходной стоимости, уменьшив ёё пропорционально количеству остатка бесплатных минут пакета, пришедшихся на этот звонок.
Был бы благодарен за подсказку.
Неактивен
А вот и тот самый невозможный один запрос, который полностью решает задачу с учетом пограничной минуты:
UPDATE `detail` INNER JOIN (SELECT `t2`.`id`, IFNULL(`t2`.`cost`*(GREATEST(0, `t2`.`dur_sum` - 100)/`t2`.`dec_duration`), 0) AS `new_cost` FROM (SELECT `id`, `dec_duration`, `cost`, (SELECT SUM(`dec_duration`) FROM `detail` WHERE `timestamp` <= `t1`.`timestamp` AND `client_id` = `t1`.`client_id`) `dur_sum` FROM `detail` `t1` ORDER BY `timestamp`) `t2` WHERE `t2`.`dur_sum` <= 100 UNION SELECT `t2`.`id`, IFNULL(`t2`.`cost`*(GREATEST(0, `t2`.`dur_sum` - 100)/`t2`.`dec_duration`),0) AS `new_cost` FROM (SELECT `id`, `dec_duration`, `cost`, (SELECT SUM(`dec_duration`) FROM `detail` WHERE `timestamp` <= `t1`.`timestamp` AND `client_id` = `t1`.`client_id`) `dur_sum` FROM `detail` `t1` ORDER BY `timestamp`) `t2` WHERE `t2`.`dur_sum` > 100 GROUP BY `client_id`) `t3` ON `t3`.`id` = `detail`.`id` SET `detail`.`cost` = `t3`.`new_cost`;
Да, пограничная минута минимум в 2 раза увеличивает накладные расходы, но тем не менее - это решение.
Всем пожалуйста.
Отредактированно Supremum (05.04.2011 17:08:01)
Неактивен
Аааа, какой страшный зверь. Я бы сделал кучкой маленьких простых
запросов. Потому что когда Вам нужно будет исправить в этом ошибку
(или что-то поменять) — я Вам не завидую
Неактивен
Страниц: 1