ПЕРВЫЙ
Вы , вероятно , не нужны все три колонки: old_id, external_id, new_id. new_idКолонна, будучи IDENTITY, будет иметь новое значение , сгенерированное для каждой строки, даже при вставке в external_id. Но, между old_idи external_id, они в значительной степени взаимоисключающие: либо old_idзначение уже есть, либо этот столбец, в текущей концепции, будет просто NULLпри использовании external_idили new_id. Поскольку вы не будете добавлять новый «внешний» идентификатор в строку, которая уже существует (то есть, в которой есть old_idзначение), и не будет никаких новых значений old_id, то может быть один столбец, который используется для обеих целей.
Таким образом, избавьтесь от external_idстолбца и переименуйте, old_idчтобы быть что-то вроде old_or_external_idили как- то еще. Это не должно требовать каких-либо реальных изменений, но уменьшает некоторые осложнения. Самое большее, вам может понадобиться вызвать столбец external_id, даже если он содержит «старые» значения, если код приложения уже написан для вставки external_id.
Это уменьшает новую структуру, чтобы быть просто:
PkId AS AS COALESCE(old_or_external_id, new_id, -1) PERSISTED NOT NULL,
old_or_external_id INT NULL, -- values from existing record OR passed in from app
new_id INT IDENTITY(2000000, 1) NOT NULL
Теперь вы добавили только 8 байтов в строку вместо 12 байтов (при условии, что вы не используете SPARSEопцию или сжатие данных). И вам не нужно было менять код, T-SQL или код приложения.
ВТОРОЙ
Продолжая этот путь упрощения, давайте посмотрим на то, что мы оставили:
old_or_external_idСтолбец либо имеет значение уже, или будет дан новое значение из приложения, или оставить как NULL.
new_idВсегда будет иметь новое значение , сгенерированное, но это значение будет использовано только если old_or_external_idстолбец NULL.
Никогда не бывает времени, когда вам понадобятся значения как в, так old_or_external_idи в new_id. Да, будут времена, когда оба столбца имеют значения из-за new_idтого, что они являются IDENTITY, но эти new_idзначения игнорируются. Опять же, эти два поля являются взаимоисключающими. И что теперь?
Теперь мы можем понять, зачем нам это было нужно external_id. Учитывая , что можно вставить в IDENTITYколонку с помощью SET IDENTITY_INSERT {table_name} ON;, вы могли бы уйти с не делая никаких изменений схемы на всех, и только изменить код приложения , чтобы обернуть INSERTзаявления / операции в SET IDENTITY_INSERT {table_name} ON;и SET IDENTITY_INSERT {table_name} OFF;заявлении. Затем вам нужно определить, к какому начальному диапазону будет возвращаться IDENTITYстолбец (для вновь сгенерированных значений), так как он должен быть значительно выше значений, которые будет вставлять код приложения, поскольку при вставке более высокого значения следующее автоматически сгенерированное значение будет быть больше, чем текущее значение MAX. Но вы всегда можете вставить значение ниже значения IDENT_CURRENT .
Объединяя old_or_external_idи new_idстолбцы также не увеличивают шансы нарваться значением ситуации , перекрывающей между автоматически сгенерированными значениями и приложениями сгенерированными значениями с целью имеющих 2 или даже 3, колонны, чтобы объединить их в значение первичного ключа, и это всегда уникальные ценности.
При таком подходе вам просто необходимо:
ВТОРАЯ, часть Б
Вариант подхода, отмеченного непосредственно выше, заключался бы в том, чтобы код приложения вставлял значения, начинающиеся с -1, и понижающиеся оттуда. Это оставляет IDENTITYценности как единственные, идущие вверх . Преимущество здесь в том, что вы не только не усложняете схему, но и не должны беспокоиться о том, чтобы столкнуться с перекрывающимися идентификаторами (если сгенерированные приложением значения попадают в новый автоматически сгенерированный диапазон). Это вариант, только если вы еще не используете отрицательные значения идентификаторов (и люди редко используют отрицательные значения в автоматически сгенерированных столбцах, поэтому в большинстве ситуаций это должно быть вероятно).
При таком подходе вам просто необходимо:
Здесь вам все еще нужно сделать IDENTITY_INSERT, но: вы не добавляете новые столбцы, не нуждаетесь в том, чтобы «повторно заполнять» какие-либо IDENTITYстолбцы, и у вас нет будущего риска наложения.
ВТОРАЯ, часть 3
Последним вариантом этого подхода может быть замена IDENTITYстолбцов и использование последовательностей . Причиной такого подхода является наличие возможности вставлять в код приложения значения, которые являются: положительными, выше автоматически сгенерированного диапазона (не ниже) и не нуждаются в этом SET IDENTITY_INSERT ON / OFF.
При таком подходе вам просто необходимо:
- Создать последовательности, используя CREATE SEQUENCE
Скопируйте IDENTITYстолбец в новый столбец, который не имеет IDENTITYсвойства, но имеет DEFAULTограничение, используя функцию NEXT VALUE FOR :
PkId INT PRIMARY KEY CONSTRAINT [DF_TableName_NextID] DEFAULT (NEXT VALUE FOR...)
Это добавляет 0 байтов к каждой строке вместо 8 или даже 12.
- Начальный диапазон для значений, сгенерированных приложением, будет намного выше того, что, как вы думаете, подойдет к автоматически сгенерированным значениям.
- Оберните код приложения INSERT в
SET IDENTITY_INSERT {table_name} ON;и SET IDENTITY_INSERT {table_name} OFF;заявления.
ОДНАКО , из-за требования, чтобы код с одним SCOPE_IDENTITY()или @@IDENTITYвсе еще функционировал должным образом, переключение на Последовательности в настоящее время не вариант, поскольку кажется, что нет эквивалента этих функций для Последовательностей :-(. Грустно!