Ответ 1
Ну, нет родной поддержки для этого типа столбца, но вы можете реализовать его с помощью триггера:
CREATE TRIGGER tr_MyTable_Number
ON MyTable
INSTEAD OF INSERT
AS
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN;
WITH MaxNumbers_CTE AS
(
SELECT ParentEntityID, MAX(Number) AS Number
FROM MyTable
WHERE ParentEntityID IN (SELECT ParentEntityID FROM inserted)
)
INSERT MyTable (ParentEntityID, Number)
SELECT
i.ParentEntityID,
ROW_NUMBER() OVER
(
PARTITION BY i.ParentEntityID
ORDER BY (SELECT 1)
) + ISNULL(m.Number, 0) AS Number
FROM inserted i
LEFT JOIN MaxNumbers_CTE m
ON m.ParentEntityID = i.ParentEntityID
COMMIT
Не проверено, но я уверен, что это сработает. Если у вас есть первичный ключ, вы также можете реализовать это как триггер AFTER
(мне не нравятся триггеры INSTEAD OF
, их сложнее понять, когда вам нужно их модифицировать через 6 месяцев).
Просто чтобы объяснить, что происходит здесь:
-
SERIALIZABLE
- самый строгий режим изоляции; он гарантирует, что только одна транзакция базы данных одновременно может выполнять эти инструкции, которые нам нужны, чтобы гарантировать целостность этой "последовательности". Обратите внимание, что это необратимо продвигает всю транзакцию, поэтому вы не захотите использовать ее внутри долговременной транзакции. -
CTE берет наибольшее число, уже использованное для каждого родительского идентификатора;
-
ROW_NUMBER
генерирует уникальную последовательность для каждого родительского идентификатора (PARTITION BY
), начиная с номера 1; мы добавляем это к предыдущему максимуму, если есть одна, чтобы получить новую последовательность.
Я, вероятно, также должен упомянуть, что если вам нужно только вставить один новый дочерний объект за раз, вам лучше просто выполнить эти операции с помощью хранимой процедуры вместо использования триггера - вы определенно получите лучшую производительность из него. Вот как это делается в настоящее время с столбцами hierarchyid
в SQL'08.