Ложное поведение - PullRequest
       42

Ложное поведение

6 голосов
/ 16 июня 2011

Выполните следующий скрипт, который создает и заполняет таблицу в вашей базе данных dev.

SET NOCOUNT ON
Drop Table dbo.Region
GO
CREATE TABLE dbo.Region(
    RegionId      int             IDENTITY(1,1),
    RegionName    varchar(100)    NOT NULL
)
GO

INSERT INTO dbo.Region (RegionName) 
VALUES  ('Region One'), 
        ('Region Two');
GO

SELECT * FROM dbo.Region

Вывод этого, что вы ожидаете получить из поля Identity с хорошим поведением.

RegionId    RegionName
----------- ------------------
1           Region One
2           Region Two

Теперь давайте введем пару значений в столбец Identity.

SET NOCOUNT ON
Drop Table dbo.Region
GO
CREATE TABLE dbo.Region(
    RegionId      int             IDENTITY(1,1),
    RegionName    varchar(100)    NOT NULL
)
GO

SET IDENTITY_INSERT dbo.Region ON;
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-9, 'Unknown'),
       (-99, 'N/A');
SET IDENTITY_INSERT dbo.Region OFF;

INSERT INTO dbo.Region (RegionName) 
VALUES  ('Region One'), 
        ('Region Two');
GO

SELECT * FROM dbo.Region

Вывод

RegionId    RegionName
----------- ------------------
-9          Unknown
-99         N/A
2           Region One
3           Region Two

Куда отправился RegionId = 1 ?




Редактировать При дальнейшем исследовании Sql-Server не пропускает пропуск ничего, если вы попробуете один и тот же трюк дважды

SET NOCOUNT ON
Drop Table dbo.Region
GO
CREATE TABLE dbo.Region(
    RegionId      int             IDENTITY(1,1),
    RegionName    varchar(100)    NOT NULL
)
GO

SET IDENTITY_INSERT dbo.Region ON;
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-9, 'Unknown'), 
       (-99, 'N/A');
SET IDENTITY_INSERT dbo.Region OFF;

INSERT INTO dbo.Region (RegionName) 
VALUES  ('Region One'), 
        ('Region Two');
GO
SET IDENTITY_INSERT dbo.Region ON;
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-999, 'Known-Unknown'), 
       (-9999, 'Really N/A');
SET IDENTITY_INSERT dbo.Region OFF;

INSERT INTO dbo.Region (RegionName) 
VALUES  ('Region Four'), 
        ('Region Five');
GO

SELECT * FROM dbo.Region

Вывод здесь

RegionId    RegionName
----------- ------------------
-9          Unknown
-99         N/A
2           Region One
3           Region Two
-999        Known-Unknown
-9999       Really N/A
4           Region Four
5           Region Five

В предыдущем случае 1 пропал.Здесь 4 не пропал без вести!

Так что теперь это непредсказуемая, недостающая личность!

Почему пропал RegionId = 1 , но RegionId = 4 не пропал?!

Ответы [ 3 ]

6 голосов
/ 17 июня 2011

IDENTITY(1,1) применяется к ПЕРВОЙ строке в таблице

Поскольку у вас уже есть две вставленные строки, семя больше не применяется

Следующий алгоритм идентификации добавляет один к начальному началу, когда он обнаруживает, что в таблице есть существующие записи, поскольку 1, возможно, уже использовался.

3 голосов
/ 17 июня 2011

Я тоже это заметил, первое значение идентичности как-то особенное. Если вы выполняете транзакцию на столе, первая личность удаляется. Это не относится к следующим личностям.

Причина в том, что current identity начинается со значения NULL:

create table YourTable (id int identity)
dbcc checkident(YourTable)
-->
Checking identity information: current identity value 'NULL', 
current column value 'NULL'.

Если current identity равно NULL, следующее число, которое будет выдано 1. Но любая транзакция, даже та, которая не использует новый идентификатор, приводит к изменению current identity с NULL на 1:

set identity_insert YourTable on;
insert YourTable (id) values (-1)
set identity_insert YourTable off;
dbcc checkident(YourTable)
-->
Checking identity information: current identity value '1', 
current column value '1'.

При current identity из 1 следующее число будет 2:

insert YourTable default values
select * from YourTable
-->
2

Итак, первая транзакция на новой таблице имеет особый эффект для current identity. Это не обязательно должна быть новая таблица, это случается и после truncate table.

1 голос
/ 13 ноября 2011

С Электронная документация по SQL Server

IDENTITY [ (seed , increment ) ]

seed

Это значение, которое используется для самой первой строкизагружается в таблицу.

приращение

Добавочное значение, добавляемое к значению идентификатора предыдущей строки, которое было загружено.

Поскольку вы использовалиIDENTITY_INSERT для загрузки первой строки в таблицу seed в этом случае не требовалось.Для IDENTITY_INSERT также задокументировано, что

Если введенное значение больше текущего значения идентификатора для таблицы, SQL Server автоматически использует новое вставленное значение в качестве текущего значения идентификатора.

Не указывается, что должно произойти в случае, описанном в вашем вопросе, но ясно, что он просто устанавливает текущее значение идентификатора на MAX(seed,identity_inserted) из приведенных ниже

CREATE TABLE dbo.Region(
    RegionId      int             IDENTITY(1,1),
    RegionName    varchar(100)    NOT NULL
)

SELECT increment_value,
       last_value as last_value_new_table
FROM sys.identity_columns 
WHERE name ='RegionId' AND object_id=object_id('dbo.Region')

SET IDENTITY_INSERT dbo.Region ON;
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-9, '-9')
SET IDENTITY_INSERT dbo.Region OFF;

SELECT increment_value,
       last_value as last_value_after_insert
FROM sys.identity_columns 
WHERE name ='RegionId' AND object_id=object_id('dbo.Region')

DROP TABLE dbo.Region

Возвращает

increment_value                last_value_new_table
------------------------------ ------------------------------
1                              NULL

increment_value                last_value_after_insert
------------------------------ ------------------------------
1                              1
...