Я хочу оптимизировать обновление на основе курсора или фактически заменить его...
Ситуация
Мы проводим рекламные кампании, и я хотел бы отслеживать активность пользователей для каждой кампании.
Логика
Каждая кампания помещается в определенный пакет — сегмент нашей клиентской базы.
CREATE TABLE `segments` (
`campaign_id` int(6) DEFAULT NULL,
`customer_id` varchar(20) DEFAULT NULL,
`tracking_start_date` date DEFAULT NULL,
`tracking_end_date` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
tracking_start_date — это дата кампании, а tracking_end_date — дата окончания отслеживания.
Каждая кампания имеет свой собственный «Призыв к действию (CTA)», который является типом транзакции, которую мы продвигаем и надеемся, что клиенты начнут использовать ее после кампании.
CREATE TABLE `cta` (
`campaign_id` int(11) DEFAULT NULL,
`Date` date DEFAULT NULL,
`segment` varchar(100) DEFAULT NULL,
`message` varchar(320) DEFAULT NULL,
`Size` int(11) DEFAULT NULL,
`cta` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
По умолчанию tracking_end_date в таблице сегментов установлен на последний день месяца, но я создал процедуру для проверки и обновления этого поля. (Идентификатор кампании выдается последовательно в соответствии с датой кампании, поэтому самая ранняя кампания имеет наименьшее значение идентификатора кампании, и наоборот). Отслеживание выполняется по календарному месяцу за месяцем.
Сценарий обновления
Для каждой записи в таблице сегментов проверьте, появляется ли тот же самый customer_id в будущей кампании и имеет ли эта кампания с большей tracking_start_date тот же CTA.
Если TRUE: измените tracking_end_date для этой записи на день перед новой кампанией.
Если FALSE: сохранить последний день месяца tracking_start_date в качестве tracking_end_date.
Если обновление не будет выполнено, то мы будем иметь двойной/тройной... учет транзакций для клиентов, которые появляются в нескольких кампаниях и имеют один и тот же CTA.
Ниже приведена процедура, которую я сейчас использую, но проблема в том, что она слишком медленная.
Эта процедура находится в другой процедуре, которая перебирает идентификаторы кампании за месяц, а затем вызывает эту процедуру, предоставляя соответствующий идентификатор кампании.
CREATE DEFINER=`root`@`localhost` PROCEDURE `set_campaign_end_date_child`(IN var_campaign_id INT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE var_customer_id VARCHAR(20);
DECLARE var_tracking_start_date DATE;
DECLARE cur1 CURSOR FOR SELECT DISTINCT customer_id FROM segments WHERE campaign_id =var_campaign_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- perform cursur update loop now
OPEN cur1;
read_loop: LOOP
IF done THEN
LEAVE read_loop;
END IF;
FETCH cur1 INTO var_customer_id;
SELECT DISTINCT DATE INTO var_tracking_start_date FROM cta WHERE campaign_id = var_campaign_id;
UPDATE segments SET tracking_end_date =
(SELECT IFNULL(DATE_SUB(MIN(tracking_start_date),INTERVAL 1 DAY),LAST_DAY(var_tracking_start_date)) FROM segments_temp
WHERE customer_id = var_customer_id
AND campaign_id
IN(SELECT campaign_id FROM cta WHERE cta IN (SELECT cta FROM cta WHERE campaign_id = var_campaign_id)
AND campaign_id > var_campaign_id))
WHERE customer_id = var_customer_id AND campaign_id =var_campaign_id ;
END LOOP read_loop;
CLOSE cur1;
END$$
DELIMITER ;
PS: перед началом процедуры я делаю копию таблицы сегментов в другой таблице с именем segments_temp и выполняю сравнение оттуда (это связано с тем, что MySQL не может выполнять обновления из самоссылающегося запроса)
Надеюсь, я ясно и заранее спасибо за ваши идеи
UPDATE
, используя самосоединение вместо подзапроса. - person Barmar   schedule 27.03.2015