Вы идете об этом неправильно. Реляционная база данных не является хранилищем значений ключей, которое вы должны читать и записывать по одному значению за раз. Вы можете сделать это атомарно, намного быстрее, с меньшим количеством кода и меньшим количеством зависимостей (и, если столбец 'id' не является УНИКАЛЬНЫМ, без ошибки). Попробуйте это:
sub set_expired {
my $id = shift;
my $dbh = DBI->connect(database);
my $sql = qq{UPDATE table SET expired=IF(NOW() > date, 1, 0) WHERE id = ?};
my $sth = $dbh->prepare($sql);
$sth->execute($id);
my $rows_affected = $sth->rows();
# if no matches, $rows_affected will be 0; on error, -1
$sth->finish();
}
Ваш mysqld, будем надеяться, будет хранить значения времени table.date, которые находятся в том же часовом поясе, в котором запущен сам mysqld. Похоже, что ваши значения table.date находятся в часовом поясе Америки / Нью-Йорка, так что с небольшой удачей ваш mysqld работает с его часовой пояс установлен таким же образом. В этом удачном случае NOW () будет временной меткой, которую можно напрямую сравнить с table.date, как указано выше.
Если вы передавали значения из другого часового пояса, вы, вероятно, захотите настроить сравнение, например:
... SET expired=IF(NOW() > DATE_ADD(date, INTERVAL 4 HOUR), 1, 0) WHERE ...
Ситуация усложняется, если часовой пояс, в котором вы храните значения table.date, меняется (или менялся) в зависимости от летнего времени: например, каждое падение составляет 1 час, когда одна и та же временная метка встречается дважды, и нет никакого способа сказать, глядя на сохраненное значение, которое вы имеете в виду. Это одна из причин, по которой, как правило, лучше хранить временные метки в GMT / UTC.
Теперь, что касается уникальности id. Если предложение WHERE гарантированно соответствует только 1 строке, то приведенный выше код и ваш будут делать то же самое. Но если он может соответствовать более 1, и если эти совпадающие строки могут иметь разные значения table.date, то ваш код, вероятно, содержит ошибку. А именно, он будет перебирать все n совпадающих строк и для каждой из них будет устанавливать для всех n строк значение table.expired, равное 1 или 0, в зависимости от того, в какой строке он находится. В результате все n строк будут иметь 1 или 0 в зависимости от последней возвращенной строки, которая не определена, поэтому ваш результат будет по существу случайным.
Единственное, что вы не получаете из вышеупомянутого ОБНОВЛЕНИЯ - это список идентификаторов, которые были сопоставлены или изменены, но, похоже, эта информация вам не нужна. Если вам это нужно, есть умный способ получить его; ответьте и я поделюсь этим:)