Я считаю, что это действительно конструктивный недостаток, хотя и не специфический для SQL Server 2016, поскольку все другие существующие реализации временных таблиц (насколько я знаю) имеют такой же недостаток. Из-за этого могут возникнуть проблемы с временными таблицами; сценарий в вашем примере мягкий по сравнению с тем, что может пойти не так в целом:
Нерабочие ссылки на внешние ключи . Предположим, у нас есть две временные таблицы, а таблица A имеет ссылку на внешний ключ на таблицу B. Теперь предположим, что у нас есть две транзакции, каждая из которых выполняется с уровнем изоляции READ COMMITTED: транзакция 1 начинается до транзакции 2, транзакция 2 вставляет строку в таблицу B и фиксирует ее, затем транзакция 1 вставляет строку в таблицу A со ссылкой на вновь добавленную строку B. Поскольку добавление новой строки в B уже зафиксировано, ограничение внешнего ключа удовлетворяется, и транзакция выполняется 1 может совершить успешно. Однако, если бы мы просматривали базу данных «КАК ВСЕ» некоторое время между началом транзакции 1 и началом транзакции 2, то мы увидели бы таблицу A со ссылкой на строку B, которая не существует. Так что в этом случаевременная таблица обеспечивает несовместимое представление базы данных . Это, конечно, не было целью стандарта SQL: 2011, в котором говорится,
Исторические системные строки в системной версии таблицы образуют неизменные снимки прошлого. Любые ограничения, которые действовали при создании исторической системной строки, уже были бы проверены, когда эта строка была текущей системной строкой, поэтому никогда не требуется применять ограничения к историческим системным строкам.
Неуникальные первичные ключи : допустим, у нас есть таблица с первичным ключом и две транзакции, обе с уровнем изоляции READ COMMITTED, в котором происходит следующее: после того, как транзакция 1 начинается, но до того, как она коснется этой таблицы, транзакция 2 удаляет определенное строка таблицы и коммитов. Затем транзакция 1 вставляет новую строку с тем же первичным ключом, который был удален. Это проходит нормально, но когда вы посмотрите на таблицу AS OF в промежуток времени между началом транзакции 1 и началом транзакции 2, мы увидим две строки с одинаковым первичным ключом.
Ошибки при одновременных обновлениях . Допустим, у нас есть таблица и две транзакции, которые обновляют в ней одну и ту же строку, снова на уровне изоляции READ COMMITTED. Транзакция 1 начинается первой, но транзакция 2 первой обновляет строку. Затем транзакция 2 фиксирует транзакцию, а транзакция 1 выполняет другое обновление строки и фиксирует транзакцию. Это нормально, за исключением того, что если это временная таблица, то при выполнении обновления в транзакции 1, когда система собирается вставить нужную строку в таблицу истории, сгенерированный SysStartTime будет временем начала транзакции 2, а SysEndTime будет временем начала транзакции 1, которое не является допустимым интервалом времени, так как SysEndTime будет перед SysStartTime. В этом случае SQL Server выдает ошибку и откатывает транзакцию (например, см.это обсуждение ). Это очень неприятно, поскольку на уровне изоляции READ COMMITTED не следует ожидать, что проблемы параллелизма приведут к прямым сбоям, а это означает, что приложения не обязательно будут готовы к попыткам повторных попыток. В частности, это противоречит «гарантии» в документации Microsoft:
Такое поведение гарантирует, что ваши унаследованные приложения продолжат работать, когда вы включите управление версиями системы для таблиц, которые выиграют от управления версиями. ( ссылка )
Другие реализации временных таблиц имели дело с этим сценарием (две параллельные транзакции обновляют одну и ту же строку), предлагая возможность автоматически «корректировать» временные метки, если они недействительны (см. Здесь и здесь ). Это уродливый обходной путь, поскольку он имеет печальное последствие нарушения атомарности транзакций, поскольку другие операторы в рамках одних и тех же транзакций обычно не будут корректировать свои временные метки таким же образом; т. е. с помощью этого обходного пути, если мы просматриваем базу данных «КАК ОТ» в определенные моменты времени, то мы можем видеть частично выполненные транзакции.
Решение: Вы уже предложили очевидное решение, которое заключается в том, чтобы реализация использовала время окончания транзакции (т.е. время фиксации) вместо времени начала. Да, это правда, что когда мы выполняем оператор в середине транзакции, невозможно знать, какое будет время фиксации (как это происходит в будущем или может даже не существовать, если транзакция должна была быть выполнена). назад). Но это не значит, что решение неосуществимо; это просто нужно сделать по-другому. Например, при выполнении оператора UPDATE или DELETE при создании строки истории система может просто ввести идентификатор текущей транзакции вместо времени начала, а затем этот идентификатор может быть позднее преобразован системой во временную отметку после фиксации транзакции. ,
В контексте такого рода реализации я хотел бы предложить, чтобы до фиксации транзакции любые строки, добавляемые в таблицу истории, не были видны пользователю. С точки зрения пользователя, должно просто показаться, что эти строки добавляются (с отметкой времени фиксации) во время фиксации. В частности, если транзакция никогда не завершается успешно, она никогда не должна появляться в истории. Конечно, это несовместимо со стандартом SQL: 2011, который описывает вставки в историю (включая временные метки) как происходящие во время операторов UPDATE и DELETE (в отличие от времени фиксации). Но я не думаю, что это действительно имеет значение, учитывая, что стандарт никогда не был должным образом реализован (и, возможно, никогда не будет) из-за проблем, описанных выше,
С точки зрения производительности, системе может показаться нежелательным возвращаться назад и пересматривать строки истории, чтобы заполнить отметку времени фиксации. Но в зависимости от того, как это делается, стоимость может быть довольно низкой. Я не совсем знаком с тем, как SQL Server работает внутри, но PostgreSQL, например, использует журнал записи вперед, что делает его таким, что если несколько обновлений выполняются для одних и тех же частей таблицы, эти обновления объединяются, так что данные нужно записать только один раз на страницы физических таблиц - и это обычно применяется в этом сценарии. В любом слючае,
Конечно, поскольку (насколько я знаю) такого рода система никогда не была реализована, я не могу с уверенностью сказать, что она будет работать - возможно, что-то мне не хватает - но я не вижу никакой причины почему это не могло работать.
20160707 11:04:58
и теперь вы обновляете все строки с этой отметкой времени. Но это обновление также выполняется в течение нескольких секунд и заканчивается на20160707 11:05:02
какой временной отметке является правильное завершение транзакции? Или предположим, что вы использовалиRead Uncommited
в20160707 11:05:00
, и получили строки, возвращенные, но позжеAS OF
не показывает их.