Рекурсивный ОБНОВЛЕНИЕ ДУБЛИКАЦИИ КЛЮЧА

У меня есть таблица базы данных с ключом UNIQUE в столбце даты, так что никогда не может быть более одной строки на дату. Если я вставлю новую строку для дня, который уже существует в базе данных, я хочу, чтобы все последующие последовательные даты отодвигались на один день назад, пока не будет достигнут «свободный» день.

Вот что я подумал:

INSERT INTO
 `activity`
 (`name`,`date`)
VALUES
 ('date 7','2009-07-31')
ON DUPLICATE KEY
 UPDATE `date` = DATE_ADD(`date`, INTERVAL '1' DAY)

Я думаю, что ON DUPLICATE KEY UPDATE будет всплывать в таблице и продолжать добавлять один день к каждой дате, пока не достигнет дня, которого не существует.

Например, если содержимое моей таблицы выглядит так:

date 1, 2009-07-30
date 2, 2009-07-31
date 3, 2009-08-01
date 4, 2009-08-02
date 5, 2009-08-04 <- notice this date is two days past the previous one

… и я собираюсь вставить «дата 7» 2009-07-31, я бы хотел, чтобы это было так:

date 1, 2009-07-30
date 7, 2009-07-31 <- newly inserted row
date 2, 2009-08-01 <- this and subsequent rows are all incremented
                      by one day until it hits a non-consecutive day
date 3, 2009-08-02
date 4, 2009-08-03
date 5, 2009-08-04 <- notice this date hasn't changed

Но при обновлении дублирующего ключа это не работает, оно только обновляет конфликтующую строку, и если эта строка затем конфликтует с другой строкой, происходит сбой с ошибкой дублирующего ключа.

Есть ли умный способ добиться этого эффекта только в SQL?


person BenLanc    schedule 26.11.2010    source источник


Ответы (2)


Запрос на первую свободную дату на или после вашей целевой даты. Это включает в себя левое самосоединение для поиска дат без преемника в таблице.

SELECT DATE_ADD(Min(a.`date`), INTERVAL '1' DAY) AS `free_date`
FROM `activity` a
LEFT JOIN `activity` z
ON z.`date` = DATE_ADD(a.`date`, INTERVAL '1' DAY)
WHERE z.`date` IS NULL 
AND a.`date` >= '2009-07-31'

Запустите обновление, чтобы увеличить каждую дату в диапазоне между вашей целевой датой и первой свободной датой.

Теперь есть место для желаемой вставки.

person Narveson    schedule 26.11.2010
comment
Я считаю, что вы должны изменить LEFT на INNER JOIN. - person Lieven Keersmaekers; 27.11.2010
comment
@Lieven: это должно быть левое соединение, потому что мы хотим получить записи, для которых отсутствует последующая дата. - person Narveson; 27.11.2010
comment
извините, уже поздно. Я продолжаю печатать, но мой мозг уже отключился. - person Lieven Keersmaekers; 27.11.2010

выберите максимальную дату перед выполнением обновления, затем добавьте один день к максимальной

что-то такое:

НА ДУБЛИКЕ КЛЮЧА

 UPDATE `date` = DATE_ADD(select max('date') from 'activity', INTERVAL '1' DAY)
person Ali Tarhini    schedule 26.11.2010