Значение SCOPE_IDENTITY() при вставке более одной строки

документация для SCOPE_IDENTITY утверждает, что это

Возвращает последнее значение идентификатора, вставленное в столбец идентификаторов в той же области.

Интересно, что на самом деле означает last. Является ли это самым большим значением, вставленным в текущую область, или значением строки, которая была физически последней вставленной в таблицу?

Я пытался

  • create table with IDENTITY as a clustered index
    • this worked as expected; return value of SCOPE_IDENTITY was the highest value of identity column inserted
  • create table with IDENTITY as a column with different column as a cluster
    • i tried to use different ORDER BY statements to make sure that the generated IDENTITY didn't match the clustered index in any way
    • SCOPE_IDENTITY всегда возвращал максимальное достигнутое значение столбца IDENTITY, даже если идентификаторы генерировались независимо от кластеризованного индекса.
  • Я не пробовал таблицу с кучей

Почему я спрашиваю: я наткнулся на фрагмент кода, который вставляет несколько строк и использует SCOPE_IDENTITY для вставки самого высокого идентификатора, с которым он затем что-то выполняет. Учитывая то, как работает SCOPE_IDENTITY, в настоящее время он работает нормально, но мне интересно, может ли он привести к неожиданным ошибкам.

Мой вопрос:
Должен ли я всегда предполагать, что SCOPE_IDENTITY возвращает наибольшее значение вставленного IDENTITY (для возрастания IDENTITY), независимо от того, как строки были физически или логически вставлены в таблицу? Или я должен просто использовать OUTPUT, а затем MAX в столбце идентификаторов, чтобы быть уверенным?

Изменить: чтобы поместить это в код, всегда ли следующий код будет приводить к этому. Это происходит каждый раз, независимо от того, что находится в CREATE TABLE #WithID, SELECT, FROM, WHERE или ORDER BY? Предполагая, что ID INTEGER IDENTITY(1,1) остается постоянным.

CREATE TABLE #WithID (ID INTEGER IDENTITY(1,1), SomeData INTEGER);
CREATE TABLE #Outputs (ID INTEGER);

INSERT INTO #WithID
OUTPUT Inserted.ID INTO #Outputs
SELECT Number
FROM dbo.Numbers
WHERE Number < 1000000
ORDER BY Number;

IF SCOPE_IDENTITY() = (SELECT MAX(ID) FROM #Outputs)
  PRINT 'This happens everytime'
ELSE
  PRINT 'Oops...';

note: Я не знаю, насколько хорошо известно, что значения, созданные IDENTITY, создаются в порядке ORDER BY предложения SELECT, а не в том порядке, в котором строки физически вставляются в таблицу.


person andowero    schedule 09.06.2021    source источник
comment
Is it the largest value inserted in current scope, or the value of row that was physically last inserted into the table — эти две категории одинаковы, потому что идентификатор — это постоянно увеличивающееся значение, поэтому последняя вставленная строка будет иметь самый большой идентификатор текущей вставленной группы. И если вы предоставляете предварительно рассчитанные идентификаторы, то вам вообще не нужен scope_identity().   -  person GSerg    schedule 09.06.2021
comment
Я предполагаю (технически говоря) вы могли создать IDENTITY, где значение постоянно уменьшается, и вышеприведенное не будет правдой, @GSerg . :) db‹›fiddle   -  person Larnu    schedule 09.06.2021
comment
На самом деле у меня есть сценарий, в котором это происходит, то есть значение идентификатора уменьшается (унаследованная система), поэтому для этой таблицы идентификатор всегда является самым низким, будь то одна строка или пакет строк.   -  person Stu    schedule 09.06.2021
comment
@Larnu Хорошо, это было недостаточно общим. Последняя вставленная строка будет иметь идентификатор, наиболее удаленный от начальной точки идентификатора в направлении знака приращения идентификатора.   -  person GSerg    schedule 09.06.2021
comment
Мой комментарий был скорее шуткой, @GSerg, не волнуйся. Хотя Стью выше сказал, что у них есть такой сценарий, я не ожидал увидеть его более одного или двух раз за карьеру. :)   -  person Larnu    schedule 09.06.2021
comment
scope_identity() предназначен только для вставки одной строки. Если вы собираетесь вставить несколько строк, например, из tvp в хранимую процедуру, вы, вероятно, захотите использовать выводить в для явного захвата значений идентификаторов для каждой вставленной строки.   -  person AlwaysLearning    schedule 09.06.2021
comment
Чего вы на самом деле пытаетесь достичь, или это просто академический вопрос?   -  person Charlieface    schedule 09.06.2021
comment
@GSerg - эти две категории не совпадают. Идентификатор не должен следовать за кластеризованным индексом, и я предполагаю, что при вставке нескольких значений SQL Server вставляет строки в порядке кластеризованного индекса.   -  person andowero    schedule 09.06.2021
comment
@Charlieface - я наткнулся на многострочную вставку с использованием SCOPE_IDENTITY в нашей производственной базе данных, и мне интересно, безопасно ли ее использовать в таком сценарии. В настоящее время это работает, но я хотел бы знать, насколько мы будем в безопасности в будущем :)   -  person andowero    schedule 09.06.2021
comment
Отредактировано в примере кода.   -  person andowero    schedule 09.06.2021
comment
Нет, конечно, потому что последовательность IDENTITY может быть сброшена. В любом случае, зачем вам это делать, что Oops вы пытаетесь поймать, и кого волнует, является ли последнее значение IDENTITY самым высоким в таблице?   -  person Charlieface    schedule 09.06.2021
comment
Меня не волнует, сбрасывается ли значение, меня волнует только последний оператор INSERT. Я предполагаю, что сброс IDENTITY является изменением схемы и что это не могло произойти во время выполнения INSERT. Упс было бы, если бы SCOPE_IDENTITY вернуло, т.е. 10, но самое большое вставленное IDENTITY было 20.   -  person andowero    schedule 09.06.2021
comment
Это, вероятно, безопасно, а также, возможно, на что-то, на что вы не хотите полагаться, потому что это очевидно не безопасно. Если SCOPE_IDENTITY не будет использоваться для идентификации одной строки, его, вероятно, не следует использовать. Для явных операций, требующих резервирования непрерывного диапазона значений или позволяющих делать предположения о значениях, используйте последовательности; для операций, которые хотят начать с текущего наибольшего значения, используйте MAX(ID) как можно ближе к точке запроса, которая требует этого (что имеет то преимущество, что вы можете сделать его атомарным).   -  person Jeroen Mostert    schedule 09.06.2021
comment
@and Порядок вставки отсутствует, таблицы принципиально не отсортированы. В то время как order by будет учитываться во вставке, если она относится к столбцу идентификаторов (с целью назначения идентификаторов), это не соответствует порядку физических записей в таблице. scope_identity() возвращает последний (самый большой*) значение идентификатора, сгенерированное для набора.   -  person GSerg    schedule 09.06.2021